Calling a Contract on the Receiving Chain
NXTP Routers are able to act as cross-chain meta transaction relayers. This means they are able to call contracts on the receiving chain using data passed in from the sending chain. This can greatly enhance user experience by allowing users to interact with the receiving chain without needing funds for gas on the receiver side. Also this can enable "money legos" types of interactions across chains.
note
It is not possible to atomically verify the result on the sending chain, due to the asynchornous nature of the cross-chain transaction.
The flow is as follows:
- The sender prepares a transaction on the sending chain with the calldata and callTo address set on chain.
- The router prepares the same transaction on the receiver side.
- The sender signs the payload when it confirms the receiver's funds are locked.
- The relayer fulfills the transaction on the receiver side, sends the funds to the interpreter address, and calls the contract specified in
callTo
using the calldata.
#
FulfillHelper ContractThe NXTP system uses a FulfillHelper contract to enable cross-chain meta transactions. The following steps must be completed to call a contract on a receiving chain:
- Generate calldata for the contract call on the receiving chain.
- Pass in the
callData
(generated calldata above) andcallTo
(address of called contract on receiving chain) params to thegetTransferQuote
andprepareTransfer
methods on the NXTP SDK.
The router and relayer will prepare
and fulfill
the transaction on the receiving chain with the callData
and callTo
.
caution
If the transaction reverts, the funds will be sent back the the provided receivingAddress
.
#
Example CodeThe integration tests in the contracts repo demonstrate the cross-chain meta transaction functionality as a full working example.
Here is a simplified code snippet:
const callData = counter.interface.encodeFunctionData("incrementAndSend", [ assetId, other.address, amount,]); // encode data using ABI
const bid = await sdk.getTransferQuote({ callData, sendingChainId, sendingAssetId, receivingChainId, receivingAssetId, callTo: counter.address, receivingAddress: user.address, amount: "1000000",}); // get quote for transfer
// wait for receiver prepared eventconst prepared = await sdk.waitFor( NxtpSdkEvents.ReceiverTransactionPrepared, 100_000, (data) => data.txData.transactionId === transfer.transactionId // filter function);
// sign the transferawait sdk.fulfillTransfer(prepared);// on completion, the contract will be called