# IBC-Hooks

### Overview

IBC-Hooks is an IBC middleware that **uses incoming ICS-20 token transfers to initiate smart contract calls**. This allows for arbitrary data to be passed in along with token transfers. This is useful for a variety of use cases such as cross-chain token swaps, auto-wrapping of SNIP-20 tokens, General Message Passing (GMP) between Secret Network and EVM chains, and much more! The mechanism enabling this is a `memo` field on every ICS20 transfer packet as of IBC v3.4.0. Wasm hooks is an IBC middleware that parses an ICS20 transfer, and if the `memo` field is of a particular form, it executes a Wasm contract call.

{% hint style="info" %}
Note that the metadata in the `memo` field is not used within ICS-20 itself, but instead, a middleware or custom CosmWasm contract can wrap around the transfer protocol to parse the metadata and execute custom logic based off of it. [See more here.](https://medium.com/the-interchain-foundation/moving-beyond-simple-token-transfers-d42b2b1dc29b)&#x20;
{% endhint %}

### ICS20 Packet Structure

ICS20 is JSON native, so JSON is used for the memo format:

{% code overflow="wrap" %}

```rust
{
  //... other ibc fields omitted for example
  "data": {
    "denom": "denom on counterparty chain (e.g. uatom)", // will be transformed to the local denom (ibc/...)
    "amount": "1000",
    "sender": "addr on counterparty chain", // will be ignored and shown to the contract as a null sender (cannot be verifed over IBC)
    "receiver": "secret1contractAddr",
    "memo": {
      "wasm": {
        "contract": "secret1contractAddr",
        "msg": {
          "raw_message_fields": "raw_message_data"
        }
      }
    }
  }
}
```

{% endcode %}

An ICS20 packet is formatted correctly for Wasm hooks if the following all hold:

* `memo` is not blank
* `memo` is valid JSON
* `memo` has at least one key, with value `"wasm"`
* `memo["wasm"]` has exactly two entries, `"contract"` and `"msg"`
* `memo["wasm"]["msg"]` is a valid JSON object
* `receiver == memo["wasm"]["contract"]`

{% hint style="info" %}
If an ICS20 packet is not directed towards wasmhooks, wasmhooks doesn't do anything. If an ICS20 packet is directed towards wasmhooks, and is formatted incorrectly, then wasmhooks returns an error.
{% endhint %}

### ICS20 Packet Execution Flow

Before Wasm hooks:

* Ensure the incoming IBC packet is cryptographically valid
* Ensure the incoming IBC packet has not timed out

In Wasm hooks, before packet execution:

* Ensure the packet is correctly formatted (as defined above)
* Edit the receiver to be the hardcoded IBC module account

In Wasm hooks, after packet execution:

* Construct wasm message as defined above
* Execute wasm message
* If wasm message has error, return `ErrAck`
* Otherwise continue through middleware

### Auto-wrapping of SNIP-20 Example

[The following contract](https://github.com/scrtlabs/secret.js/blob/e6919420e1650c1a37ff188743b2e6bb33a93823/test/ibc-hooks-contract/src/contract.rs#L22-L46) receives funds from IBC, wraps them as SNIP-20 tokens, and then transfers them to the recipient that is specified in the ibc-hooks message:

{% code overflow="wrap" %}

```rust
#[entry_point]
pub fn execute(_deps: DepsMut, env: Env, info: MessageInfo, msg: Msg) -> StdResult<Response> {
    match msg {
        Msg::WrapDeposit {
            snip20_address,
            snip20_code_hash,
            recipient_address,
        } => Ok(Response::default().add_messages(vec![
            CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
                contract_addr: snip20_address.clone(),
                code_hash: snip20_code_hash.clone(),
                msg: to_binary(&secret_toolkit::snip20::HandleMsg::Deposit { padding: None })
                    .unwrap(),
                funds: info.funds.clone(),
            }),
            CosmosMsg::Wasm(cosmwasm_std::WasmMsg::Execute {
                contract_addr: snip20_address,
                code_hash: snip20_code_hash,
                msg: to_binary(&secret_toolkit::snip20::HandleMsg::Transfer {
                    recipient: recipient_address,
                    amount: info.funds[0].amount,
                    memo: None,
                    padding: None,
                })
                .unwrap(),
                funds: vec![],
            }),
        ])),
        }
    }
```

{% endcode %}

### Ack callbacks

A contract that sends an IBC transfer may need to listen for the acknowledgment (`ack`) of that packet. To allow contracts to listen to `ack` of specific packets, we provide **Ack callbacks**. The sender of an IBC transfer packet may specify a callback in the `memo` field of the transfer packet when the `ack` of that packet is received.

{% hint style="info" %}
Only the IBC packet sender can set the callback
{% endhint %}

#### Ack callback implementation

For the callback to be processed, the transfer packet's `memo` should contain the following in its JSON:

`{"ibc_callback": "secret1contractAddr"}`

The WASM hooks will keep the mapping from the packet's channel and sequence to the contract in storage. When an `ack` is received, it will notify the specified contract via an `execute` message.

**Interface for receiving the Acks and Timeouts**

[The contract that awaits the callback](https://github.com/scrtlabs/secret.js/blob/e6919420e1650c1a37ff188743b2e6bb33a93823/test/ibc-hooks-contract/src/contract.rs#L66C10-L66C10) should implement the following interface for a sudo message:

{% code overflow="wrap" %}

```rust
#[cw_serde]
pub enum IBCLifecycleComplete {
    #[serde(rename = "ibc_ack")]
    IBCAck {
        /// The source channel (secret side) of the IBC packet
        channel: String,
        /// The sequence number that the packet was sent with
        sequence: u64,
        /// String encoded version of the ack as seen by OnAcknowledgementPacket(..)
        ack: String,
        /// Weather an ack is a success of failure according to the transfer spec
        success: bool,
    },
    #[serde(rename = "ibc_timeout")]
    IBCTimeout {
        /// The source channel (secret side) of the IBC packet
        channel: String,
        /// The sequence number that the packet was sent with
        sequence: u64,
    },
}

/// Message type for `sudo` entry_point
#[cw_serde]
pub enum SudoMsg {
    #[serde(rename = "ibc_lifecycle_complete")]
    IBCLifecycleComplete(IBCLifecycleComplete),
}

#[entry_point]
pub fn sudo(_deps: DepsMut, _env: Env, msg: SudoMsg) -> StdResult<Response> {
    match msg {
        SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCAck {
            channel,
            sequence,
            ack,
            success,
        }) => todo!(),
        SudoMsg::IBCLifecycleComplete(IBCLifecycleComplete::IBCTimeout {
            channel,
            sequence,
        }) => todo!(),
    }
}
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.scrt.network/secret-network-documentation/confidential-computing-layer/ibc/basics/cross-chain-messaging-with-ibc-hooks/ibc-hooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
