Build
Tutorials
Localnet

Localnet is a local development environment that simplifies the development of universal apps.

Localnet:

  • Starts an Anvil (opens in a new tab) local testnet node
  • Deploys protocol contracts (opens in a new tab) on the local testnet node. Both EVM gateway and ZetaChain gateway are deployed and running on the same local blockchain Simulates the real-world testnet environment of ZetaChain by observing events and relaying the contract calls between EVM gateway and ZetaChain gateway

Clone the example contracts repo and install dependencies:

git clone https://github.com/zeta-chain/example-contracts
cd universal/hello
yarn

Localnet is installed from the @zetachain/localnet package. If you need to update localnet just run yarn add --dev @zetachain/localnet. The template ships with the latest version of localnet.

Start the localnet:

yarn hardhat localnet

Once the localnet is started you will see the standard Anvil output with a list of accounts, private keys as well as the output from protocol contracts being deployed. After the localnet is set up you will see a list of protocol contract addresses:

EVM Contract Addresses
======================

Gateway EVM: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
ZETA:        0x5FbDB2315678afecb367f032d93F642f64180aa3

ZetaChain Contract Addresses
============================

Gateway ZetaChain: 0x610178dA211FEF7D417bC0e6FeD39F05609AD788
ZETA:              0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
ZRC-20 ETH:        0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c

Keep the terminal window with localnet running open and open a new terminal.

Compile the contracts and deploy both a Hello universal app contract and a Receiver contract.

yarn deploy

Universal app contract address:

🚀 Successfully deployed contract on localhost.
📜 Contract address: 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82

Receiver contract:

🚀 Successfully deployed contract on localhost.
📜 Contract address: 0x9A676e781A523b5d0C0e43731313A708CB607508

Even though both contracts are deployed on the same local testnet we will think of Hello as running on ZetaChain and Receiver as running on a generic EVM chain. These two contracts are connected with each other through the gateway.

Call the depositAndCall function on the EVM gateway to call a universal app contract:

yarn hardhat deposit-and-call --contract 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 --amount 2 --message bob --network localhost --revert-address 0x9A676e781A523b5d0C0e43731313A708CB607508 --revert-message 0x --call-on-revert

The command calls the depositAndCall function on the EVM gateway.

The EVM gateway emits a "Deposited" event acknowledging that the function call has been successful:

[EVM]: Gateway: 'Deposited' event emitted

Localnet picks up the "Deposited" event and calls the ZetaChain's gateway execute function, which calls the universal app:

[ZetaChain]: Universal contract 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 executing onCrossChainCall (context: {"origin":"0x610178dA211FEF7D417bC0e6FeD39F05609AD788","sender":"0x735b14BB79463307AAcBED86DAf3322B1e6226aB","chainID":1}), zrc20: 0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c, amount: 2000000000000000000, message: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003626f620000000000000000000000000000000000000000000000000000000000)

The onCrossChainCall is executed and emits an event:

[ZetaChain]: Event from onCrossChainCall: {"_type":"log","address":"0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82","blockHash":"0xc8a8ebc484c5330f118a9e838587b918657ca2b347b7b76846236c00e44006bd","blockNumber":23,"data":"0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001a48656c6c6f2066726f6d206120756e6976657273616c206170700000000000000000000000000000000000000000000000000000000000000000000000000003626f620000000000000000000000000000000000000000000000000000000000","index":2,"removed":false,"topics":["0x39f8c79736fed93bca390bb3d6ff7da07482edb61cd7dafcfba496821d6ab7a3"],"transactionHash":"0x3a6612c174d980a13e3ee6b17a21c4708f0f31b823c1fba1037fc6c4124a7b68","transactionIndex":0}

Introduce a revert(); statement anywhere inside the onCrossChainCall function to force the contract to revert.

Make the same call to the EVM gateway contract:

yarn hardhat deposit-and-call --message bob --network localhost --revert-address 0x9A676e781A523b5d0C0e43731313A708CB607508 --revert-message 0x --call-on-revert --contract 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 --amount 2

The EVM gateway emits the "Deposited" event:

[EVM]: Gateway: 'Deposited' event emitted

A universal app gets called:

[ZetaChain]: Universal contract 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 executing onCrossChainCall (context: {"origin":"0x610178dA211FEF7D417bC0e6FeD39F05609AD788","sender":"0x735b14BB79463307AAcBED86DAf3322B1e6226aB","chainID":1}), zrc20: 0x9fd96203f7b22bCF72d9DCb40ff98302376cE09c, amount: 2000000000000000000, message: 0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003626f620000000000000000000000000000000000000000000000000000000000)

Now instead of emitting an event from a universal app we see an error caused by the revert(); statement we introduced earlier:

[ZetaChain]: Error executing onCrossChainCall: Error: transaction execution reverted (action="sendTransaction", data=null, reason=null, invocation=null, revert=null, transaction={ "data": "", "from": "0x735b14BB79463307AAcBED86DAf3322B1e6226aB", "to": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, receipt={ "_type": "TransactionReceipt", "blobGasPrice": "1", "blobGasUsed": null, "blockHash": "0x7f313eb2281d3ce784b4470c310b24b5a284cffcde76bd9b60a21b0626067bea", "blockNumber": 27, "contractAddress": null, "cumulativeGasUsed": "77284", "from": "0x735b14BB79463307AAcBED86DAf3322B1e6226aB", "gasPrice": "10000000000", "gasUsed": "77284", "hash": "0xec360d8c235799450d2e5a3ea3386deee9bb0653f26619034601d0bbeaa2095c", "index": 0, "logs": [  ], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "root": "0xceb5ff1771bf62103eb1b419ad6922afdac15c99dc20326ace7e37434badcd9c", "status": 0, "to": "0x610178dA211FEF7D417bC0e6FeD39F05609AD788" }, code=CALL_EXCEPTION, version=6.13.2)

The EVM gateway executes onRevert function of the Receiver contract:

[EVM]: Contract 0x9A676e781A523b5d0C0e43731313A708CB607508 executing onRevert (context: {"asset":"0x0000000000000000000000000000000000000000","amount":0,"revertMessage":"0x3078"})

Execute the callFromZetaChain function of the universal app:

hardhat call-from-zetachain --message bob --network localhost --revert-address 0x9A676e781A523b5d0C0e43731313A708CB607508 --revert-message "my revert message" --call-on-revert --contract 0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82 --receiver 0x9A676e781A523b5d0C0e43731313A708CB607508

The function calls the ZetaChain gateway contract. The ZetaChain gateway emits a "Called" event acknowledging that the call has been successful.

[ZetaChain]: Gateway: 'Called' event emitted

Localnet picks up the "Called" event and calls the EVM gateway execute function, which calls the Receiver contract:

[EVM]: Calling 0x9a676e781a523b5d0c0e43731313a708cb607508 with message 0xa777d0dc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003626f620000000000000000000000000000000000000000000000000000000000

The Receiver contract then emits an event:

[EVM]: Event from contract: {"_type":"log","address":"0x9A676e781A523b5d0C0e43731313A708CB607508","blockHash":"0x0deb4a3e22b73320ffad3d916c5e2b2db450dddc162feaafa84546612cebd238","blockNumber":22,"data":"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003626f620000000000000000000000000000000000000000000000000000000000","index":0,"removed":false,"topics":["0xfa051318aca3e493e16e6cf4bccc017c7e061924a42ef27b3bb373c4707b636a"],"transactionHash":"0x94c4d53aed68c97f18e467207ff6cdc1e855c65bcddab399f100fe2786ee33a8","transactionIndex":0}