### F.ail Transactions when they're likely to cause user harm
We recommend preventing transactions that may significantly harm the user altogether -- even if your user seems to want to complete the transaction.
We recommend failing/preventing user transactions in the following scenarios:
1. **Greater than 10% difference in relative USD value in and out** `(usd_amount_in - usd_amount_out)/usd_amount_in)*100 > 10`
2. **Greater than 10% price impact** (`swap_price_impact > 10`)
You could can decide how you want to signal this failure to user, e.g. no route found, some form of low liquidity message, or something else entirely.
Preventing transactions altogether that violate your safety thresholds is the strongest form of user safety.
### E.nforce explicit, additional approval
If you do not want to fail transactions that exceed safety thresholds outright, one viable alternative is to require additional stages of user approval before letting the user sign the transaction.
Importantly, this is different and more disruptive than simply warning the user about some aspect of the quote looking unfavorable. This means putting additional clicks between the user and the swap they want to perform, and having them explicitly agree to performing a swap your UI indicates will have a bad price.
For example, this is go.skip.build's warning screen:
* It's very clear that our expectation is that the swap will harm the user with the "Bad Trade Warning" across the top
* The page explicitly reminds the user what the problem is -- foregrounding the predicted price impact and forcing them to acknowledge it again
* The "happy path" or "default" path is to go back -- not to finish the swap (Notice that the "Go Back" button is highlighted)
We recommend requiring additional steps of user approval in the following cases:
1. **Greater than 5% difference in relative USD value in and out** `(usd_amount_in - usd_amount_out)/usd_amount_in)*100 > 5`
2. **Greater than 5% price impact** (`swap_price_impact > 5`)
3. **Price impact AND relative-USD-value-difference cannot be calculated** (i.e. `swap_price_impact`, `usd_amount_out`, and/or `usd_amount_in` are missing)
#### Choosing the right level of protection: warnings, additional approvals, and outright failures
It's important to think about this tradeoff because protecting users often directly trades off against making a cleaner, simpler, and more powerful user experiences. For example, excessive warnings might get annoying to users who know they're trading illiquid shitcoins, and additional steps of approval might frustrate pro traders who care deeply about speed
For any safety metric you might track to determine whether a transaction could harm a user, consider 4 tiers of safety you can implement. From least safe and least disruptive to most safe and most disruptive:
1. **None**: Just let it rip. Don't give the user any heads up. Don't do anything to slow them down or prevent them from trading.
2. **Alert**: Use some visual cue to indicate to the user that they should be wary about swap
3. **Enforce additional approval**: Require additional clicks to actually execute the swap and foreground the warning -- so the user needs to approve it explicitly.
4. **Fail**: Just block / fail / prevent transactions that exceed your safety tolerance bounds outright
Here are some suggestions for navigating this design space:
1. **Set lower trigger thresholds for weaker forms of security and more conservative thresholds for stronger forms of security** (e.g. You could alert users about high price impact at 2.5%, require an additional stage of approval at 10%, and fail the transaction outright at 25%) This approach is nice because it gives users who may be very conservative some indication that they may face some danger without getting in their way too much, while still hard-stopping more inexcusable failures that are probably never acceptable to any trader
2. **Use stronger forms of security when safety tolerances are exceeded for higher value transactions** (e.g. You could use warnings when price impact is greater than 10% for transactions where the amount in is $0-100, additional approvals when amount in is $1,000 - 10,000, and block transactions above \$10k outright if price impact is greater than 10%.
* Call a contract on a remote chain after purchasing a cw20 asset (e.g. since this requires an IBC transfer under the hood)
* IBC transfer from a remote chain to the CW20's origin chain then perform a swap or any other post-route action on that chain
In principle, you can use the Skip Go API to construct any of these action sequences across multiple transactions, but it will be more challenging for you and your end users.
2. **Post-swap failures:**
* **Description**: These are failures that occur on the sequence of transfers between the swap venue chain and the user's destination chain, after the user's origin tokens have already been successfully swapped for their desired destination asset.
* **Outcome / What to Expect**: The user's newly purchased destination asset tokens will be transferred to their address on the swap chain. (This is the address passed to `chains_to_addresses` in `/fungible/msgs` for the chain where the swap takes place, which is given by `swap_venue.chain_id` in the response from `/fungible/route`)
* **Common failure sources:**
* Inactive relayers on a channel allow a packet to timeout
* The user / frontend provides an invalid address for the destination chain
* An IBC client on the destination chain has expired
* **Examples:** Consider a route where the source asset is ATOM on Neutron, the destination asset is STRIDE on Stride, and the swap takes place on Osmosis:
* Suppose the swap took place and the transfer to Stride has been initiated, but the Relayer between Osmosis and Stride is down. So the packet’s timeout occurs after 5 minutes. When the Relayer comes back online after 8 minutes, it relays a timeout message to Osmosis, releasing the user’s STRIDE, which gets forwarded to their Osmosis address
## Axelar Failures
Axelar transfers can be tracked on [Axelarscan](https://axelarscan.io/). Often, Axelar transfers are delayed by Axelar's relayer or execution services. If a transaction is taking longer than expected, users can visit Axelarscan, find their transaction, and manually execute the steps needed to get the transfer through. See the [Axelar docs](https://docs.axelar.dev/dev/general-message-passing/recovery) for details on how to use Axelarscan.
Internally, the Skip Go API may use Axelar's General Message Passing service to move assets between EVM and Cosmos. There are similar failure modes for Axelar as there are for IBC:
1. **Swap failures**
* **What:** Axelar GMP takes user assets from an EVM chain to the swap chain. The swap can still fail at this point due to a timeout or slippage.
* **Outcome / What to Expect:** The user receives the Axelar-transferred token on the chain where the swap was supposed to take place at their recovery address. (Note this is different from the IBC swap failure case where the user receives the swap token back on the source chain)
* **Common failure sources:**
* Slow relaying from Axelar causes a timeout, and the swap is not attempted.
* Slippage (the amount out for the swap turns out to be less than the user's specified minimum, i.e. their slippage exceeds their tolerance)
2. **Post-swap failures**
* Once the swap is executed, Axelar is no longer involved, and the same rules that apply to IBC post-swap failures apply here, so the **Post-swap failures** section above applies.
## CCTP Failures
Routes that use CCTP transfers rely on Circle to produce attestations. The Circle attestation service waits for a specified number of on-chain block confirmations before producing an attestation. The number of block confirmations required is specified by Circle in their documentation [here](https://developers.circle.com/stablecoins/docs/required-block-confirmations).
If Circle's attestation service experiences an outage, malfunction, or otherwise becomes unresponsive, CCTP transfers will continue to burn assets on the source chain, but will not be able to mint assets on the destination chain. In this case, funds that have been burned to initiate a CCTP transfer will be inaccessible until the Circle attestation service recovers.
## Hyperlane Failures
Each Hyperlane token transfer route is secured by an Interchain Security Module (ISM) designated by the deployer of the Hyperlane Warp Route Contracts (the interface to send tokens across chains using Hyperlane). The ISM defines the requirements for a message to be successfully processed on the destination chain.
The most common ISM is a Multisig ISM where "Validators" of a specific Hyperlane route sign attestations that a specific message on an origin chain is a valid message to be processed on the destination chain. In the case where the set of Validators have not hit the required signature threshold to successfully process a Hyperlane message on the receiving chain, funds will not be accessible by the user on either chain until the threshold is met (once met, funds will be sent to the user on the destination chain). This generalizes to the different requirements for different ISMs. The Hyperlane documentation explains the different types of ISMs in more detail: [https://docs.hyperlane.xyz/docs/reference/ISM/specify-your-ISM](https://docs.hyperlane.xyz/docs/reference/ISM/specify-your-ISM)
## Go Fast Failures
If a transfer timeout occurs, meaning a user's intent does not receive a response from solvers within a predefined time frame, the solver initiates a refund process to ensure that users do not lose funds.
Here's a breakdown of what happens in the event of a timeout:
1. Intent Expiration: When a user initiates an intent by calling the `submitOrder` function on the source chain, a time limit is specified. Solvers monitor the intent and assess whether they can fulfill it within this period. If no solver fills the intent before the timeout, the refund process begins.
2. Refunds: Once the timeout period is reached without fulfillment, the solver calls a function on the contract to trigger a refund process. This is handled on-chain, and includes any fees initially allocated from the user for solver compensation.
*Notice that both chains maintain their own channel IDs for the channel, which might not be the same. As an analogy, you might think of the different chains as cities and the channel as a road connecting them. IBC packets are cars driving across the road*
When transferring a fungible token from one chain to another over a channel, the denomination of the token on the destination chain is uniquely and predictably determined by the source denomination + the channel(s) over which the token was transferred. Specifically the denomination algorithm is:
```text Naming Algorithm theme={null}
ibc_denom = 'ibc/' + hash('path' + 'base_denom')
```
*`hash` is typically the sha256 hash function*
Continuing the example from above, the denom of this version of WETH.axl on Terra2 is:
```text axlWETH on Terra2 theme={null}
axlweth_on_terra2_denom = 'ibc/' + hash('transfer/channel-6/weth-wei')
axlweth_on_terra2_denom = 'ibc/BC8A77AFBD872FDC32A348D3FB10CC09277C266CFE52081DE341C7EC6752E674'
```
### So Different Paths Produce Different Tokens
Now that you understand that IBC denoms get their names from their paths, you understand the crux of the routing problem: **The same asset transferred to the same destination over two different paths will have different denominations.**
Continuing the example from above, WETH.axl transferred directly from Axelar to Terra2 will have a different denom than WETH.axl transferred through Osmosis:
To make matters worse, multiple channels can exist between the same two chains (IBC is permissionless afterall), and IBC uses channel identifiers--not chain identifiers--to construct denoms. That means two different versions of the same asset will exist on the destination chain even when tokens are transferred from the same source chain, if they're transferred over two different channels:
2. Use `/fungible/route` (`route()`) to get a quote when the user selects all their chains & tokens and inputs one of their amounts
3. Use `/fungible/msgs` (`messages()`) to get a transaction for the user to sign after they've locked in the route & begun the transaction creation process
4. Use `/tx/track` (`trackTransaction()`) to register the transaction for tracking (or `/tx/submit` to register and submit it on-chain)
5. Use `/tx/status` (`transactionStatus()`) to get the real-time status of the transaction as it progresses across bridges and/or chains.
# Post-Route Actions
Source: https://skip-go.mintlify-go.com/go/general/post-route-actions
How to specify actions to perform after a route of transfers/swaps is completed
Use the `post_route_handler` parameter of `/v2/fungible/msgs` endpoint to define actions that will be executed on the destination chain after a route transfer or swap is completed. These actions are executed within the same transaction as the original swap or transfer.
This handler allows developers to build omni-chain and omni-token workflows where users can swap, liquid stake, deposit, buy an NFT, or take any other action starting from any chain or any token in Cosmos -- all in a single transaction.
This parameter currently supports:
1. CosmWasm contract calls on the destination chain
2. `autopilot` support for liquid staking interactions on Stride
### Background Info
All `post_route` actions must have the following characteristics:
* **Permissionless:** Skip Go can only support permissionless actions because the underlying protocols (e.g. IBC hooks, IBC callbacks, packet-forward-middleware) derive the addresses they use to call contracts based on the origin of the transfer. Skip Go supports both `ibc-hooks` and `ibc-callbacks` depending on which module the destination chain has implemented (they are incompatible approaches), automatically generating the appropriate message payloads for each. This means one user originating on two different chains or starting with two different tokens will eventually call the final contract / module with different addresses. You can only reliably permission actions that you know will 1) always originate on the same chain and 2) always take the same path to the destination chain. In general, we recommend not making this assumption unless you are an interoperability expert
* **Single-token input:** The underlying IBC transfer protocol (ICS-20) doesn't support transfers of more than 1 token denom in a single transfer message, so we can only send 1 token denom to the final contract or module at a time. This means the contract or module in the `post_route_handler` must not require multiple token denoms sent to it simultaneously. For example, a classic LP action where the user must provide tokens in both sides of the pool simultaneously would not work.
For pre-launch chains, we focus on establishing communication:
Before submitting this form, ensure you have:
After submitting this form:
For questions or support, please open a ticket in our{' '} Discord .