Pre-UIP: Outbound Packet Forwarding Middleware Support

For IBC transactions in the ecosystem, the Packet Forwarding Middleware has achieved widespread use as a solution for improving the UX of multi-hop IBC transfers, that is, IBC transfers that traverse multiple chains before reaching their destination. Instead of manually issuing a transfer on each chain in a token’s IBC path, the user can authorize chains to autonomously perform this unwinding, by specifying the path in the memo field of the packet data.

Currently, the Penumbra IBC stack has no concept of packet forwarding. This results in a degraded UX for withdrawals that have long paths. This UIP proposes an improvement to IBC UX by supporting autonomous multi-hop withdrawals through the ICS packet forwarding middleware.

There are two main parts of this:

(1) Outbound Packet Forwarding Middleware Support: The ability to encode packet data on Penumbra such that counterparty chains which run a PFM in their IBC state machine can autonomously forward these packets to their destination.
(2) Packet Forwarding Middleware Implementation: The ability for Penumbra to act as a ‘forwarding chain’; to parse forwarded packets that have the Penumbra chain somewhere in their path and correctly unwind their path, handling error cases and so on.

Implementing (1) allows for a major UX improvement with minimal implementation complexity: since all that’s required to integrate with packet-forwarding counterparties is the ability to set a custom memo field in the packet data, the Ics20Withdrawal struct can be expanded to allow this data to be included:

pub struct Ics20Withdrawal {
    // Existing fields...
    pub amount: Amount,
    pub denom: asset::Metadata,
    pub destination_chain_address: String,
    pub return_address: Address,
    pub timeout_height: IbcHeight,
    pub timeout_time: u64,
    pub source_channel: ChannelId,
    pub use_compat_address: bool,
    
    // New field
    pub ics20_memo: String,
}
impl From<Ics20Withdrawal> for pb::FungibleTokenPacketData {
    fn from(w: Ics20Withdrawal) -> Self {
        let return_address = match w.use_compat_address {
            true => w.return_address.compat_encoding(),
            false => w.return_address.to_string(),
        };

        pb::FungibleTokenPacketData {
            amount: w.value().amount.to_string(),
            denom: w.denom.to_string(),
            receiver: w.destination_chain_address,
            sender: return_address,
            memo: w.ics20_memo,
        }
    }
}

This is the total protocol change required to support Outbound packet forwarding. It will be up to the client which constructs the packet forwarding metadata in the memo to ensure that the memo is constructed correctly and deal with any address compatibility issues (note that the deployed PFM implementation requires at least one of the Sender or Receiver to be a valid cosmos sdk-style address in order to handle errorAcks properly). The client implementation details, which construct memos for outbound packets, are considered out-of-scope for this UIP which aims to add the minimal required protocol level change to support multi-hop outbound packets.

3 Likes