# Skip RFC-002: IBC Unwinding
| Summary | Design the initial approach for IBC unwinding |
| --- | --- |
| Created | February 24, 2023 |
| Owner | @zrbecker |
| Contributors | @bpiv400 @hxrts @davidterpay @nivasan1 |
## Background
Today, IBC UX is troublesome for users that want to move and utilize relatively established tokens across different chains - primarily because of a lack of a native canonical representation of denominations across different chains. As such, Osmosis on Terra is distinguishable from Osmosis on Juno when they should not be.
While this make sense because bridging from different sources implies different trusted counterparties (i.e. the validator sets of each source chain), it can be problematic for users who want to:
- Bridge an asset back to its native chain if it’s been transferred across intermediate chains
- Swap a token bridged from chain-B in a pool that contains the same token bridged from chain-A
This is especially problematic for new users and “non-native” users who don’t understand channels, IBC, bridges, or any of the implementation details that give rise to this problem.
**In short, the problem is that a token traveling from A→B→C is not fungible with a token traveling from A→C due to their differing security models.**
### User Story:
I’m a user or smart contract on **chain A**. I have token **foo** on **chain A**. I would like to get token **bar** on **chain B**, and I believe **foo** and **bar** are fungible over some IBC path. In other words, I believe **foo** and **bar** can be converted into one another by traversing particular IBC paths because they’re both representations of ****baz**** tokens from minted **chain C.** I’d like to obtain **bar** tokens on **chain B** with a single transaction, regardless of the path between them.

## Goals
1. **ICS-20** Achieve adoption of the protocol as widely as possible, by contributing directly to `ibc-go` and the`ICS-20` standard
2. **Existing channels:** Add functionality to standard ICS-20 channels to ensure we solve the problem where it exists (and don’t exacerbate it by creating / relying on new channels)
3. **Atomicity:** Ensure that multi-chain token transfers succeed or fail atomically (i.e. After some timeout, the tokens will be recoverable at the destination chain or the source chain, and will not get stuck at an intermediate chain)
## Non-goals
1. **Generalized multi-hop token transferring:** Support any length `n` path of token transfers between arbitrary connected chains - *We are only concerned with unwinding operations specifically*
2. **Generalized packet-forwarding/multi-hop:** Provide atomicity guarantees for other kinds of IBC operations. - *We are only concerned with token transfers*
4. **Inference of preferred tokens:** Determine what token the user would like to have on the destination - *We are only concerned with providing pathing information and execution given a desired outcome, not determining what the desired outcome is. This is left optionally to another service.*
5. **Chain name service implementation:** Prescribe a canonical implementation of a lookup service to determine paths between two tokens on two different chains given global chain identifiers across chains - *We would like to see developers build and promote many potential solutions and should be agnostic to path / chain name source.*
## Definitions
`Source` - When acting as the source chain, the bridge module escrows an existing local asset denomination on the sending chain and mints vouchers on the receiving chain.
`Destination` - The intended terminus of an IBC transaction.
`Origin` - The chain where the token was originally minted
`Unwind path` - The unique path from any chain C to the Origin chain where a token was minted that results in the native token on the origin chain.
`Unwind` - To send an IBC token back to its origin chain
## **tl;dr:**
1. We assume the existence of some service (trusted by the caller) that can determine a path from an origin chain to the destination chain given
1. Some global identifier of the destination chain
2. `lookup(destination_chain_id, destination_token_id) = [(channel_id, port_id), ... (channel_id, port_id)]`
3. The lookup service may be provided by a chain, a smart contract, or some off-chain service
2. Create a new `MsgTransferUnwind` with a new field that should be populated with the path from the origin to the destination chain (provided by some lookup service). On execution:
1. Check whether we have reached the origin chain yet
1. If not, retrace over IBC channels until tokens are on the origin chain
2. If so, check whether we have reached the destination chain. If not, execute the next step in the path from the origin to the destination
2. If no path origin to destination path is given, just trace the unwind path and stop at the origin chain
3. Modify the sequence of `send`, `recv`, and `ack` / `err` packets to initiate an ack/err that waterfalls back to the source only after successful delivery to the destination chain
## Proposal
:::info
:mega: *This is essentially a recursive solution that keeps re-routing until:*
* *The tokens reach the origin chain, then*
* *The IBC hops in specified in the `Route` calldata are exhausted.*
:::
The details below are preliminary, subject to change, and feedback.
### `MsgTransferUnwind`
We create a new message type (i.e. `MsgTransferUnwind` ) which resembles `MsgTransfer` with the following data structure modifications:
- A new `Route` field that stores a sequence of (channel, port) hops intended to represent the path between the origin chain and the destination
- A new `Unwound` field to indicate which part of the path the tokens are on (`False` = not yet reached origin chain, `True` = reached origin chain)
- No `SourcePort` and `SourceChannel`
```go
// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between
// ICS20 enabled chains. See ICS Spec here:
// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures
type MsgTransferUnwind struct {
// the tokens to be transferred
Token types.Coin `protobuf:"bytes,3,opt,name=token,proto3" json:"token"`
// the sender address
Sender string `protobuf:"bytes,4,opt,name=sender,proto3" json:"sender,omitempty"`
// the recipient address on the destination chain
Receiver string `protobuf:"bytes,5,opt,name=receiver,proto3" json:"receiver,omitempty"`
// Timeout height relative to the current block height.
// The timeout is disabled when set to 0.
TimeoutHeight types1.Height `protobuf:"bytes,6,opt,name=timeout_height,json=timeoutHeight,proto3" json:"timeout_height" yaml:"timeout_height"`
// Timeout timestamp in absolute nanoseconds since unix epoch.
// The timeout is disabled when set to 0.
TimeoutTimestamp uint64 `protobuf:"varint,7,opt,name=timeout_timestamp,json=timeoutTimestamp,proto3" json:"timeout_timestamp,omitempty" yaml:"timeout_timestamp"`
// optional memo
Memo string `protobuf:"bytes,8,opt,name=memo,proto3" json:"memo,omitempty"`
Route []IBCHop `protobuf:"bytes,9,rep,name=route,proto3" json:"route" yaml:"route"`
Unwound Boolean `protobuf:bytes,10,opt,name=unwound,proto3" json:"unwound" yaml:"unwound"`
}
// IBCHop defines a hop in the IBC packet unwinding.
type IBCHop struct {
// the port on which the packet needs to be rerouted
Port string `protobuf:"bytes,1,opt,name=port,proto3" json:"port,omitempty"`
// the channel on which the packet needs to be rerou ted
Channel string `protobuf:"bytes,2,opt,name=channel,proto3" json:"channel,omitempty"`
}
```
The IBC unwinding logic will require new versions of the transfer modules `sendTransfer` and `OnRecvPacket`, and `OnAcknowledgement` functions, which we call `sendTransferUnwind`, `onRecieveUnwindPacket`, `OnUnwindAcknowledgement`. We outline their functionality below.
### Minting and Burning
Since we do not have intermediate reciever addresses, the mint/escrow logic on intermediate chains needs to be changed slightly. When unwinding back to origin, we should burn tokens currenting in escrow, while on the origin, we should transfer tokens between escrow addresses for channels, and while sending to destination from origin, we should mint a token to the escrow address.
### Packet Data Structure
There are two alternatives to pass the unwind data over IBC Packets:
1. We can use the `memo` field of the current ICS20 packet
2. We can introduce a new version of the packet with explicit fields set.
The decision between these two is expected to only have minor implementation detail implications. We strive to approach this in the most idiomatic IBC way.
### `sendTransferUnwind`
We create the following algorithm (which is a modified version of `sendTransfer`):
1. On the source chain, we burn and mint vouchers/tokens as needed
2. If the `Unwound` field is `False` :
1. If the token is native, set `Unwound` to true in the next packet and jump to `Route` field check
2. If the token is not native, forward the packet to the channel over which the tokens were bridged to the current chain and terminate
3. If the `Unwound` field is `True`, jump to the `Route` field check
4. Route field check
1. If the `Route` field is non-empty, extract the first `IBCHop` from the `Route` field, supply the (`channel, port`) as the destination of the new packet, and pass the updated `Route` field to the new packet
### `onRecieveUnwindPacket`
When a IBC transfer packet is received, all of the same burning/minting logic must exist in order to ensure no funds are created or burned. To determine whether the IBC packet needs to be rerouted, the following modifications to `onRecieveUnwindPacket` will be added
1. Burn and mint vouchers/tokens as needed
2. If `Route` is not empty, this means we have to forward this packet to the next chain. In this case, we simply call `sendTransferUnwind` with the same route data, return nil from `onRecieveUnwindPacket` to indicate ack will be send asynchronously.
3. If `Route` is empty, call `WriteAcknowledgement` so ack sequence will begin.
### `onUnwindAcknowledgement`
1. If error, reverse token mints and escrows done by recieve and send
2. Otherwise, if not at source call `WriteAcknowledgement` to continue the ack chain.
3. If on source, return nil to complete.
### `PathLookupService`
We assume the existence of but do not specify a Chain Name Service that provides at least the following api (or equivalent):
```go
type PathLookupService interface {
PathFromOriginToDestination(destination_chain_id []byte, destination_token_id []byte) []IBCHop
}
```
## Considerations
- If a intermediate chain does not support our IBC unwinding module, the module should start a chain of ack failures, and reverse any escrow/mint operations it performed. This avoids funds being stuck on an intermediate chain.
- We expect that during message construction an unsupported chain failure could be early detected to avoid having to wait for failure.
## Abandoned Ideas
### Do nothing, just support adoption of packet-forwarding-middleware
**Benefits:**
- Packet-forwarding-middleware solves the multi-chain atomicity aspect of the problem
- PFM approach is generalizable across all IBC standards and only relies on the use of a memo field
**Downsides:**
- The json formatted "memo" field is more likely for users and wallets to format incorrectly and more difficult for smart contracts to set succinctly, so a more structured approach based on a modified message type seems sensible
- Middleware requires more work from chains to opt-in because it involves additional wiring up at the baseapp level, whereas an upgrade to ICS-20 will be adopted as part of standard upgrade procedures
### Generalized Multihop Transfer
Instead of inferring the path to the origin chain, then executing the path to the destination chain based on call data, we could specify the entire path in the call data.
**Benefits**
- More general approach → Could be used for multi-hop transfers that are totally unrelated to unwinding (In theory, any path could eventually be created by returning to the origin chain then traversing to the destination chain, so this isn’t exactly true. But it could be more efficient)
**Downsides**
- Requiring additional pre-specification of input increases likelihood of user errors
- “Grounding” path lookup services to use directed paths beginning on the chains where tokens are minted provides a useful schelling point / standard