Swapping
Integrations
Advanced Scenarios
EVM Integration

EVM contract integration

Overview

The Vault smart contract, besides holding assets, acts as an entry point for starting a swap without requiring a deposit address. Furthermore, for cross-chain messaging swaps, the Vault will call the destination address with a particular function signature.

For more information, learn How Swapping Works.

Initiate simple swap

To make a swap the user needs to call swap on the Vault of the source chain, specifying the parameters described in the table below. In the case of swapping ERC-20 tokens an approval is required.

ParamDescriptionData type
dstChainDestination chain for the swapuint32
dstAddressAddress where the swapped tokens will be sent to on the destination chain. Addresses must be encoded into a bytes typeaddress
dstTokenToken to be received on the destination chainuint32
srcTokenAddress of the token to be swapped from the source chainaddress
amountAmount of the source token to be swapped. When swapping a native asset, the msg.value passed in the call will be used instead. uint
cfParametersAdditional metadata for future features. Currently unused.bytes
    // Swap native token
    function xSwapNative(
        uint32 dstChain,
        bytes calldata dstAddress,
        uint32 dstToken,
        bytes calldata cfParameters
    ) external payable;
 
    // Swap ERC20 token
    function xSwapToken(
        uint32 dstChain,
        bytes calldata dstAddress,
        uint32 dstToken,
        IERC20 srcToken,
        uint256 amount,
        bytes calldata cfParameters
    ) external;

Initiate swap with call to a receiver with cross-chain messaging (CCM)

Chainflip supports cross-chain messaging, calling a smart contract on the destination chain and passing a message between chains. The smart contract call looks very similar to the previous ones but with some added parameters.

For more information see Cross-Chain Messaging. The swap could have originated in any supported chain, irrespective of whether the chain supports smart contracts.

Invalid parameters may lead to loss of funds. Make sure all parameters are correct, supported and follow the Chainflip protocol's nomenclature since reverting a transaction is not guaranteed.

ParamDescriptionData type
messageMessage that is passed to the destination address on the destination chain. It must be shorter than 10k bytes.bytes
gasBudgetGas budget for the call on the destination chain. This amount is based on the source asset and will be subtracted from the input amount and swapped to pay for gas.uint
    // Swap native token
    function xCallNative(
        uint32 dstChain,
        bytes calldata dstAddress,
        uint32 dstToken,
        bytes calldata message,
        uint256 gasAmount,
        bytes calldata cfParameters
    ) external payable;
 
    // Swap ERC20 token
    function xCallToken(
        uint32 dstChain,
        bytes calldata dstAddress,
        uint32 dstToken,
        bytes calldata message,
        uint256 gasAmount,
        IERC20 srcToken,
        uint256 amount,
        bytes calldata cfParameters
    ) external;

Receive call and asset on the receiver contract (CCM)

Chainflip's Vault will transfer the destination token amount to the specified address either within the same call (for native token) or by transfering the ERC20 token to it. Then it will call the destination address with the following parameters:

ParamDescriptionData type
srcChainSource chain for the swapuint32
srcAddressAddress that initiated the swap on the source chain. Addresses are encoded into a bytes typeaddress
messageMessage that is passed to the destination address on the destination chain.bytes
tokenAddress of the token transferred to the receiver. A value of0xEeee...eeEEeE represents the native token.address
amountAmount of the destination token transferred to the receiver. If it's the native token, the amount value will equal the msg.value. uint

In order for a contract to be a valid receiver it must implement the Solidity function signature below on the destination address.

function cfReceive(
    uint32 srcChain,
    bytes calldata srcAddress,
    bytes calldata message,
    address token,
    uint256 amount
) external payable;

It's the receiver's responsability to correctly implement the function's interface and to ensure the call doesn't revert. If the receiver can't garantee that the receiving logic won't revert, it is recommended to use try/catch-like structure to handle the reversion. This is to avoid the full transaction reverting and therefore the tokens failing to be transferred.

IMPORTANT! Chainflip will transfer tokens to the receiver and then make the call. For ERC-20 tokens, the logic has to assume the amount has been transferred. An attacker could call this function and fake the transfer, exploiting the receiver. We strongly suggest that only the Chainflip Vault can call your function, unless you're transferring all tokens out of the receiver in the same call (like DEX Aggregators).

Here is an example of the function with the adequate access control:

contract CFReceiver {
 
    function cfReceive(
        uint32 srcChain,
        bytes calldata srcAddress,
        bytes calldata message,
        address token,
        uint256 amount
    ) external payable {
        require(msg.sender == cfVault, "CFReceiver: caller not CF Vault");
    }
 
    ...
}

You can find an example of an implementation of a receiver contract with the mentioned interface here (opens in a new tab). The contract also has other logic that is not necesary like the cfReceivexCall (currently unsupported) or the logic to update the cfVault address.

If the receiver is not a contract, doesn't have the specified interface or the logic in the receiver reverts, the Chainflip protocol will not submit the transaction to the destination chain. This may result in a loss of funds.

Chain and Asset Notation

Chainflip uses it's own notation for chain and token within the the smart contracts. These are the values for the source and destination chains and assets parameters described above. The same values apply for the the corresponding testnets (e.g. Ethereum mainnet and Sepolia share the same value).

Supported Assets

AssetID
ETH1
FLIP2
USDC3
DOT4
BTC5
arbETH6
arbUSDC7
USDT8
SOL9
solUSDC10

Supported Chains

ChainID
Ethereum1
Polkadot2
Bitcoin3
Arbitrum4
Solana5