How to integrate anyCall V6?
Last updated
Last updated
anyCall contract is deployed at: 0xC10Ef9F491C9B59f936957026020C321651ac078
Chains supported: BNB Chain, Polygon,Ethereum, Optimism, Gnosis Chain, Fantom, Moonriver, IoTeX, Arbitrum, Avalanche, Harmony
Audit report: PeckShield
You can also test anyCall V6 on testnets freely now. Follow the link below for more information.
anyCall V6 added an important middleware called AnyCallExecutor. This sandbox contract will make the final execution of your destination contract. Hence any caller verification modifier should authorize AnyCallExecutor.
The anyCall protocol is made up of three main functions anyCall
, anyExec
. These two methods exist in our deployed anyCall contracts.
DAPPS need to develop and deploy a sender contract on chain A and a receiver contract on chain B. On the receiver contract, a function named anyExecute
needs to be present and it will be called.
DAPPS sender contract(Chain A) -> anyCall
(Chain A) -> SMPC Network -> anyExec
(Chain B) ->AnyCallExecutor
-> anyExecute
by DAPP receiver contract(Chain B)
DAPPS sender contract call anyCall
on the Chain A. Then SMPC network will relay anyCall event which will call AnyCallExecutor
to make the final execution of the anyExecute
function on the DAPP receiver contract. Hence a function named anyExecute
needs to be present in the DAPP receiver contract.
If the anyExec
contract execution failed on Chain B, it can call _fallback
function to send messages back to Chain A.
anyExecute
DAPP receiver contract(Chain B failed) -> anyCall(Chain B) -> SMPC Network -> anyExec(Chain A) -> anyFallback
DAPP sender contract(Chain A)
More docs on fallback can be found below:
As stated above, AnyCallExecutor
will make the final execution to your destination contract as a sandbox.
The address of this executor contract is stored in the main anycall contract. It can be accessed with an interface function like below.
The executor should then be saved in your contract constructor
function anyCall( address _to, bytes calldata _data, address _fallback, uint256 _toChainID, uint256 _flags )
function anyExec( address _to, bytes memory _data, address _fallback, string memory _appID, RequestContext memory _ctx )
Sender Contract: Your sender contract needs to call the method anyCall
on the official anyCall contract.
Receiver Contract: A method named anyExecute
needs to exist on your _to
contract address. This is needed because anyExec
(Our deployed anyCall Contract) will call anyExecute
on your receiver contract.
Refer to this example for the correct interface to implement
This example will send a simple message from BNB chain to Polygon chain. This message is represented as a string event.
Deployed sender contract on BNB chain and receiver contract on Polygon:
Sender contract on BNB chain: https://bscscan.com/address/0xa7ce20b8254fcb2caa6c43315be470df3438a50a#code
Receiver contract on Polygon chain: https://polygonscan.com/address/0xF9D415fcDe051DE5D36D4c6faE03185759cCBe1E#code
1. Import the anyCall interface, so we can call the deployed anyCall contract.
2. Define the AnycallV6senderBNBMainnet
contract.
anycallcontractbnb
: define where the anyCall contract exists which will use the interface above to call the anyCall. This is the contract deployed by Multichain team.
owneraddress
: Simple owner address protection.
receivercontract
: The receiver contract deployed on Polygon.
An event is also defined to show what message is being sent on Polygon. This is not 100% necessary.
3. step1_initiateAnyCallSimple
This method takes a string input. Then it uses the interface and anyCall contract address defined above to call the anyCall function with the following parameters.
Address _to: This is the receiver contract address on Polygon.
Bytes calldata _data: This is the bytes data the destination anyExecute
will take. This can be any data you want. In this case, we are using abi.encode to encode our message string. And the destination contract will process this byte data.
Address _fallback: We're putting address(0) because we don't have a fallback function.
Uint256 _toChainID: The destination chain id. Polygon is 137.
uint256 _flags: 0 in our case because we want gas fees to be paid on the destination chain.
The function anyExecute
is executed with the _data
passed in from anyCall above.
Compatibility Notes:
It needs to be named anyExecute.
returns (bool success, bytes memory result)
has to be the return values to be compatible.
This function simply decodes the _data
that was previously encoded by abi.encode
back to a string and creates an event containing that message.
Step 1 on BNB Chain:
The function step1_initiateAnyCallSimple(string calldata _msg)
is called with the msg 'hi polygon 7 with correct returns'
Txhash: https://bscscan.com/tx/0x5f4f2486153aced0f3bca0d967836624cc71525417a0e32c9a81c266425c1cae
Step 2 on Polygon Chain:
SMPC networks relay the anyCall event, then invoke anyExecute function on our Polygon receiver contract.
Txhash: https://polygonscan.com/tx/0x41dadc279612965c142c1e37262d70652a30ea44482ea9aced1afab1f0e18b35
This function emits the message from BNB chain.
Fees: Before using the anyCall protocol, projects need to deposit some gas fee to the destination anyCall contract.
Note that the address argument of the deposit(address _account)
function should be the sender contract address on the source chain. The fee depends on the calldata size. The totalCost of one transaction = (gasUsed - gasleft()) * (tx.gasprice + _feeData.premium)
You can also pay fees on the source chain, refer to this article
Whitelisting is not required anymore for anyCall V6.
Param | Type | Description |
---|---|---|
Param | Type | Description |
---|---|---|
_to
address
The target contract to interact with on _toChainID
_data
bytes calldata
The calldata supplied for the interaction with _to
anyExecute
will be run with this _data
on the receiver contract you deployed.
_fallback
address
This is the fallback contract on the SOURCE CHAIN if the destination chain contract execution failed. If you put address(0)
, it means you don’t have a fallback contract.
Note that this fallback mechanism is done by the destination chain issuing another anyCall with the following parameters. So you need to design your fallback function to be compatible with the following parameters. abi.encodeWithSelector(IApp.anyFallback.selector, _to, _data)
_toChainID
uint256
The target chain id to interact with
_flags
uint256
How dapps are paying gas fee of tx execution:
0: Gas fee paid on destination chain. A gas balance needs to be topped up by dapps on destination chain.
2: Gas fee paid on source chain. Allow users to pay the gas fee. (The fee details will be explained further)
_to
address
The target contract to interact with
_data
bytes calldata
The calldata supplied for the interaction with target
_fallback
address
The address to call on _fromChainID
if the cross chain interaction fails
_fromChainID
uint256
The originating chain id
_ctx
struct
A struct containing information about the source chain anyCall request.
struct RequestContext { bytes32 txhash; address from; uint256 fromChainID; uint256 nonce; uint256 flags; }
Data includes:
1. txhash
: Original txhash
2. from
: Original txhash
txhash: Original txhash
3. fromChainID
: The source chain id
4. nonce
: The nonce of anyCall, used to keep track of order of anyCall.
5. flags
: The fee setting flag. Used to determine if fees need to be deducted on destination chain.