1.0.8 • Published 1 year ago

evm-gateway-contract v1.0.8

Weekly downloads
-
License
-
Repository
-
Last release
1 year ago

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. 1. Using the Router Chain contract as a middleware contract 2. 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. 1. Send a request to the Router chain 2. 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. 1. Send a request to the destination chain 2. Handle requests from the source chain 3. Send acknowledgment from the destination to the source chain 4. 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