Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 34 additions & 51 deletions docs/docs/04-middleware/01-callbacks/02-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ slug: /middleware/callbacks/integration

Learn how to integrate the callbacks middleware with IBC applications. The following document is intended for developers building on top of the Cosmos SDK and only applies for Cosmos SDK chains.

:::tip
An example integration for an IBC v2 transfer stack using the callbacks middleware can be found in the [ibc-go module integration](../../01-ibc/02-integration.md) section
:::

The callbacks middleware is a minimal and stateless implementation of the IBC middleware interface. It does not have a keeper, nor does it store any state. It simply routes IBC middleware messages to the appropriate callback function, which is implemented by the secondary application. Therefore, it doesn't need to be registered as a module, nor does it need to be added to the module manager. It only needs to be added to the IBC application stack.

## Pre-requisite Readings
Expand All @@ -23,13 +27,13 @@ For Cosmos SDK chains this setup is done via the `app/app.go` file, where module

As mentioned in [IBC middleware development](../../01-ibc/04-middleware/02-develop.md) an application stack may be composed of many or no middlewares that nest a base application.
These layers form the complete set of application logic that enable developers to build composable and flexible IBC application stacks.
For example, an application stack may just be a single base application like `transfer`, however, the same application stack composed with `29-fee` and `callbacks` will nest the `transfer` base application twice by wrapping it with the Fee Middleware module and then callbacks middleware.
For example, an application stack may just be a single base application like `transfer`, however, the same application stack composed with `packet-forward-middleware` and `callbacks` will nest the `transfer` base application twice by wrapping it with the callbacks module and then packet forward middleware.

The callbacks middleware also **requires** a secondary application that will receive the callbacks to implement the [`ContractKeeper`](https://github.com/cosmos/ibc-go/blob/v7.3.0/modules/apps/callbacks/types/expected_keepers.go#L11-L83). Since the wasm module does not yet support the callbacks middleware, we will use the `mockContractKeeper` module in the examples below. You should replace this with a module that implements `ContractKeeper`.
The callbacks middleware also **requires** a secondary application that will receive the callbacks to implement the [`ContractKeeper`](https://github.com/cosmos/ibc-go/blob/main/modules/apps/callbacks/types/expected_keepers.go#L12-L100). The wasmd contract keeper has been implemented [here](https://github.com/CosmWasm/wasmd/tree/main/x/wasm/keeper) and is referenced as the `WasmKeeper`.

### Transfer

See below for an example of how to create an application stack using `transfer`, `29-fee`, and `callbacks`. Feel free to omit the `29-fee` middleware if you do not want to use it.
See below for an example of how to create an application stack using `transfer`, `packet-forward-middleware`, and `callbacks`. Feel free to omit the `packet-forward-middleware` if you do not want to use it.
The following `transferStack` is configured in `app/app.go` and added to the IBC `Router`.
The in-line comments describe the execution flow of packets between the application stack and IBC core.

Expand All @@ -42,57 +46,36 @@ The in-line comments describe the execution flow of packets between the applicat
// channel.RecvPacket -> fee.OnRecvPacket -> callbacks.OnRecvPacket -> transfer.OnRecvPacket

// transfer stack contains (from top to bottom):
// - IBC Fee Middleware
// - IBC Packet Forward Middleware
// - IBC Callbacks Middleware
// - Transfer

// create IBC module from bottom to top of stack
var transferStack porttypes.IBCModule
transferStack = transfer.NewIBCModule(app.TransferKeeper)
transferStack = ibccallbacks.NewIBCMiddleware(transferStack, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas)
transferICS4Wrapper := transferStack.(porttypes.ICS4Wrapper)
transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper)
// Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the transfer keeper
app.TransferKeeper.WithICS4Wrapper(transferICS4Wrapper)

// Add transfer stack to IBC Router
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
```

:::warning
The usage of `WithICS4Wrapper` after `transferStack`'s configuration is critical! It allows the callbacks middleware to do `SendPacket` callbacks and asynchronous `ReceivePacket` callbacks. You must do this regardless of whether you are using the `29-fee` middleware or not.
:::
// initialise the gas limit for callbacks, recommended to be 10M for use with cosmwasm contracts
maxCallbackGas := uint64(10_000_000)

### Interchain Accounts Controller
// the keepers for the callbacks middleware
wasmStackIBCHandler := wasm.NewIBCHandler(app.WasmKeeper, app.IBCKeeper.ChannelKeeper, app.IBCKeeper.ChannelKeeper)

```go
// Create Interchain Accounts Stack
// SendPacket, since it is originating from the application to core IBC:
// icaControllerKeeper.SendTx -> callbacks.SendPacket -> fee.SendPacket -> channel.SendPacket

// initialize ICA module with mock module as the authentication module on the controller side
var icaControllerStack porttypes.IBCModule
icaControllerStack = ibcmock.NewIBCModule(&mockModule, ibcmock.NewIBCApp("", scopedICAMockKeeper))
app.ICAAuthModule = icaControllerStack.(ibcmock.IBCModule)
icaControllerStack = icacontroller.NewIBCMiddleware(icaControllerStack, app.ICAControllerKeeper)
icaControllerStack = ibccallbacks.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas)
icaICS4Wrapper := icaControllerStack.(porttypes.ICS4Wrapper)
icaControllerStack = ibcfee.NewIBCMiddleware(icaControllerStack, app.IBCFeeKeeper)
// Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the ica controller keeper
app.ICAControllerKeeper.WithICS4Wrapper(icaICS4Wrapper)

// RecvPacket, message that originates from core IBC and goes down to app, the flow is:
// channel.RecvPacket -> fee.OnRecvPacket -> icaHost.OnRecvPacket

var icaHostStack porttypes.IBCModule
icaHostStack = icahost.NewIBCModule(app.ICAHostKeeper)
icaHostStack = ibcfee.NewIBCMiddleware(icaHostStack, app.IBCFeeKeeper)

// Add ICA host and controller to IBC router ibcRouter.
AddRoute(icacontrollertypes.SubModuleName, icaControllerStack).
AddRoute(icahosttypes.SubModuleName, icaHostStack).
// create IBC module from bottom to top of stack
// Create Transfer Stack
var transferStack porttypes.IBCModule
transferStack = transfer.NewIBCModule(app.TransferKeeper)
// callbacks wraps the transfer stack as its base app, and uses PacketForwardKeeper as the ICS4Wrapper
// i.e. packet-forward-middleware is higher on the stack and sits between callbacks and the ibc channel keeper
// Since this is the lowest level middleware of the transfer stack, it should be the first entrypoint for transfer keeper's
// WriteAcknowledgement.
cbStack := ibccallbacks.NewIBCMiddleware(transferStack, app.PacketForwardKeeper, wasmStackIBCHandler, maxCallbackGas)
transferStack = packetforward.NewIBCMiddleware(
cbStack,
app.PacketForwardKeeper,
0,
packetforwardkeeper.DefaultForwardTransferPacketTimeoutTimestamp,
)
app.TransferKeeper.WithICS4Wrapper(cbStack)

// Create static IBC router, add app routes, then set and seal it
ibcRouter := porttypes.NewRouter()
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
ibcRouter.AddRoute(wasmtypes.ModuleName, wasmStackIBCHandler)
app.IBCKeeper.SetRouter(ibcRouter)
```

:::warning
The usage of `WithICS4Wrapper` here is also critical!
:::
7 changes: 2 additions & 5 deletions docs/docs/04-middleware/01-callbacks/06-gas.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@ Since the callbacks middleware does not have a keeper, it does not use a governa
```go
// app.go

maxCallbackGas := uint64(1_000_000)
maxCallbackGas := uint64(10_000_000)

var transferStack porttypes.IBCModule
transferStack = transfer.NewIBCModule(app.TransferKeeper)
transferStack = ibcfee.NewIBCMiddleware(transferStack, app.IBCFeeKeeper)
transferStack = ibccallbacks.NewIBCMiddleware(transferStack, app.IBCFeeKeeper, app.MockContractKeeper, maxCallbackGas)
// Since the callbacks middleware itself is an ics4wrapper, it needs to be passed to the transfer keeper
app.TransferKeeper.WithICS4Wrapper(transferStack.(porttypes.ICS4Wrapper))
transferStack = ibccallbacks.NewIBCMiddleware(transferStack, app.MockContractKeeper, maxCallbackGas)

// Add transfer stack to IBC Router
ibcRouter.AddRoute(ibctransfertypes.ModuleName, transferStack)
Expand Down
29 changes: 29 additions & 0 deletions docs/docs/04-middleware/01-callbacks/07-callbacks-IBCv2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Callbacks with IBC v2
sidebar_label: Callbacks with IBC v2
sidebar_position: 7
slug: /middleware/callbacks/callbacks-with-v2
---

# Use with IBC v2

This page highlights some of the differences between IBC v2 and IBC classic relevant for the callbacks middleware and how to use the module with IBC v2. More details on middleware for IBC v2 can be found in the [middleware section](../../01-ibc/04-middleware/02-developIBCv2.md).

## Interfaces

Some of the interface differences are:

- The callbacks middleware for IBC v2 requires the [`Underlying Application`](../01-callbacks/01-overview.md) to implement the new [`CallbacksCompatibleModuleV2`](https://github.com/cosmos/ibc-go/blob/main/modules/apps/callbacks/types/callbacks.go#L53-L58) interface.
- `channeltypesv2.Payload` is now used instead of `channeltypes.Packet`
- With IBC classic, the `OnRecvPacket` callback returns the `ack`, whereas v2 returns the `recvResult` which is the [status of the packet](https://github.com/cosmos/ibc-go/blob/main/modules/core/04-channel/v2/types/packet.pb.go#L26-L38): unspecified, success, failue or asynchronous
- `api.WriteAcknowledgementWrapper` is used instead of `ICS4Wrapper.WriteAcknowledgement`. It is only needed if the lower level application is going to write an asynchronous acknowledgement.

## Contract Developers

The wasmd contract keeper enables cosmwasm developers to use the callbacks middleware. The [cosmwasm documentation](https://cosmwasm.cosmos.network/ibc/extensions/callbacks) provides information for contract developers. The IBC v2 callbacks implementation uses a `Payload` but reconstructs an IBC classic `Packet` to preserve the cosmwasm contract keeper interface. Additionally contracts must now handle the IBC v2 `ErrorAcknowledgement` sentinel value in the case of a failure.

The callbacks middleware can be used for transfer + action workflows, for example a transfer and swap on recieve. These workflows require knowledge of the ibc denom that has been recieved. To assist with parsing the ics20 packet, [helper functions](https://github.com/cosmos/solidity-ibc-eureka/blob/a8870b023e58622fb7b3f733572c684851f8e5ee/packages/cosmwasm/ibc-callbacks-helpers/src/ics20.rs#L7-L41) can be found in the solidity-ibc-eureka repository.

## Integration

An example integration of the callbacks middleware in a transfer stack that is using IBC v2 can be found in the [ibc-go integration section](../../01-ibc/02-integration.md)
Loading