Documentation Index
Fetch the complete documentation index at: https://cosmos-docs-evm-upgrade-7.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Changelog
- 10-08-2022: Initial Draft
- 22-03-2023: Merged
- 13-09-2023: Updated with decisions made in implementation
- 24-02-2025: RecvPacket callback error now returns error acknowledgement
Status
Accepted, middleware implementedContext
IBC was designed with callbacks between core IBC and IBC applications. IBC apps would send a packet to core IBC. When the result of the packet lifecycle eventually resolved into either an acknowledgement or a timeout, core IBC called a callback on the IBC application so that the IBC application could take action on the basis of the result (e.g. unescrow tokens for ICS-20). This setup worked well for off-chain users interacting with IBC applications. We are now seeing the desire for secondary applications (e.g. smart contracts, modules) to call into IBC apps as part of their state machine logic and then do some actions on the basis of the packet result. Or to receive a packet from IBC and do some logic upon receipt. Example Usecases:- Send an ICS-20 packet, and if it is successful, then send an ICA-packet to swap tokens on LP and return funds to sender
- Execute some logic upon receipt of token transfer to a smart contract address
Definitions
- Actor: an actor is an on-chain module (this may be a hardcoded module in the chain binary or a smart contract) that wishes to execute custom logic whenever IBC receives a packet flow that it has either sent or received. It must be addressable by a string value.
Decision
Create a middleware that can interface between IBC applications and smart contract VMs. The IBC applications and smart contract VMs will implement respective interfaces that will then be composed together by the callback middleware to allow a smart contract of any compatible VM to interact programmatically with an IBC application.Data structures
TheCallbackPacketData struct will get constructed from custom callback data in the application packet. The CallbackAddress is the IBC Actor address on which the callback should be called on. The SenderAddress is also provided to optionally allow a VM to ensure that the sender is the same as the callback address.
The struct also defines a CommitGasLimit which is the maximum gas a callback is allowed to use. If the callback exceeds this limit, the callback will panic and the tx will commit without the callback’s state changes.
The ExecutionGasLimit is the practical limit of the tx execution that is set in the context gas meter. It is the minimum of the CommitGasLimit and the gas left in the context gas meter which is determined by the relayer’s choice of tx gas limit. If ExecutionGasLimit < CommitGasLimit, then an out-of-gas error will revert the entire transaction without committing anything, allowing for a different relayer to retry with a larger tx gas limit.
Any middleware targeting this interface for callback handling should define a global limit that caps the gas that a callback is allowed to take (especially on AcknowledgePacket and TimeoutPacket) so that a custom callback does not prevent the packet lifecycle from completing. However, since this is a global cap it is likely to be very large. Thus, users may specify a smaller limit to cap the amount of fees a relayer must pay in order to complete the packet lifecycle on the user’s behalf.
Callback Middleware
The CallbackMiddleware wraps an underlying IBC application along with a contractKeeper that delegates the callback to a virtual machine. This allows the Callback middleware to interface any compatible IBC application with any compatible VM (e.g. EVM, WASM) so long as the application implements theCallbacksCompatibleModule interface and the VM implements the ContractKeeper interface.
Callback-Compatible IBC Application
TheCallbacksCompatibleModule extends porttypes.IBCModule to include an UnmarshalPacketData function that allows the middleware to request that the underlying app unmarshal the packet data. This will then allow the middleware to retrieve the callback specific data from an arbitrary set of IBC application packets.
ContractKeeper
TheContractKeeper interface must be implemented by any VM that wants to support IBC callbacks. This allows for separation of concerns
between the middleware which is handling logic intended for all VMs (e.g. setting gas meter, extracting callback data, emitting events),
while the ContractKeeper can handle the specific details of calling into the VM in question.
The ContractKeeper may impose additional checks such as ensuring that the contract address is the same as the packet sender in source callbacks.
It may also disable certain callback methods by simply performing a no-op.
PacketCallbacks
The packet callbacks implemented in the middleware will first call the underlying application and then route to the IBC actor callback in the post-processing step. It will extract the callback data from the application packet and set the callback gas meter depending on the global limit, the user limit, and the gas left in the transaction gas meter. The callback will then be routed through the callback keeper which will either panic or return a result (success or failure). In the event of a (non-oog) panic or an error, the callback state changes are discarded and the transaction is committed. If the relayer-defined gas limit is exceeded before the user-defined gas limit or global callback gas limit is exceeded, then the entire transaction is reverted to allow for resubmission. If the chain-defined or user-defined gas limit is reached, the callback state changes are reverted and the transaction is committed. For theSendPacket callback, we will revert the entire transaction on any kind of error or panic. This is because the packet lifecycle has not yet started, so we can revert completely to avoid starting the packet lifecycle if the callback is not successful.
maxCallbackGas to ensure that callbacks do not consume an arbitrary amount of gas. Thus, it should always be possible for a relayer to complete the packet lifecycle even if the actor callbacks cannot run successfully.
Consequences
Positive
- IBC Actors can now programmatically execute logic that involves sending a packet and then performing some additional logic once the packet lifecycle is complete
- Middleware implementing ADR-8 can be generally used for any application
- Leverages a similar callback architecture to the one used between core IBC and IBC applications
Negative
- Callbacks may now have unbounded gas consumption since the actor may execute arbitrary logic. Chains implementing this feature should take care to place limitations on how much gas an actor callback can consume.
- The relayer pays for the callback gas instead of the IBCActor
Neutral
- Application packets that want to support ADR-8 must additionally have their packet data implement
PacketDataProviderandPacketDatainterfaces. - Applications must implement
PacketDataUnmarshalerinterface - Callback receiving module must implement the
ContractKeeperinterface