Integration
Swapping and Aggregation
Cross-Chain Messaging

Cross-Chain Messaging

Introduction

As explained in the Cross-Chain Messaging section, cross-chain messaging allows for developers to send messages cross-chains along with a swap. Cross-chain messaging swaps follow the same flow as regular swaps but with the addition of a message that is sent to a receiver contract on the destination chain.

As described in How Swapping Works, swaps in Chainflip can be initiated via requesting a deposit address or via a smart contract call (only in EVM chains). Both of those methods support cross-chain messaging by specifying a message parameter and a gasBudget parameter. The SDK makes it seemless to request a deposit address with CCM or execute a swap with CCM.

More details in the EVM smart contract implementation can be found in the EVM vault implementation.

Implementation Checklist

  • The destination chain needs to be a Chainflip supported EVM-compatible chain.
  • The swap must be initiated by requesting a deposit address or via a smart contract call specifying the extra specifying the message and the gasBudget parameters in both cases.
  • The message should be shorter than 10k bytes.
  • The gas budget needs to be paid upfront on the source chain as part of initiating the swap. In the future, Chainflip might support paying the gas budget on the destination chain.
  • The gas budget provided needs to be sufficient to cover the gas costs of the receiver's logic on the destination chain, otherwise the transaction won't be broadcasted.
  • The receiver on the destination chain needs to be a smart contract with the interface described here.
  • The receiver on the destination chain needs to ensure that the receiving logic won't revert.

For more implementation details about the smart contract implementation see Vault.

The message must be shorter than 10k bytes. Also, the gas limit on the destination chain will be capped to 10 million gas.

Example

  1. A user has BTC on the Bitcoin blockchain and wants to swap it to USDC on Ethereum blockchain and then execute some logic on a smart contract.
  2. The user requests a bitcoin deposit address as described here (only when the source chain is EVM-compatible, this can be initiated via a smart contract call).
  3. The user defines, as part of requesting a deposit adddress, two additional parameters: message and gasBudget. The message can be an arbitrary HEX-encoded sequence of bytes. The gasBudget is the source asset amount that will be used to pay for gas.
  4. The user transfers an amount to swap.
  5. The Chainflip protocol swaps the gasBudget amount to the destination chain's native asset to pay for gas — ETH in this case — and the remaining swap amount (input amount - gasBudget) is swapped to the destination asset — USDC in this case. Therefore, input amount must be at least larger than gasBudget amount.
  6. After the threshold signature is completed, a transaction originating from the Vault contract transfers the destination asset to the specified receiver address, on the destination chain, and makes a call to that address with a specific interface passing the user's message. The gas limit on that call is set according to the gasBudget swap output and the current gas price.
  7. The receiver contract executes its logic, which can entail decoding the received message from the source chain and execute logic accordingly, i.e. swap USDC into <any-long-tail-asset> in a Uniswap pool.

Gas budget estimation

The user has to provide a gas budget for the destination chain that must cover the entire transaction on the destination chain, that is both the Chainflip signature verification and the execution of the user's logic. The gas limit used on the destination chain is calculated as follows:

gasLimit = gasBudgetAfterSwap / (baseFee + priorityFee)

where:

  • gasBudgetAfterSwap is the input gasBudget provided by the user after being swapped by Chainflip into the destination chain's native asset.
  • baseFee and priorityFee are the current base and priority fees on the destination chain.

The final gasLimit has to be more than the amount of gas the transaction will consume on the destination chain, otherwise the transaction won't be broadcasted. That gas limit is capped to 10M gas. There are multiple factors to take into account when estimating the gas budget:

  • The baseFee and priorityFee of the destination chain are moving targets so it's advised to use a conservative estimation.

  • That gas limit has to also cover the chainflip signature verification logic. A good approximation of that gas cost is a constant 120k gas plus the number of bytes of the message times 17. That is considering calldata costs 16 gas per byte plus a margin for additional verification overhead.

    SigVerificationGas = 120k + messageLength * 17

Therefore, the gasBudget required, assuming the user requires userLogicGas to execute the user logic, can be calculated as follows:

gasBudget = (userLogicGas + 120k + messageLength * 17) * (baseFee + priorityFee) * overestimationRatio * swapPrice 

An overestimation, overestimationRatio, is advised both due to the gas prices and the gas swap being non-deterministic and constantly changing. Currently Chainflip does not issue refunds of unspent gas but that will change in the future.

Gas budget estimation example

  • User wants to swap 0.5 BTC to FLIP, destination chain being Ethereum.
  • User needs 100k gas to execute their logic on the destination chain.
  • The message is 100 bytes long.
  • Current base fee on the destination chain is 10 gwei and the current priority fee is 1 gwei.
  • The user overestimates the gas budget by 30% for safety.
  • BTC will be swapped to ETH for gas, let's use a 0.05 ETH to BTC conversion rate. This can be obtained by getting a quote via the Chainflip SDK.
// Total gas limit for the CCM transaction
gasLimit = (userLogicGas + 120k + messageLength * 17) * overestimationRatio = (100k + 120k + 100 * 17) * 1.3 = 288,210 gas units
 
// Destination's chain native token needed to cover for the gas
nativeTokenNeeded = gasLimit * (baseFee + priorityFee) = 288210 * (10 + 1) = 3,170,310 Gwei
 
// Required amount of input swap token to be paid as gas (gasBudget)
gasBudget = nativeTokenNeeded * swapPrice = (3,170,310 / 10^9) * 0.05 = 0.00016 BTC

Therefore, in this example, the user would specify a gasBudget of 0.00016 BTC and provide a total of 0.50016 BTC when making a swap.

CCM broadcast failure

While Chainflip will do it's best to broadcast and succesfully execute any swap with cross-chain messaging, there are some factors that can make the swap fail that are external to Chainflip. Here are some examples.

  • The gas budget provided is not enough to cover the gas costs of the receiver's logic on the destination chain.
  • The receiver contract not implementing the correct interface (or being an EOA).
  • The logic executed on the receiver's contract reverts.

Chainflip will use TSS to sign over a valid payload for the destination chain and try to broadcast it. If a transaction can't be broadcasted for any reason, the signed payload will be publicly accessible on the Chainflip State Chain for the user. The user can then sign over the payload and broadcast the transaction.

If the transaction reverts due to not enough gas, the user can sign that payload and broadcast it with a higher amount of gas. If it's due to the receiving logic reverting, the user can try to modify the state of the receiving contract before broadcasting it again. The payload will be valid until two key rotations have taken place.

The signed payload can be broadcasted with different gas settings but it can't modify any value in that payload (e.g. message). Therefore, always make sure to test the receiving logic and ensure it won't revert before initiating a swap.

;