Chain Abstraction Guide
A chain abstraction integration will generally involve two steps: writing an adapter contract and constructing calldata
for users to send.
The Chain Abstraction Reference is an example repository with adapter contracts and a frontend that implements the full stack of the integration. We'll be referencing code from it in this guide.
Adapter Contract
The adapter is a smart contract that integrators must build. It should implement the xReceive
interface and be deployed to all destination chains that the integrating protocol supports. After the user's transaction has been dispatched and their funds/data arrive on the destination chain, this contract will be called to handle all the execution logic for the protocol.
The execution logic includes a swap and a "forward call". The swap converts the Connext-bridged asset into the asset that will be used for the protocol's function call (a.k.a "target function"). The forward call is where the adapter contract actually calls the "target function".
The following sections will walk through how to implement these two parts.
Installation
Assuming you're using Foundry, you can add the Connext contracts to your project by running the following command to install the connext-integration
repository as a submodule:
The library will be installed to lib/connext-integration
.
SwapForwarderXReceiver
The SwapForwarderXReceiver
is an audited abstract contract that should be implemented by adapter contracts.
Notice that the _prepare
function expects a bytes memory _data
parameter that will be decoded into:
_swapper
: The specific swapper that will be used for the swap on the destination domain._toAsset
: The asset that should be swapped into._swapData
: The encoded swap data that will be constructed offchain (using the Chain Abstraction SDK in the next section)._forwardCallData
: The forward call data that the receiver contract will call on the destination domain.
Adapter Contract
The adapter inherits SwapForwarderXReceiver
and implements the _forwardFunctionCall
method.
For example, let's take a look at the Greeter
from the Chain Abstraction Reference repo and pretend that this is the existing contract for a protocol that wants to use the chain abstraction pattern.
The "target function" we want to call cross-chain is greetWithTokens
which takes payment in any amount of WETH to update the greeting.
So GreeterAdapter
below is the adapter contract that needs to be created:
The _forwardFunctionCall
function unwraps data which includes arguments for the forward call (greetWithTokens
) and the amount of tokens received after the swap. It then "forwards" the call to the greeter
contract.
That's it!
Origin Domain Transaction
The second part of the integration is to set up the transaction that users send on the origin domain. This involves a few steps but Connext's Chain Abstraction SDK makes this process easier:
Use
getPoolFeeForUniV3
to fetch thepoolFee
for a specific swap route on the destination domainConstruct the
forwardCallData
for the integrating protocol's "target function"Use
getXCallCallData
to construct thecalldata
that will be passed intoxcall
Use
prepareSwapAndXCall
to combine the swap and thexcall
into a single transaction request
With Connext's Core SDK:
Use
estimateRelayerFee
to fetch therelayerFee
for thexcall
Use
calculateAmountReceived
to show users the expected amount received on the destination domain
Installation
For installing the SDKs, use Node.js v18.
1) Get the pool fee
First we need to retrieve inputs for the destination swap. The function getPoolFeeForUniV3
returns the poolFee of the UniV3 pool for a given token pair which will be used in the UniV3 router execution. The poolFee is the fee that is charged by the pool for trading tokens.
The function takes four parameters:
domainId
: The target domain ID.rpc
: The RPC endpoint for a given domain.token0
: The first token address.token1
: The second token address.
The function returns a Promise
that resolves to a string representing the poolFee of the UniV3 pool.
Example
2) Encode the forward call
This step depends on the target function. In our Greeter
example, the encoded calldata is quite simple:
3) Construct the xcall
The getXCallCallData
function generates calldata to be passed into xcall
. This combines the destination swap and the forward call.
It takes four parameters.
domainId
: A string representing the destination domain ID.swapper
: A string representing which swapper should be used. It can beUniV2
,UniV3
, orOneInch
.forwardCallData
: encoded data for passing into the target contract usingabiencoder
.params
: An object containing the following fields.fallback
: The fallback address to send funds to if the forward call fails on the destination domain.swapForwarderData
: An object with the following fields.toAsset
: Address of the token to swap into on the destination domain.swapData
: Calldata that the swapper contract on the destination domain will use to perform the swap.forwardCallData
: Calldata that the xReceive target on the destination domain will use in the forward call.
The function returns the encoded calldata as a string.
Example
4) Prepare swap and xcall
The prepareSwapAndXCall
function constructs the TransactionRequest
that contains the origin swap and xcall
.
It takes two parameters:
signerAddress
(required): The address of the signer to send a transaction from.params
: An object containing the following fields:originDomain
(required): The origin domain ID.destinationDomain
(required): The destination domain ID.fromAsset
(required): The address of the asset to swap from.toAsset
(required): The address of the asset to swap to.amountIn
(required): The number of fromAsset tokens.to
(required): The address to send the asset and call with the calldata on the destination.delegate
(optional): The fallback address on the destination domain which defaults toto
.slippage
(optional): Maximum acceptable slippage in BPS which defaults to 300. For example, a value of 300 means 3% slippage.route
(optional): The address of the swapper contract and the data to call the swapper contract with.callData
(optional): The calldata to execute (can be empty: "0x").relayerFeeInNativeAsset
(optional): The fee amount in native asset.relayerFeeInTransactingAsset
(optional): The fee amount in the transacting asset.
The function returns a Promise that resolves to a TransactionRequest
object to be sent to the RPC provider.
Example
5) Estimate relayer fee
Relayer fees must be paid by the user initiating xcall
. The Core SDK exposes an estimateRelayerFee
function (see Estimating Fees) that returns an estimate given current gas prices on the origin and destination domains.
6) Estimate amount received
Showing an estimate of the final amount to be used in the destination target function can be informative for users. The getEstimateAmountReceived
function returns this estimate which accounts for slippage from the origin and destination swaps as well as the bridge operation itself.
Last updated