evm-gateway-contract
v1.0.8
Published
EVM Gateway Contract
Downloads
36
Maintainers
Readme
Router EVM Gateway contracts
Overview
The Router EVM Gateway contract will bridge EVM chains with the Router Chain. We can deploy this gateway contract on any EVM-compatible chain. The gateway contract validates all incoming requests coming from the router chain using the validator set signatures. These signatures should have at-least 2/3 validator power. The current validator set(valset) can update the validator set to a new valset by invoking the updateValset function.
The EVM gateway contract implements two ways to connect with other chains.
- Using the Router Chain contract as a middleware contract
- Directly calling the destination contract without any middleware contract
The Router Chain contract as middleware flow
In this scenario, The user application will have one contract on the Router chain. This router chain contract will behave as a middleware contract for the application. The EVM gateway contract implements the following functionalities for the user application.
- Send a request to the Router chain
- Handle requests from the Router chain
Send a Request to the Router Chain
To send a request to the router chain, the user contract needs to call the following function and needs to provide the bridge contract address & payload bytes
# Gateway contract address variable
Gateway public gatewayContract;
# User/ Application contract constructor
constructor(address gatewayAddress) {
gatewayContract = Gateway(gatewayAddress);
}
# example of send request to the Router chain
function sendRequestToRouter(bytes memory payload, string memory routerBridgeContract) public payable {
# implement the business logic
uint64 eventNonceIdentifier = gatewayContract.requestToRouter(payload, routerBridgeContract);
}
Handle Requests from the Router
To handle requests coming from the router chain, the user contract needs to implement the following function in their contract
function handleRequestFromRouter(string memory sender, bytes memory payload) external {
# implement the business logic
}
In case of state update from the requestFromRouter function we are emitting the following event
# This is OutBound Request Acknowledgement event
event EventOutboundAck(
string relayerRouterAddress,
string outboundTxRequestedBy,
string chainId,
uint64 chainType,
uint64 eventNonce,
uint64 outboundTxNonce,
bytes contractAckResponses,
uint8 exeCode,
bool status
);
Currently we are emitting this outbound acknowledgement event in two cases only. The ChainType, ChainId, and OutboundTxNonce will have same values in all cases.
When the txn is valid, but It is getting executed past the timeout. In this scenario, we will update the nonce mapping to 1 as executed and the event will have the following values
emit EventOutboundAck( relayerRouterAddress, routerBridgeAddress, chainId, chainType, eventNonce, outboundTxNonce, "", 1, false );
When the txn is valid and executed its handler calls to user contract In this scenario, we will update the nonce mapping to 1 as executed and the event will have the following values
emit EventOutboundAck( relayerRouterAddress, routerBridgeAddress, chainId, chainType, eventNonce, outboundTxNonce, data, 0, success );
Here, data and success values are coming from the handlerExecuteCalls funciton. Data bytes can be decoded according to the success value. If it is true, then it will be array of bool values and if it is false, then it will string value.
Directly calling the destination contract without any middleware contract
In this scenario, Having a contract on the Router chain is not mandatory for a user application. The application can send or receive contract calls directly from the other chains. The EVM gateway contract implements the following functionalities for the user application.
- Send a request to the destination chain
- Handle requests from the source chain
- Send acknowledgment from the destination to the source chain
- Receive ack request on the source chain
Send a Request to the Destination Chain
To send a request to the destination chain, the user contract needs to call the following function and needs to provide the following payloads
# Gateway contract address variable
Gateway public gatewayContract;
# User/ Application contract constructor
constructor(address gatewayAddress) {
gatewayContract = Gateway(gatewayAddress);
}
# example of send request to the Router chain
function sendRequestToDestination(
uint64 chainType,
string memory chainId,
address destinationContractAddress,
string memory str
) public payable {
# implement the application business logic
bytes memory payload = abi.encode(str);
uint64 timestamp = <timestamp value>;
bytes[] memory addresses = new bytes[](1);
addresses[0] = toBytes(destinationContractAddress);
bytes[] memory payloads = new bytes[](1);
payloads[0] = payload;
uint64 eventIdentifier = gatewayContract.requestToDest(
timestamp,
<isAtomicCalls>,
Utils.AckType.ACK_ON_SUCCESS,
# provide the gas limit and price for the ack execution, in testnet it can be zero
Utils.AckGasParams(
0,
0
),
# provide the gas limit and price for the destination contract calls execution, in testnet it can be zero
Utils.DestinationChainParams(
0,
0,
chainType,
chainId
),
Utils.ContractCalls(
payloads,
addresses
));
# implement the application business logic
}
Handle requests from the source chain
To handle request coming from the source chain, the user contract needs to implement the following function in their contract
function handleRequestFromSource(
bytes memory srcContractAddress,
bytes memory payload,
string memory srcChainId,
uint64 srcChainType
) external returns (bytes memory) {
# It is recommended to verify the source caller contract address(srcContractAddress)
# Also, verify this funciton is invoked by the gateway address only
require(msg.sender == address(gatewayContract));
# implement the business logic
}
In case of state update from the requestFromSource function we are emitting the following event
# This is CrossTalk Request Acknowledgement event
// This is OutBound Request Acknowledgement event
event CrossTalkAckEvent(
string relayerRouterAddress,
bytes srcContractAddress,
string srcChainId,
uint64 srcChainType,
string chainId,
uint64 chainType,
uint64 eventIdentifier,
uint64 crossTalkNonce,
uint64 indexed eventNonce,
bytes contractAckResponses,
uint8 exeCode,
bool status
);
Currently, we are emitting this outbound acknowledgment event in two cases only. In both cases, apart from contractAckResponses, execCode, and status all will have the same values.
When the txn is valid but It is getting executed past the timeout. In this scenario, we will update the nonce mapping to 1 as executed and the event will have the following values
emit CrossTalkAckEvent( relayerRouterAddress, srcContractAddress, srcChainId, srcChainType, chainId, chainType, eventIdentifier, crossTalkNonce, eventNonce, "", 1, false );
When the txn is valid and executed its handler calls to user contract In this scenario, we will update the nonce mapping to 1 as executed and the event will have the following values
emit CrossTalkAckEvent( relayerRouterAddress, srcContractAddress, srcChainId, srcChainType, chainId, chainType, eventIdentifier, crossTalkNonce, eventNonce, data, 0, success );
Here, data and success values are coming from the handlerExecuteCalls funciton. Data bytes can be decoded according to the success value. If it is true, then it will be (bool[] & bytes[]) and if it is false, then it will custom error containing (bool[] & bytes[]).
Send acknowledgement from the destination to the source chain
Each valid cross-talk request will emit one acknowledgement event. But the application contract can decide whether they want to receive this ack or not. For that they can tell their condition while initiating this coss-talk request. The acknowledgement is sent back to the source application contract on basis of provided AckType and the execution result of the contractCalls on the destionation side.
enum AckType {
NO_ACK,
ACK_ON_SUCCESS,
ACK_ON_ERROR,
ACK_ON_BOTH
}
Receive ack request on the source chain
To receive the ack request on the source chain, the user contract need to implement the following function in the application contract
# Gateway contract address variable
Gateway public gatewayContract;
# User/ Application contract constructor
constructor(address gatewayAddress) {
gatewayContract = Gateway(gatewayAddress);
}
function handleCrossTalkAck(
uint64 eventIdentifier,
bool[] memory execFlags,
bytes[] memory execData
) external {
# implement the business logic
}
Update Validator Set
This is used to update validator set on the gateway contract. This will be called by the router chain validator set only.
Please use the following instruction to setup, test and deploy the project
Setup
To run any command you need to have .env in your local
cd router-gateway-contracts/evm
cp .env.test .env
then update the value in .env file.
Compile Project
cd router-gateway-contracts/evm
npm install
npx hardhat compile
Run Tests
Use the following commands to run the test cases:
npx hardhat help
npx hardhat test
GAS_REPORT=true npx hardhat test
Deploy Gateway Contract on live network
Add gateway contract constructor arguments in args.json
cd router-gateway-contracts/evm
npx hardhat deploy:Gateway --network <network> --chaintype <chainType> --valsetnonce <valsetNonce> --validators <validators> --powers <powers>
Upgrade Gateway Contract on live network
cd router-gateway-contracts/evm
npx hardhat upgrade:Gateway --network <network>
Verify GateWay Contract on a network
cd router-gateway-contracts/evm
npx hardhat verify --constructor-args <args-file-path> <gateway-contract-address> --network <network-name>
Example:-
npx hardhat verify --constructor-args scripts/arguments.js 0x610aEe9387488398c25Aca6aDFBac662177DB24D --network polygonMumbai
Generate ABIs, BIN and GO bindings of the Gateway Contract
cd router-gateway-contracts
npm install
sh scripts/createBinding.sh