Packet Forward Middleware Flows
This document outlines some example flows leveraging packet forward middleware and formats of the memo field.
Example Scenarios
Successful Transfer forwarding through chain B
Memo for simple forward
- The packet-forward-middleware integrated on Chain B.
- The packet data
receiverfor theMsgTransferon Chain A is set to"pfm"or some other invalid bech32 string.* - The packet
memois included inMsgTransferby user on Chain A.
{
"forward": {
"receiver": "chain-c-bech32-address",
"port": "transfer",
"channel": "channel-123"
}
}
Error on Forwarding Hop, Refund to A
Forwarding with Retry and Timeout Logic
A -> B -> C full success
AThis sends packet over underlying ICS-004 wrapper with memo as is.BThis receives packet and parses it into ICS-020 packet.BValidatesforwardpacket on this step, returnACKerror if fails.BIf other middleware not yet called ICS-020, call it and ACK error on fail. Tokens minted or unescrowed here.BHandle denom. If denom prefix is fromB, remove it. If denom prefix is other chain - addBprefix.BTake fee, create new ICS-004 packet with timeout from forward for next step, and remaining innermemo.BSend transfer toCwith parameters obtained frommemo. Tokens burnt or escrowed here.BStore trackingin flight packetunder next(channel, port, ICS-20 transfer sequence), do notACKpacket yet.CHandle ICS-020 packet as usual.BOn ICS-020 ACK fromCfindin flight packet, delete it and writeACKfor original packet fromA.AHandle ICS-020ACKas usual
Example of USDC transfer from Osmosis -> Noble -> Sei
A -> B -> C with C error ACK
BOn ICS-020 ACK fromCfindin flight packet, delete itBBurns or escrows tokens.BAnd write errorACKfor original packet fromA.AHandle ICS-020 timeout as usualCwrites successACKfor packet fromB
Same behavior in case of timeout on C
A packet timeouts on B before C timeouts packet from B
ACannot timeout becausein flight packethas proof onBof packet inclusion.Bwaits for ACK or timeout fromC.Btimeout fromCbecomes failACKonBforAAreceives success or failACK, but not timeout
In this case A assets hang until final hop timeouts or ACK.
Memo for Retry and Timeout Logic, with Nested Memo (2 forwards)
- The packet-forward-middleware integrated on Chain B and Chain C.
- The packet data
receiverfor theMsgTransferon Chain A is set to"pfm"or some other invalid bech32 string. - The forward metadata
receiverfor the hop from Chain B to Chain C is set to"pfm"or some other invalid bech32 string. - The packet
memois included inMsgTransferby user on Chain A. - A packet timeout of 10 minutes and 2 retries is set for both forwards.
In the case of a timeout after 10 minutes for either forward, the packet would be retried up to 2 times, afterwards an error ack would be written to issue a refund on the prior chain.
next is the memo to pass for the next transfer hop. Per memo intended usage of a JSON string, it should be either JSON which will be Marshaled retaining key order, or an escaped JSON string which will be passed directly.
next as JSON
{
"forward": {
"receiver": "pfm", // intentionally invalid
"port": "transfer",
"channel": "channel-123",
"timeout": "10m",
"retries": 2,
"next": {
"forward": {
"receiver": "chain-d-bech32-address",
"port": "transfer",
"channel": "channel-234",
"timeout": "10m",
"retries": 2
}
}
}
}
Intermediate Address Security
Intermediate chains don’t need a valid receiver address. Instead, they derive a secure address from the packet’s sender and channel, preventing users from forwarding tokens to arbitrary accounts.
To avoid accidental transfers to chains without PFM, use an invalid bech32 address (e.g., "pfm") for intermediate receivers.