Skip to content

Instantly share code, notes, and snippets.

@niconiconi
Last active February 3, 2024 06:30
Show Gist options
  • Save niconiconi/1091cd6aeda59cd768ec95d0bb7e0e8a to your computer and use it in GitHub Desktop.
Save niconiconi/1091cd6aeda59cd768ec95d0bb7e0e8a to your computer and use it in GitHub Desktop.
HOW TO CROSS CHAIN

How to Cross Chain

You will need the SendApi interface and ReceiveApi interface.

Send Chain

To send a message, call the zkBridge send() function. here is an simple example

// perform a zkBridge send() in a solidity smart contract function
// the msg.value is the "fee" that zkBridge needs to pay for the cross chain message

//TODO handle your business
//.......
bytes memory payload =abi.encode(
    tokenAddress,   // Address of the token
    tokenChain,     // Chain ID of the token
    amount,         // amount of the token
    to,             // Address of the recipient.
    toChain         // Chain ID of the recipient
)

uint64 nonce = IZKBridge(zkBridgeAddress).send{value:msg.value}(dstChainId, dstAddress, payload);

Estimating Message Fees

You will note in the topmost example we call send() with {value: msg.value} this is because send() requires a bit of native gas token so the relayer can complete the message delivery on the destination chain. If you don't set this value you might get error when calling zkBridge.send()

function estimateFees(uint16 dstChainId) external view override returns (uint256 fee) {
    return IZKBridge(zkBridgeAddress).estimateFee(dstChainId);
}

Receive Chain

Destination contracts must implement zkReceive() to handle incoming messages

The code snippet explains how the message will be received. To receive a message, your User Application contract must implement the IZKBridgeReceiver interface and override the zkReceive function

pragma solidity ^0.8.0;

contract Demo is IZKBridgeReceiver {
    // @notice ZKBridge endpoint will invoke this function to deliver the message on the destination
    // @param srcChainId - the source endpoint identifier
    // @param srcAddress - the source sending contract address from the source chain
    // @param nonce - the ordered message nonce
    // @param message - the signed payload is the UA bytes has encoded to be sent
    function zkReceive(uint16 srcChainId, address srcAddress, uint64 nonce, bytes calldata payload) external override {
        (
            address tokenAddress,
            uint16 tokenChain,
            uint256 amount,
            address to,
            uint16 toChain
        ) = abi.decode(payload, (address, uint16, uint256, address, uint16));
         //TODO handle your business
    }
}

Contract Addresses

Mainnet

  • Ethereum
    • chainId:2
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Binance Smart Chain
    • chainId:3
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Polygon
    • chainId:4
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Avalanche
    • chainId:5
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Fantom
    • chainId:6
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Optimistic
    • chainId:7
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Arbitrum One
    • chainId:8
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Moonbeam
    • chainId:9
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Gnosis
    • chainId:12
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Metis
    • chainId:13
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Arbitrum Nova
    • chainId:14
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • CoreDao
    • chainId:17
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Celo
    • chainId:18
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Mantle
    • chainId:20
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Base
    • chainId:22
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • OPBNB
    • chainId:23
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC
  • Manta
    • chainId:24
    • Address:0xb20F0105f3598652a3bE569132F7b3F341106dDC

EVM (Solidity) Interfaces

IZKBridge.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IZKBridge {
    
    // @notice send a zkBridge message to the specified address at a zkBridge endpoint.
    // @param dstChainId - the destination chain identifier
    // @param dstAddress - the address on destination chain
    // @param payload - a custom bytes payload to send to the destination contract
    function send(uint16 dstChainId, address dstAddress, bytes memory payload) external payable returns (uint64 nonce);

    // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
    // @param dstChainId - the destination chain identifier
    function estimateFee(uint16 dstChainId) external view returns (uint256 fee);
}

IZKBridgeReceiver.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

interface IZKBridgeReceiver {
    // @notice zkBridge endpoint will invoke this function to deliver the message on the destination
    // @param srcChainId - the source endpoint identifier
    // @param srcAddress - the source sending contract address from the source chain
    // @param nonce - the ordered message nonce
    // @param payload - a custom bytes payload from send chain
    function zkReceive(uint16 srcChainId, address srcAddress, uint64 nonce, bytes calldata payload) external;
}
@Bhavik-punmiya
Copy link

Bhavik-punmiya commented Feb 3, 2024

what are the contract addresses for bsc testnent and opbnb testnet ? , I need your help in implemention of something..

Computation on the L1 Blockchain is excessively costly. opBNB provides a much more cost-effective platform for conducting such computations. Your objective is to delegate the computation from BSC to opBNB and then transfer the final result back to BSC via the Polyhedra Bridge. It's worth noting that you can assume the client will request this computation every 10 seconds, offering the opportunity to batch transfer computation results to further reduce the cost.

What I did here is implmented both interface in both smartcontract, and once resul is computed i tried to send it back to the orignal chain, can you let me know where I am doing suff wrong. @niconiconi

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IZKBridgeReceiver.sol";
import "./IZKBridge.sol";

contract BscContract is IZKBridgeReceiver,IZKBridge{
    IZKBridge zkBridge;
    address zkBridgeAddress;
    address owner;

    uint256 num1; // First number
    uint256 num2; // Second number
    uint256 result;
    bytes payload = abi.encode(num1, num2);


    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    constructor(address _zkBridgeAddress) {
        zkBridgeAddress = _zkBridgeAddress;
        zkBridge = IZKBridge(_zkBridgeAddress);
        owner = msg.sender;
    }
    function calculate(uint16 dstChainId,address dstAddress, uint256 num1, uint256 num2) public {
        bytes memory payload = abi.encode(num1, num2);
        this.send(dstChainId, dstAddress, payload);
    }

    function send(uint16 dstChainId, address dstAddress, bytes memory payload) external payable onlyOwner returns (uint64 nonce){
        nonce = zkBridge.send{value:msg.value}(dstChainId, dstAddress, payload);
        emit ResultReceived(payload);
        return nonce;
    }

    function estimateFee(uint16 dstChainId) external view returns (uint256 fee){
        fee = zkBridge.estimateFee(dstChainId);
        return fee;
    }

    function zkReceive(uint16 srcChainId, address srcAddress, uint64 nonce, bytes calldata payload) external override {
        // Decode the result from the payload
        result = abi.decode(payload, (uint256));
        emit ResultReceived(payload);
    }

    // Getter function to retrieve the result
    function getResult() public view returns (uint256) {
        return result;
    }
    // Event for logging purposes
    event ResultReceived(bytes resultPayload);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IZKBridgeReceiver.sol";
import "./IZKBridge.sol";

contract OpBNBContract is IZKBridgeReceiver,IZKBridge{
    IZKBridge zkBridge;
    address zkBridgeAddress;
    address owner;

    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }

    constructor(address _zkBridgeAddress) {
        zkBridgeAddress = _zkBridgeAddress;
        zkBridge = IZKBridge(_zkBridgeAddress);
        owner = msg.sender;
    }

    function zkReceive(uint16 srcChainId, address srcAddress, uint64 nonce, bytes calldata payload) external override onlyOwner {
    // Decode the numbers from the payload
       (uint256 num1, uint256 num2) = abi.decode(payload, (uint256, uint256));

    // Perform the multiplication
        uint256 result = num1 * num2;

    // Encode the result into the payload
        bytes memory resultPayload = abi.encode(result);

        // Call the send function to send the result back to BSC
        this.send(srcChainId, srcAddress, resultPayload);
    }
    function send(uint16 dstChainId, address dstAddress, bytes memory payload) external payable returns (uint64 nonce){
        nonce = zkBridge.send{value:msg.value}(dstChainId, dstAddress, payload);
        return nonce;
    }

    // @notice gets a quote in source native gas, for the amount that send() requires to pay for message delivery
    // @param dstChainId - the destination chain identifier
    function estimateFee(uint16 dstChainId) external view returns (uint256 fee){
        fee = zkBridge.estimateFee(dstChainId);
        return fee;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment