Explanation
The Transfer module enables tokens to move seamlessly between IBC-connected blockchains. This document explains the concepts and mechanics of IBC token transfers and Neutron's enhancements to the standard IBC transfer protocol.
IBC Token Transfer Basics
IBC token transfers function through a packet-relay mechanism between chains:
sequenceDiagram
participant Sender as Sender (Chain A)
participant ChainA as Chain A
participant ChainB as Chain B
participant Receiver as Receiver (Chain B)
Sender->>ChainA: Send Tokens
ChainA->>ChainA: Escrow Tokens
ChainA->>ChainB: IBC Packet (Transfer)
ChainB->>ChainB: Mint or Unescrow Tokens
ChainB->>Receiver: Receive Tokens
ChainB->>ChainA: IBC Packet (Acknowledgement)
Core Transfer Mechanism
-
Token Escrow/Burn: When a token is sent from its source chain, it is escrowed (for native tokens) or burned (for non-native tokens) on the sending chain.
-
Packet Relay: An IBC packet containing transfer details is sent to the destination chain.
-
Token Release/Mint: On the destination chain, the token is either:
- Released from escrow (if the destination is the original source of the token)
- Minted as an IBC voucher token (if the token is not native to the destination)
-
Acknowledgement: The destination chain sends back an acknowledgement to confirm receipt of the packet.
Denomination Trace
To track the origin of tokens as they move between chains, IBC uses denomination traces:
- Native Tokens: When sent to another chain, native tokens (e.g.,
untrn) become prefixed with path information:{destPort}/{destChannel}/{denom} - IBC Tokens: When received from another chain, tokens carry their full path history
Example flow:
untrnon Neutron is sent to Cosmos Hub via channel-1- On Cosmos Hub, it appears as
transfer/channel-X/untrn - If sent back to Neutron, it reverts to its original form:
untrn
Neutron's Enhanced Transfer Module
Neutron extends the standard IBC transfer module with contract-focused features:
Contract Detection and Handling
The Transfer module automatically detects contract senders and applies specific handling:
sequenceDiagram
participant Contract as Smart Contract
participant Transfer as Transfer Module
participant IBC as IBC Core
participant Remote as Remote Chain
Contract->>Transfer: Send Tokens (MsgTransfer)
Transfer->>Transfer: HasContractInfo() Check
Transfer->>Transfer: Fee Validation & Locking (if contract)
Transfer->>IBC: Create IBC Packet
IBC->>Remote: Send Packet
Remote->>IBC: Send Acknowledgement
IBC->>Transfer: Process Acknowledgement
Transfer->>Transfer: Distribute Fees
Transfer->>Contract: Sudo Callback
-
Contract Detection: The module uses
HasContractInfo(ctx, senderAddress)to determine if the sender is a smart contract. -
Contract-Specific Validation: For contract senders, the module validates fees using
msg.Fee.Validate(). -
Fee Locking: For contracts, fees are automatically locked using
FeeKeeper.LockFees()before sending the packet.
Enhanced Response Objects
Standard IBC transfers return minimal information. Neutron enhances this:
-
Extended Response: The
MsgTransferResponseincludes:sequence_id: The IBC packet sequence numberchannel: The source channel ID
-
Traceability: These fields enable correlation between transfer requests and their outcomes.
Callback System
The Transfer module implements a callback system for contract senders:
sequenceDiagram
participant IBC as IBC Core
participant Transfer as Transfer Module
participant FeeKeeper as Fee Keeper
participant Contract as Smart Contract
IBC->>Transfer: Acknowledgement/Timeout Handler
Transfer->>Transfer: Check HasContractInfo()
Transfer->>FeeKeeper: Distribute Fees
Transfer->>Transfer: Prepare Sudo Message
Transfer->>Contract: Sudo Callback
Note over Transfer: Logs errors, continues processing
-
Callback Trigger: When acknowledgements or timeouts are received, the module checks if the original sender was a contract.
-
Message Preparation: The module calls
keeper.PrepareSudoCallbackMessage()to create the callback message. -
Sudo Call: The module executes
sudoKeeper.Sudo(ctx, senderAddress, msg)to deliver the callback. -
Error Handling: Failed Sudo calls are logged but do not block IBC processing:
"HandleAcknowledgement: failed to Sudo contract on packet acknowledgement"
Fee Management
The Transfer module coordinates with the Fee Refunder module for packet fees:
flowchart TD
A[Transfer Request] --> B{Is Contract?}
B -->|Yes| C[Validate & Lock Fees]
B -->|No| D[Skip Fee Processing]
C --> E[Send IBC Packet]
D --> E
E --> F[Packet Outcome]
F -->|Acknowledgement| G[Distribute Ack Fee]
F -->|Timeout| H[Distribute Timeout Fee]
G --> I[Callback to Contract]
H --> J[Callback to Contract]
-
Fee Validation: Only contracts must provide valid fees (
Validate(isContract bool)method). -
Fee Locking: Before packet transmission, contracts' fees are locked via
FeeKeeper.LockFees(). -
Fee Distribution: The module distributes fees to relayers:
DistributeAcknowledgementFee()for successful packetsDistributeTimeoutFee()for timed-out packets
Module Architecture
The Transfer module is implemented as a wrapper around the standard IBC Transfer module:
Wrapper Pattern
type IBCModule struct {
wrappedKeeper wrapkeeper.KeeperTransferWrapper
keeper keeper.Keeper
sudoKeeper neutrontypes.WasmKeeper
tokenfactoryKeeper neutrontypes.TokenfactoryKeeper
transfer.IBCModule
}
Method Overrides
The module overrides key IBC handlers:
OnAcknowledgementPacket(): Adds callback processing after standard handlingOnTimeoutPacket(): Adds callback processing after standard handlingTransfer(): Adds contract detection, fee locking, and enhanced response
Standard IBC Delegation
For all other functionality (queries, denomination traces, parameters), the module delegates to the underlying standard IBC Transfer implementation.
Error Handling and Processing
The Transfer module implements robust error handling:
- Sudo Call Failures: Logged as debug messages but processing continues
- Fee Processing: Happens regardless of callback success/failure
- IBC Processing: Not blocked by contract callback failures
This ensures that IBC transfer functionality remains reliable even when contract callbacks fail.
Contract Callback Behavior: The specific format and content of callback messages depend on the Contract Manager module's PrepareSudoCallbackMessage() implementation. Contracts should refer to the Contract Manager module documentation for callback message specifications.