diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 3b4ecdbce..f7e5d5c50 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -24,4 +24,4 @@ jobs: # in subdirectories. The former only checks sol files in the current directory and directories one level down. - name: Run Lint run: | - ./scripts/local/lint.sh + ./scripts/lint.sh diff --git a/.gitignore b/.gitignore index 649082785..fc932e387 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,6 @@ docker-compose-run-local.yml main.log server.log tests.test + +# Forge documentation +contracts/docs/ diff --git a/README.md b/README.md index 54d6e7477..e6bd9f52d 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,26 @@ -# Overview +## Overview Teleporter is an EVM compatible cross-subnet communication protocol built on top of [Avalanche Warp Messaging (AWM)](https://docs.avax.network/learn/avalanche/awm), and implemented as a Solidity smart contract. It provides a mechanism to asynchronously invoke smart contract functions on other EVM blockchains within Avalanche. Teleporter provides a handful of useful features on top of AWM, such as specifying relayer incentives for message delivery, replay protection, message delivery and execution retries, and a standard interface for sending and receiving messages within a dApp deployed across multiple subnets. It's important to understand the distinction between Avalanche Warp Messaging and Teleporter. AWM allows subnets to communicate with each other via authenticated messages by providing signing and verification primitives in Avalanchego. These are used by the blockchain VMs to sign outgoing messages and verify incoming messages. -The Teleporter protocol, on the other hand, is implemented at the smart contract level, and is a user-friendly interface to AWM, aimed at dApp developers. All of the message signing and verification is abstracted away from developers. Instead, developers simply call `sendCrossChainMessage` on the `TeleporterMessenger` contract to send a message invoking a smart contract on another subnet, and implement the `ITeleporterReceiver` interface to receive messages on the destination subnet. Teleporter handles all of the message signing and verification, as well as the message delivery and execution. - -**Note:** Teleporter and Avalanche Warp Messaging are under active development and may cease to function at any time. This repository is intended to be used for testing and development purposes only and should **not** be used in production. +The Teleporter protocol, on the other hand, is implemented at the smart contract level, and is a user-friendly interface to AWM, aimed at dApp developers. All of the message signing and verification is abstracted away from developers. Instead, developers simply call `sendCrossChainMessage` on the `TeleporterMessenger` contract to send a message invoking a smart contract on another subnet, and implement the `ITeleporterReceiver` interface to receive messages on the destination subnet. Teleporter handles all of the Warp message construction and sending, as well as the message delivery and execution. - [Overview](#overview) - - [Setup](#setup) - - [Docker Setup](#docker-setup) - - [General Setup](#general-setup) - - [Structure](#structure) - - [Build + Run + Test](#build--run--test) - - [Run tests on Fuji Testnet](#run-tests-on-fuji-testnet) - - [E2E tests](#e2e-tests) - - [Docs](#docs) - - [Resources](#resources) +- [Setup](#setup) + - [Docker Setup](#docker-setup) + - [General Setup](#general-setup) +- [Structure](#structure) +- [Build + Run + Test](#build--run--test) + - [Run tests on Fuji Testnet](#run-tests-on-fuji-testnet) + - [E2E tests](#e2e-tests) +- [Docs](#docs) +- [Resources](#resources) ## Setup + ### Docker Setup + - Install Docker: ``` @@ -57,32 +57,41 @@ docker run hello-world # This should work without sudo now - Note that as you develop and continuously build Docker images, it will eat up continuously more disk space. Periodically you'll want to remedy this be removing everything Docker has built: `docker system prune --all --volumes --force` ### General Setup + The above steps are sufficient to run the included integration tests inside Docker containers. If you wish to run them outside docker, you'll need to install the following dependencies: - [Foundry](https://book.getfoundry.sh/getting-started/installation) - [Python3](https://www.python.org/downloads/) ## Structure +- `contracts/` is a [Foundry](https://github.com/foundry-rs/foundry) project that includes the implementation of the `TeleporterMessenger` contract and example dApps that demonstrate how to write contracts that interact with Teleporter. +- `abi-bindings/` includes Go ABI bindings for the contracts in `contracts/`. +- `tests/` includes integration tests for the contracts in `contracts/`, written using the [Ginkgo](https://onsi.github.io/ginkgo/) testing framework. +- `utils/` includes Go utility functions for interacting with the contracts in `contracts/`. Included are Golang scripts to derive the expected EVM contract address deployed from a given EOA at a specific nonce, and also construct a transaction to deploy provided byte code to the same address on any EVM chain using [Nick's method](https://yamenmerhi.medium.com/nicks-method-ethereum-keyless-execution-168a6659479c#). - `subnet-evm/` is the public subnet-evm repository (included as a submodule) checked out on the `warp-contract` branch with our changes. -- `contract-deployment/` includes Golang scripts to derive the expected EVM contract address deployed from a given EOA at a specific nonce, and also construct a transaction to deploy provided byte code to the same address on any EVM chain using [Nick's method](https://yamenmerhi.medium.com/nicks-method-ethereum-keyless-execution-168a6659479c#). -- `contracts/` is a [foundry](https://github.com/foundry-rs/foundry) project that includes the implementation of the `TeleporterMessenger` contract and other dApps that serve as examples for how to write contracts that interact with Teleporter. -- `scripts/` includes scripts to run Teleporter and integration tests. - - Note that all the scripts should be run from the root of the repository. - - `scripts/local/` includes scripts to run Teleporter in Docker containers and tests in the `scripts/local/integration-tests` locally. +- `scripts/` includes bash scripts for interacting with Teleporter in various environments, as well as utility scripts. + - `abi_bindings.sh` generates ABI bindings for the contracts in `contracts/` and outputs them to `abi-bindings/`. + - `lint.sh` lints the contracts in `contracts/`. + - `scripts/local/` includes scripts for running Teleporter in Docker containers and for running the tests in the `scripts/local/integration-tests` locally. - `scripts/local/integration-tests/` includes integration test scripts written in bash. The scripts use `foundry` to deploy smart contracts that use Teleporter, send transactions to interact with the contracts, and check that cross-chain messages have the expected effect on destination chains. - - `scripts/fuji/` includes scripts to interact with a live Teleporter deployment on Fuji subnets. - - `scripts/fuji/example-workflows/` includes example workflows that send transactions to interact with Teleporter contracts on Fuji subnets. -- `docker/` includes containerized setup for running a local setup of Teleporter, as well as a script to run each of the integration tests against the local network. + - *Note* These tests will be deprecated in favor of the end to end tests in `tests/`, written using the [Ginkgo](https://onsi.github.io/ginkgo/) testing framework. + - `scripts/fuji/` includes scripts to interact with a live Teleporter deployment on Fuji subnets. + - `scripts/fuji/example-workflows/` includes example workflows that send transactions to interact with Teleporter contracts on [Fuji](https://docs.avax.network/learn/avalanche/fuji) subnets. +- `docker/` includes configurations for a local, containerized setup of Teleporter. -## Build + Run + Test +## Run the Docker integration tests - Get all submodules: `git submodule update --init --recursive` - Install Docker as described in the setup section of the README in the root of this repository. -- If we are using a local version of the `awm-relayer` image, build it using `./scripts/build_local_image.sh` from the root of `awm-relayer` repository. +- If we are using a local version of the `awm-relayer` image, build it using `./scripts/build_local_image.sh` from the root of the `awm-relayer` repository. + +### Start up the local testnet + - Run `./scripts/local/run.sh` to run the local testnet in Docker containers with the ability to interact with the nodes directly. - `./scripts/local/run.sh` usage is as follows: ``` -l, --local-relayer-image Use a local AWM Relayer image instead of pulling from dockerhub + -p, --pause Pause the network on stop. Will attempt to restart the paused network on subsequent runs -h, --help Print this help message ``` - Note that if `-l, --local` is not set, then the latest published `awm-relayer` image will be pulled from Dockerhub. @@ -93,11 +102,11 @@ cast send --private-key 0x56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5c cast balance --rpc-url http://127.0.0.1:9652/ext/bc/C/rpc 0x333d17d3b42bf7930dbc6e852ca7bcf560a69003 ``` -- After calling `./scripts/local/run.sh`, you can directly send messages between the deployed subnets (in comparison, calling `./scripts/local/test.sh` runs the same setup steps, then runs the tests in `./scripts/integration-tests/` automatically). As an example, `./scripts/integration-tests/basic_send_receive.sh` can be run manually like so: +- After calling `./scripts/local/run.sh`, you can directly send messages between the deployed subnets. As an example, `./scripts/integration-tests/basic_send_receive.sh` can be run manually like so: ``` # Open a shell in the container -docker exec -it relayer_run /bin/bash +docker exec -it local_network_run /bin/bash # In the container: set -a # export all variables so child processes can access source vars.sh @@ -112,6 +121,7 @@ source vars.sh -t, --test Run a specific test. If empty, runs all tests in the ./scripts/local/integration-tests/ -t, --test "test1 test2" Run multiple tests. Test names must be space delimited and enclosed in quotes -l, --local-relayer-image Use a local AWM Relayer image instead of pulling from dockerhub + -p, --pause Pause the network on stop. Will attempt to restart the paused network on subsequent runs -h, --help Print this help message ``` - Note that if `-l, --local` is not set, then the latest published `awm-relayer` image will be pulled from Dockerhub. @@ -121,13 +131,21 @@ source vars.sh ./run_stop.sh # stop the running containers and preserve the network for subsequent runs ./run_stop.sh -c # stop the running containers and clean the network ``` -- Additional Notes: - - Both the `./scripts/local/run.sh` and `./scripts/local/test.sh` scripts run local five node networks, with each of the nodes validating the primary network and three subnets (Subnet A, Subnet B, and Subnet C). - - Logs from the subnets on one of the five nodes are printed to stdout when run using either script. - - These logs can also be found at `~/.avalanche-cli/runs/network-runner-root-data__/node{1,5]/logs/.log`, or at `/var/lib/docker/overlay2//merged/root/.avalanche-cli/....` on your local machine. You will need to be the root user to access the logs under `/var/lib`. + +### Run the integration tests in Docker containers + +- Run `./scripts/local/test.sh` to run the integration tests in Docker containers. See the section above for usage. +- This script performs the same setup steps as `scripts/local/run.sh` (described above), and then runs the tests in `./scripts/integration-tests/` automatically + +### Additional notes + +- Both the `./scripts/local/run.sh` and `./scripts/local/test.sh` scripts run five local network nodes, with each of the nodes validating the primary network and three subnets (Subnet A, Subnet B, and Subnet C). +- Logs from the subnets on one of the five nodes are printed to stdout when run using either script. +- These logs can also be found at `~/.avalanche-cli/runs/network-runner-root-data__/node{1,5]/logs/.log` in the `local_network_run` container, or at `/var/lib/docker/overlay2//merged/root/.avalanche-cli/....` on your local machine. You will need to be the root user to access the logs under `/var/lib`. ### Run tests on Fuji Testnet -The following steps will allow you to run integration tests and example workflows against three Fuji subnets that have AWM enabled. These workflows send transaction on each of these subnets, so you will need to fund your account with some native tokens to pay for gas fees. The three subnets are called [Amplify](https://subnets-test.avax.network/amplify), [Bulletin](https://subnets-test.avax.network/bulletin), and [Conduit](https://subnets-test.avax.network/conduit). + +The following steps will allow you to run the integration tests described above and example workflows against three Fuji subnets that have AWM enabled. These workflows send transaction on each of these subnets, so you will need to fund your account with some native tokens to pay for gas fees. The three subnets are called [Amplify](https://subnets-test.avax.network/amplify), [Bulletin](https://subnets-test.avax.network/bulletin), and [Conduit](https://subnets-test.avax.network/conduit). The configuration for all the subnets is in `.env.testnet`. If needed, you can change the subnet IDs, the chain IDs, urls and teleporter contract address to match your setup. @@ -160,16 +178,16 @@ source .env.testnet source .env ``` -### E2E tests +## E2E tests -E2E tests are ran as part of CI, but can also be ran locally with the `--local` flag. To run the E2E tests locally, you'll need to install Gingko following the intructions [here](https://onsi.github.io/ginkgo/#installing-ginkgo) +E2E tests are run as part of CI, but can also be run locally with the `--local` flag. To run the E2E tests locally, you'll need to install Gingko following the instructions [here](https://onsi.github.io/ginkgo/#installing-ginkgo) Next, provide the path to the `subnet-evm` repository and the path to a writeable data directory (here we use the `subnet-evm` submodule and `~/tmp/e2e-test`) to use for the tests: ```bash ./scripts/local/e2e_test.sh --local --subnet-evm ./subnet-evm --data-dir ~/tmp/e2e-test ``` -### ABI Bindings +## ABI Bindings To generate Golang ABI bindings for the Solidity smart contracts, run: ```bash diff --git a/abi-bindings/go/Teleporter/TeleporterMessenger/TeleporterMessenger.go b/abi-bindings/go/Teleporter/TeleporterMessenger/TeleporterMessenger.go index 296b9de97..ee12e4e70 100644 --- a/abi-bindings/go/Teleporter/TeleporterMessenger/TeleporterMessenger.go +++ b/abi-bindings/go/Teleporter/TeleporterMessenger/TeleporterMessenger.go @@ -64,7 +64,7 @@ type TeleporterMessageReceipt struct { // TeleporterMessengerMetaData contains all meta data concerning the TeleporterMessenger contract. var TeleporterMessengerMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"updatedFeeInfo\",\"type\":\"tuple\"}],\"name\":\"AddFeeAmount\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"MessageExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"MessageExecutionFailed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"deliverer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rewardRedeemer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"ReceiveCrossChainMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"RelayerRewardsRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"}],\"name\":\"SendCrossChainMessage\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MINIMUM_REQUIRED_CALL_DATA_LENGTH\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"REQUIRED_ORIGIN_CHAIN_ID_START_INDEX\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"WARP_MESSENGER\",\"outputs\":[{\"internalType\":\"contractWarpMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"feeContractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"additionalFeeAmount\",\"type\":\"uint256\"}],\"name\":\"addFeeAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockchainID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delivererAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayers\",\"type\":\"address[]\"}],\"name\":\"checkIsAllowedRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"}],\"name\":\"checkRelayerRewardAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getFeeInfo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getMessageHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"}],\"name\":\"getNextMessageID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getReceiptAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"}],\"name\":\"getReceiptQueueSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getRelayerRewardAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestMessageIDs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"messageReceived\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"delivered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"outstandingReceipts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"first\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"last\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"messageIndex\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"name\":\"receiveCrossChainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"receivedFailedMessageHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"}],\"name\":\"redeemRelayerRewards\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"relayerRewardAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"relayerRewardAmounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"retryMessageExecution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"retrySendCrossChainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessageInput\",\"name\":\"messageInput\",\"type\":\"tuple\"}],\"name\":\"sendCrossChainMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"messageIDs\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"}],\"name\":\"sendSpecifiedReceipts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"sentMessageInfo\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"updatedFeeInfo\",\"type\":\"tuple\"}],\"name\":\"AddFeeAmount\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"MessageExecuted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"MessageExecutionFailed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"deliverer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"rewardRedeemer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"ReceiveCrossChainMessage\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"redeemer\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"RelayerRewardsRedeemed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"indexed\":false,\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"}],\"name\":\"SendCrossChainMessage\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"WARP_MESSENGER\",\"outputs\":[{\"internalType\":\"contractWarpMessenger\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"feeContractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"additionalFeeAmount\",\"type\":\"uint256\"}],\"name\":\"addFeeAmount\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"blockchainID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delivererAddress\",\"type\":\"address\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayers\",\"type\":\"address[]\"}],\"name\":\"checkIsAllowedRelayer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"relayer\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"}],\"name\":\"checkRelayerRewardAmount\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getFeeInfo\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"feeAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getMessageHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"}],\"name\":\"getNextMessageID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getReceiptAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"chainID\",\"type\":\"bytes32\"}],\"name\":\"getReceiptQueueSize\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"getRelayerRewardAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"latestMessageIDs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"name\":\"messageReceived\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"delivered\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"outstandingReceipts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"first\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"last\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"messageIndex\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"name\":\"receiveCrossChainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"receivedFailedMessageHashes\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"feeAsset\",\"type\":\"address\"}],\"name\":\"redeemRelayerRewards\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"relayerRewardAddresses\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"relayerRewardAmounts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"retryMessageExecution\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"senderAddress\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"receivedMessageID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"relayerRewardAddress\",\"type\":\"address\"}],\"internalType\":\"structTeleporterMessageReceipt[]\",\"name\":\"receipts\",\"type\":\"tuple[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessage\",\"name\":\"message\",\"type\":\"tuple\"}],\"name\":\"retrySendCrossChainMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"destinationChainID\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"destinationAddress\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"requiredGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTeleporterMessageInput\",\"name\":\"messageInput\",\"type\":\"tuple\"}],\"name\":\"sendCrossChainMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"originChainID\",\"type\":\"bytes32\"},{\"internalType\":\"uint256[]\",\"name\":\"messageIDs\",\"type\":\"uint256[]\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"},{\"internalType\":\"address[]\",\"name\":\"allowedRelayerAddresses\",\"type\":\"address[]\"}],\"name\":\"sendSpecifiedReceipts\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"messageID\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"sentMessageInfo\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"messageHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"contractAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"structTeleporterFeeInfo\",\"name\":\"feeInfo\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // TeleporterMessengerABI is the input ABI used to generate the binding from. @@ -213,68 +213,6 @@ func (_TeleporterMessenger *TeleporterMessengerTransactorRaw) Transact(opts *bin return _TeleporterMessenger.Contract.contract.Transact(opts, method, params...) } -// MINIMUMREQUIREDCALLDATALENGTH is a free data retrieval call binding the contract method 0x8f12376f. -// -// Solidity: function MINIMUM_REQUIRED_CALL_DATA_LENGTH() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerCaller) MINIMUMREQUIREDCALLDATALENGTH(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _TeleporterMessenger.contract.Call(opts, &out, "MINIMUM_REQUIRED_CALL_DATA_LENGTH") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// MINIMUMREQUIREDCALLDATALENGTH is a free data retrieval call binding the contract method 0x8f12376f. -// -// Solidity: function MINIMUM_REQUIRED_CALL_DATA_LENGTH() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerSession) MINIMUMREQUIREDCALLDATALENGTH() (*big.Int, error) { - return _TeleporterMessenger.Contract.MINIMUMREQUIREDCALLDATALENGTH(&_TeleporterMessenger.CallOpts) -} - -// MINIMUMREQUIREDCALLDATALENGTH is a free data retrieval call binding the contract method 0x8f12376f. -// -// Solidity: function MINIMUM_REQUIRED_CALL_DATA_LENGTH() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerCallerSession) MINIMUMREQUIREDCALLDATALENGTH() (*big.Int, error) { - return _TeleporterMessenger.Contract.MINIMUMREQUIREDCALLDATALENGTH(&_TeleporterMessenger.CallOpts) -} - -// REQUIREDORIGINCHAINIDSTARTINDEX is a free data retrieval call binding the contract method 0x5bf91119. -// -// Solidity: function REQUIRED_ORIGIN_CHAIN_ID_START_INDEX() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerCaller) REQUIREDORIGINCHAINIDSTARTINDEX(opts *bind.CallOpts) (*big.Int, error) { - var out []interface{} - err := _TeleporterMessenger.contract.Call(opts, &out, "REQUIRED_ORIGIN_CHAIN_ID_START_INDEX") - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// REQUIREDORIGINCHAINIDSTARTINDEX is a free data retrieval call binding the contract method 0x5bf91119. -// -// Solidity: function REQUIRED_ORIGIN_CHAIN_ID_START_INDEX() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerSession) REQUIREDORIGINCHAINIDSTARTINDEX() (*big.Int, error) { - return _TeleporterMessenger.Contract.REQUIREDORIGINCHAINIDSTARTINDEX(&_TeleporterMessenger.CallOpts) -} - -// REQUIREDORIGINCHAINIDSTARTINDEX is a free data retrieval call binding the contract method 0x5bf91119. -// -// Solidity: function REQUIRED_ORIGIN_CHAIN_ID_START_INDEX() view returns(uint256) -func (_TeleporterMessenger *TeleporterMessengerCallerSession) REQUIREDORIGINCHAINIDSTARTINDEX() (*big.Int, error) { - return _TeleporterMessenger.Contract.REQUIREDORIGINCHAINIDSTARTINDEX(&_TeleporterMessenger.CallOpts) -} - // WARPMESSENGER is a free data retrieval call binding the contract method 0xb771b3bc. // // Solidity: function WARP_MESSENGER() view returns(address) diff --git a/contracts/README.md b/contracts/README.md index 334a47125..57d3dd8ed 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -7,3 +7,6 @@ This directory is set up as a [Foundry](https://github.com/foundry-rs/foundry) p - To compile the contracts run `forge build` from this directory. - Similarly, to run unit tests, run `forge test`. - See additional testing and deployment options [here](https://book.getfoundry.sh/forge/). + +## Generate documentation +- Documentation can be generated by running `forge doc --build` from this repository. By default, this will generate documentation to `contracts/docs/`, and an HTML book to `contracts/docs/book/`. It's also possible to serve this book locally by running `forge doc --serve `. \ No newline at end of file diff --git a/contracts/src/CrossChainApplications/ERC20Bridge/ERC20Bridge.sol b/contracts/src/CrossChainApplications/ERC20Bridge/ERC20Bridge.sol index 7111261f4..ba8cf838f 100644 --- a/contracts/src/CrossChainApplications/ERC20Bridge/ERC20Bridge.sol +++ b/contracts/src/CrossChainApplications/ERC20Bridge/ERC20Bridge.sol @@ -71,7 +71,7 @@ contract ERC20Bridge is uint256 public constant TRANSFER_BRIDGE_TOKENS_REQUIRED_GAS = 300_000; /** - * @dev Initializes the Teleporter messenger used for sending and receiving messages, + * @dev Initializes the Teleporter Messenger used for sending and receiving messages, * and initializes the current chain ID. */ constructor( @@ -390,7 +390,7 @@ contract ERC20Bridge is * Emits a {CreateBridgeToken} event. * * Note: This function is only called within `receiveTeleporterMessage`, which can only be - * called by the Teleporter messenger. + * called by the Teleporter Messenger. */ function _createBridgeToken( bytes32 nativeChainID, @@ -438,7 +438,7 @@ contract ERC20Bridge is * Emits a {MintBridgeTokens} event. * * Note: This function is only called within `receiveTeleporterMessage`, which can only be - * called by the Teleporter messenger. + * called by the Teleporter Messenger. */ function _mintBridgeTokens( bytes32 nativeChainID, @@ -472,7 +472,7 @@ contract ERC20Bridge is * and optionally routing them to a different third chain. * * Note: This function is only called within `receiveTeleporterMessage`, which can only be - * called by the Teleporter messenger. + * called by the Teleporter Messenger. */ function _transferBridgeTokens( bytes32 sourceChainID, @@ -571,7 +571,7 @@ contract ERC20Bridge is ITeleporterMessenger teleporterMessenger = teleporterRegistry .getLatestTeleporter(); - // Allow the Teleporter messenger to spend the fee amount. + // Allow the Teleporter Messenger to spend the fee amount. if (feeAmount > 0) { IERC20(nativeContractAddress).safeIncreaseAllowance( address(teleporterMessenger), @@ -630,7 +630,7 @@ contract ERC20Bridge is .getLatestTeleporter(); // If necessary, transfer the primary fee amount to this contract and approve the - // Teleporter messenger to spend it when the first message back to the native subnet + // Teleporter Messenger to spend it when the first message back to the native subnet // is submitted. The secondary fee amount is then handled by the native subnet when // submitting a message to the destination chain, if applicable. uint256 adjustedPrimaryFeeAmount = 0; diff --git a/contracts/src/CrossChainApplications/ERC20Bridge/README.md b/contracts/src/CrossChainApplications/ERC20Bridge/README.md index 8f37bd658..584e3b44d 100644 --- a/contracts/src/CrossChainApplications/ERC20Bridge/README.md +++ b/contracts/src/CrossChainApplications/ERC20Bridge/README.md @@ -13,7 +13,7 @@ The generic ERC20 bridge is implemented using two primary contracts. - Bridge contract that uses Teleporter to facilitate the bridging operations of adding new supported tokens and moving tokens between chains. - The bridge contract tracks the balances of each token sent to other bridge instances, and only allows those bridge instances to redeem up to the amount of tokens that has been previously sent to them. - Primary functions include: - - `submitCreateBridgeToken`: Called on the origin subnet to add support for an ERC20 native to the chain to a bridge instance on a different chain. Submits a Teleporter message that invokes `createBridgeToken` on the destination chain. + - `submitCreateBridgeToken`: Called on the origin chain to add support for an ERC20 on a different chain's bridge instance. Submits a Teleporter message that invokes `createBridgeToken` on the destination chain. - `createBridgeToken`: Called by cross-chain messages to add support for a new bridge token from another chain. Assuming the token is not already supported, deploys a new `BridgeToken` contract instance to represent the tokens on this chain. - `bridgeTokens`: Called to move supported tokens from one chain to another. This includes both "wrapping" of tokens native to the chain and also "unwrapping" of bridge tokens on the chain to other chains. In the case of unwrapping or moving of a bridge token, if the destination chain ID is the native chain for the given token, the original token is sent to the recipient on the destination. If the destination chain ID is another non-native chain for the underlying token, a Teleporter message is first sent to the native chain, which then subsequently updates its accounting of balances and sends a Teleporter message on the destination chain to re-wrap the given tokens. This multi-hop flow is illustrated below. - `mintBridgeTokens`: Called by cross-chain messages in the event of new bridge transfers of tokens from their native chains. @@ -25,7 +25,7 @@ The generic ERC20 bridge is implemented using two primary contracts. ## Integration Test -An integration test demostrating the use of the generic ERC20 token bridge contracts can be found from the root of this repository in `integration-tests/erc20_bridge_multihop.sh`. This test implements the following flow and checks that each step is successful: +An integration test demostrating the use of the generic ERC20 token bridge contracts can be found from the root of this repository in `scripts/local/integration-tests/erc20_bridge_multihop.sh`. This test implements the following flow and checks that each step is successful: 1. An example "native" ERC20 token is deployed on subnet A 1. The `ERC20Bridge` contract is deployed on subnets A, B, and C. 1. Teleporter messages are sent from subnet A to B and subnet A to C to add the example ERC20 deployed in step 1 to the bridge instances on the other chains. diff --git a/contracts/src/CrossChainApplications/README.md b/contracts/src/CrossChainApplications/README.md index b6f040df6..e9cfa8ac1 100644 --- a/contracts/src/CrossChainApplications/README.md +++ b/contracts/src/CrossChainApplications/README.md @@ -1,6 +1,6 @@ # Teleporter Cross Chain Applications -This directory includes cross chain applications that are built on top of the [Teleporter Messenger Protocol](../Teleporter/README.md). +This directory includes cross-chain applications that are built on top of the [Teleporter protocol](../Teleporter/README.md). - [Teleporter Cross Chain Applications](#teleporter-cross-chain-applications) - [Applications](#applications) @@ -18,18 +18,13 @@ This directory includes cross chain applications that are built on top of the [T - `ExampleMessenger` a simple cross chain messenger that demonstrates Teleporter application sending arbitrary string data. - `VerifiedBlockHash` publishes the latest block hash of one chain to a destination chain that receives the hash and verifies the sender. Includes `BlockHashPublisher` and `BlockHashReceiver`. More details found [here](./VerifiedBlockHash/README.md) -## Get Started +## Getting started with an example application -We are going to walk through how to build a cross chain application on top of the Teleporter Messenger protocol, creating an `ExampleCrossChainMessenger` that sends arbitrary string data cross chain. +This section walks through how to build a cross-chain application on top of the Teleporter protocol, recreating the `ExampleCrossChainMessenger` contract that sends arbitrary string data from one chain to another. ### Step 1: Create Initial Contract -Create a new file called `ExampleCrossChainMessenger.sol` in the directory that will hold the application. In our case, under [ExampleMessenger](./ExampleMessenger/). - -```bash -cd -touch ExampleCrossChainMessenger.sol -``` +Create a new file called `MyExampleCrossChainMessenger.sol` in the directory that will hold the application. At the top of the file define the Solidity version to work with, and import the `ITeleporterMessenger` and `ITeleporterReceiver` interfaces. @@ -39,17 +34,17 @@ pragma solidity 0.8.18; import "../../Teleporter/ITeleporterMessenger.sol"; ``` -Next we are going to define the initial empty contract. +Next, define the initial empty contract. ```solidity -contract ExampleCrossChainMessenger {} +contract MyExampleCrossChainMessenger {} ``` ### Step 2: Integrating Teleporter Messenger -Now that we have our initial empty `ExampleCrossChainMessenger`, it's time to add our `ITeleporterMessenger` that will provide the functionality to deliver cross chain messages. +Now that the initial empty `MyExampleCrossChainMessenger` is defined, it's time to integrate the `ITeleporterMessenger` that will provide the functionality to deliver cross chain messages. -Create a state variable of `ITeleporterMessenger` type called `teleporterMessenger`. Then we'll create a constructor for our contract that takes in an address where the Teleporter messenger would be deployed on this chain, and set our state variable with it. +Create a state variable of `ITeleporterMessenger` type called `teleporterMessenger`. Then create a constructor for our contract that takes in an address where the Teleporter Messenger would be deployed on this chain, and set our state variable with it. ```solidity contract ExampleCrossChainMessenger { @@ -63,9 +58,9 @@ contract ExampleCrossChainMessenger { ### Step 3: Send and Receive -Now that our `ExampleCrossChainMessenger` has an instantiation of `ITeleporterMessenger`, we can add in functionality of sending and receiving arbitrary string data between chains. +Now that `MyExampleCrossChainMessenger` has an instantiation of `ITeleporterMessenger`, the next step is to add in functionality of sending and receiving arbitrary string data between chains. -To start, we create the function declarations for `sendMessage`, which will send string data cross chain to the specified destination address' receiver. This function allows callers to specify the destination chain ID, destination address to send to, relayer fees, required gas limit for message execution on destination address, and the actual message data. +To start, create the function declarations for `sendMessage`, which will send string data cross-chain to the specified destination address' receiver. This function allows callers to specify the destination chain ID, destination address to send to, relayer fees, required gas limit for message execution on destination address, and the actual message data. ```solidity // Send a new message to another chain. @@ -79,7 +74,7 @@ function sendMessage( ) external returns (uint256 messageID) {} ``` -We also need to implement `ITeleporterReceiver` by adding the method `receiveTeleporterMessage` that receives the cross chain messages from Teleporter. +`MyExampleCrossChainMessenger` also needs to implement `ITeleporterReceiver` by adding the method `receiveTeleporterMessage` that receives the cross-chain messages from Teleporter. ```solidity // Receive a new message from another chain. @@ -90,11 +85,7 @@ function receiveTeleporterMessage( ) external { ``` -Now that the function declarations are set up, we can move on to implementation. - -> To incentivize relayers to deliver our cross chain messages from source to destination, we will need to support `ERC20` fees. Relayer fees aren't needed when running your own relayers to deliver your own messages and pay costs out of pocket. - -Import OpenZeppelin's `IERC20` contract, then in our `sendMessage` we will check whether `feeAmount` is greater than zero. If it is, we transfer and approve the amount of IERC20 asset at `feeContractAddress` to the teleporter messenger saved as a state variable. +Now it's time to implement the methods, starting with `sendMessage`. First, import OpenZeppelin's `IERC20` contract, then in `sendMessage` check whether `feeAmount` is greater than zero. If it is, transfer and approve the amount of IERC20 asset at `feeContractAddress` to the Teleporter Messenger saved as a state variable. Relayer fees are an optional way to incentive relayers to deliver a Teleporter message to its destination. They are not strictly necessary, and may be omitted if relaying is guaranteed, such as with a self-hosted relayer. ```solidity // For non-zero fee amounts, transfer the fee into the control of this contract first, and then @@ -112,9 +103,7 @@ if (feeAmount > 0) { } ``` -Next we are going to tell the Teleporter messenger the message data to be executed when delivered to the destination address. We need to call the destination `receiveTeleporterMessage` function to receive our message, which in our case will directly store the message in the contract. - -With the above message data, as well as other inputs to form a `TeleporterMessageInput`, we now call `sendCrossChainMessage` on our Teleporter instance to start the cross chain messaging process. +Next, add the call to the `TeleporterMessenger` contract with the message data to be executed when delivered to the destination address. In `sendMessage`, form a `TeleporterMessageInput` and call `sendCrossChainMessage` on the `TeleporterMessenger` instance to start the cross chain messaging process. > `allowedRelayerAddresses` is empty in this example, meaning any relayer can try to deliver this cross chain message. Specific relayer addresses can be specified to ensure only those relayers can deliver the message. > The `message` must be ABI encoded so that it can be properly decoded on the receiving end. @@ -136,7 +125,7 @@ return ); ``` -With the sending complete, we move on to implementing the receiver. The receiver in our example will just receive the arbitrary string data, and check that the message is sent through Teleporter. +With the sending side complete, the next step is to implement `ITeleporterReceiver.receiveTeleporterMessage`. The receiver in this example will just receive the arbitrary string data, and check that the message is sent through Teleporter. ```solidity // Receive a new message from another chain. @@ -153,11 +142,11 @@ function receiveTeleporterMessage( } ``` -The base of sending and receiving messages cross chain is complete. Now, we can add functionality that saves the received messages, and allows users to query for the latest message received from a specified chain. +The base of sending and receiving messages cross chain is complete. `MyExampleCrossChainMessenger` can now be expanded with functionality that saves the received messages, and allows users to query for the latest message received from a specified chain. ### Step 4: Storing the Message -We start by defining the `struct` for how we want to save our messages. In this case, we want to just save the string message itself and the address of the sender. +Start by defining the `struct` for how to save our messages. It saves the string message itself and the address of the sender. A map will also be added where the key is the `originChainID`, and the value is the latest `message` sent from that chain. @@ -171,7 +160,7 @@ struct Message { mapping(bytes32 => Message) messages; ``` -Our previous `receiveTeleporterMessage` is updated to save the message into our mapping after we receive and verify that it's sent from Teleporter. We ABI decode the `message` bytes into a string. +Next, update `receiveTeleporterMessage` to save the message into our mapping after we receive and verify that it's sent from Teleporter. ABI decode the `message` bytes into a string. ```solidity // Receive a new message from another chain. @@ -189,7 +178,7 @@ function receiveTeleporterMessage( } ``` -Let's add a function called `getCurrentMessage` that allows users or contracts to easily query our contract for the latest message sent by a specified chain. +Next, add a function called `getCurrentMessage` that allows users or contracts to easily query our contract for the latest message sent by a specified chain. ```solidity // Check the current message from another chain. @@ -205,13 +194,13 @@ There we have it, a simple cross chain messenger built on top of Teleporter! Ful ### Step 5: Testing -For testing we have a `test.sh` script that sets up a local avalanche network with three subnets deployed with Teleporter, and a relayer to deliver Teleporter messages. To add an integration test simply add a new test script under `integration-tests`. In this case we already have our `example_messenger.sh`, which performs the following steps: +For testing, `scripts/local/test.sh` sets up a local Avalanche network with three subnets deployed with Teleporter, and a relayer to deliver Teleporter messages. To add an integration test simply add a new test script under `integration-tests`. An integration test for `ExampleCrossChainMessenger` is already included (`scripts/local/integration_tests/example_messenger.sh`), which performs the following steps: -1. Deploys our [ExampleERC20](../Mocks/ExampleERC20.sol) token to subnet A. -2. Deploy the above `ExampleCrossChainMessenger` to both subnets A and B. -3. Approve the cross chain messenger on subnet A to spend erc20 from the default address. -4. Send `"hello world"` from subnet A to subnet B's cross chain messenger to receive. -5. Call `getCurrentMessage` on subnet B to make sure the right message and sender are received. +1. Deploys the [ExampleERC20](../Mocks/ExampleERC20.sol) token to subnet A. +2. Deploys `ExampleCrossChainMessenger` to both subnets A and B. +3. Approves the cross-chain messenger on subnet A to spend ERC20 tokens from the default address. +4. Sends `"hello world"` from subnet A to subnet B's cross-chain messenger to receive. +5. Calls `getCurrentMessage` on subnet B to make sure the right message and sender are received. Running `./test.sh example_messenger` at the root of the repo should yield at the end of the test: diff --git a/contracts/src/CrossChainApplications/VerifiedBlockHash/README.md b/contracts/src/CrossChainApplications/VerifiedBlockHash/README.md index c2ed72914..7daf45f40 100644 --- a/contracts/src/CrossChainApplications/VerifiedBlockHash/README.md +++ b/contracts/src/CrossChainApplications/VerifiedBlockHash/README.md @@ -9,8 +9,8 @@ A sample cross-chain application built on top of Teleporter that publishes the b - Does not currently include fees to incentivize a relayer to deliver the cross-chain message. It is assumed the relayers will want to deliver the message even without a monetary incentive, but the contract could be easily extended to support providing message fees. - `BlockHashReceiver`: - Must be deployed on the destination chain after `BlockHashPublisher` is deployed on the origin such that the `BlockHashPublisher` contract address can be provided in the contract constructor. - - `receiveBlockHash` is the method invoked by cross-chain messages sent by the publisher contract. + - `receiveTeleporterMessage` is the method invoked by cross-chain messages sent by the publisher contract. - Anyone can view the latest block hashes received from other chains using `getLatestBlockInfo`. ## Integration Test -An integration test demostrating the use of these contracts can be found from the root of this repository in `integration-tests/block_hash_publish_receive.sh`. This test deploys the contracts on two different chains in the required order, and then checks that it can publish the block hash from one subnet to another. \ No newline at end of file +An integration test demostrating the use of these contracts can be found from the root of this repository in `integration-tests/block_hash_publish_receive.sh`. This test deploys the contracts on two different chains in the required order, and then checks that it can publish the block hash from one chain to another. \ No newline at end of file diff --git a/contracts/src/Teleporter/ITeleporterMessenger.sol b/contracts/src/Teleporter/ITeleporterMessenger.sol index c452f117a..58df1d292 100644 --- a/contracts/src/Teleporter/ITeleporterMessenger.sol +++ b/contracts/src/Teleporter/ITeleporterMessenger.sol @@ -35,11 +35,11 @@ struct TeleporterFeeInfo { } /** - * @dev Interface that describes functionalities for a cross chain messenger. + * @dev Interface that describes functionalities for a cross-chain messenger implementing the Teleporter protcol. */ interface ITeleporterMessenger { /** - * @dev Emitted when sending a Teleporter message cross chain. + * @dev Emitted when sending a Teleporter message cross-chain. */ event SendCrossChainMessage( bytes32 indexed destinationChainID, @@ -50,7 +50,7 @@ interface ITeleporterMessenger { /** * @dev Emitted when an additional fee amount is added to a Teleporter message that had previously - * been sent, but receipt not yet received. + * been sent, but not yet delivered to the destination chain. */ event AddFeeAmount( bytes32 indexed destinationChainID, @@ -59,8 +59,8 @@ interface ITeleporterMessenger { ); /** - * @dev Emitted when Teleporter message is being delivered on destination chain and address, - * but message execution fails. Failed messages can then be retried. + * @dev Emitted when a Teleporter message is being delivered on the destination chain to an address, + * but message execution fails. Failed messages can then be retried with `retryMessageExecution` */ event MessageExecutionFailed( bytes32 indexed originChainID, @@ -101,19 +101,19 @@ interface ITeleporterMessenger { ); /** - * @dev Called by transactions to initiate the sending of a cross subnet message. + * @dev Called by transactions to initiate the sending of a cross-chain message. */ function sendCrossChainMessage( TeleporterMessageInput calldata messageInput ) external returns (uint256 messageID); /** - * @dev Called by transactions to retry the sending of a cross subnet message. + * @dev Called by transactions to retry the sending of a cross-chain message. * * Retriggers the sending of a message previously emitted by sendCrossChainMessage that has not yet been acknowledged * with a receipt from the destination chain. This may be necessary in the unlikely event that less than the required * threshold of stake weight successfully inserted the message in their messages DB at the time of the first submission. - * The message is checked to have already been previously submitted by comparing it's message hash against those kept in + * The message is checked to have already been previously submitted by comparing its message hash against those kept in * state until a receipt is received for the message. */ function retrySendCrossChainMessage( @@ -123,7 +123,7 @@ interface ITeleporterMessenger { /** * @dev Adds the additional fee amount to the amount to be paid to the relayer that delivers - * the given message ID to the destination subnet. + * the given message ID to the destination chain. * * The fee contract address must be the same asset type as the fee asset specified in the original * call to sendCrossChainMessage. Returns a failure if the message doesn't exist or there is already @@ -137,7 +137,7 @@ interface ITeleporterMessenger { ) external; /** - * @dev Receives a cross chain message, and marks the `relayerRewardAddress` for fee reward for a successful delivery. + * @dev Receives a cross-chain message, and marks the `relayerRewardAddress` for fee reward for a successful delivery. * * The message specified by `messageIndex` must be provided at that index in the access list storage slots of the transaction, * and is verified in the precompile predicate. @@ -151,8 +151,10 @@ interface ITeleporterMessenger { * @dev Retries the execution of a previously delivered message by verifying the payload matches * the hash of the payload originally delivered, and calling the destination address again. * - * Intended to be used if the original required gas limit was not sufficient for the message - * execution. Messages are ensured to be successfully executed at most once. + * Intended to be used if message excution failed on initial delivery of the Teleporter message. + * For example, this may occur if the original required gas limit was not sufficient for the message + * execution, or if the destination address did not contain a contract, but a compatible contract + * was later deployed to that address. Messages are ensured to be successfully executed at most once. */ function retryMessageExecution( bytes32 originChainID, @@ -195,7 +197,7 @@ interface ITeleporterMessenger { ) external view returns (bool delivered); /** - * @dev Returns the address the relayer reward should be sent to on the origin subnet + * @dev Returns the address the relayer reward should be sent to on the origin chain * for a given message, assuming that the message has already been delivered. */ function getRelayerRewardAddress( diff --git a/contracts/src/Teleporter/README.md b/contracts/src/Teleporter/README.md index 6fcae0c7d..5841a7157 100644 --- a/contracts/src/Teleporter/README.md +++ b/contracts/src/Teleporter/README.md @@ -34,13 +34,25 @@ Teleporter provides a handful of useful properties to cross-chain applications t 5. Message execution: Teleporter enables cross-chain messages to have direct effect on their destination chain by using `evm.Call()` to invoke the `receiveTeleporterMessage` function of destination contracts that implement the `ITeleporterReceiver` interface. ## Fees -Fees can be paid on a per message basis by specifing the ERC20 asset and amount to be used to incentivize a relayer to deliver the message in the call to `sendCrossChainMessage`. The fee amount is transferred into the control of the Teleporter contract (i.e. locked) before the Warp message is sent. The Teleporter contract tracks the fee amount for each message ID it creates. When it subsquently receives a message back from the destination chain of the original message, the new message will have a list of receipts identifying the relayer that delivered the given message ID. At this point, the fee amount originally locked by Teleporter for the given message will be redeemable by the relayer identified in the receipt. If the initial fee amount was not sufficient to incentivize a relayer, it can be added to by using `AddFeeAmount`. +Fees can be paid on a per message basis by specifing the ERC20 asset and amount to be used to incentivize a relayer to deliver the message in the call to `sendCrossChainMessage`. The fee amount is transferred into the control of the Teleporter contract (i.e. locked) before the Warp message is sent. The Teleporter contract tracks the fee amount for each message ID it creates. When it subsquently receives a message back from the destination chain of the original message, the new message will have a list of receipts identifying the relayer that delivered the given message ID. At this point, the fee amount originally locked by Teleporter for the given message will be redeemable by the relayer identified in the receipt. If the initial fee amount was not sufficient to incentivize a relayer, it can be increased by calling `addFeeAmount`. + + ### Message Receipts and Fee Redemption +In order to confirm delivery of a Teleporter message from a source chain to a destination chain, a receipt is included in the next Teleporter message sent in the opposite direction, from the destination chain back to the source chain. This receipt contains the message ID of the original message, as well as the reward address that the delivering relayer specified. That reward address is then able to redeem the corresponding reward on the original chain by calling `redeemRelayerRewards`. The following example illustrates this flow: +- A Teleporter message is sent from Chain A to Chain B, with a relayer incentive of `10` `USDC`. This message is assigned the ID `1` by the Teleporter contract on Chain A. + - On Chain A, this is done by calling `sendCrossChainMessage`, and providing the `USDC` contract address and amount in the function call. +- A relayer delivers the message on Chain B by calling `receiveCrossChainMessage` and providing its address, `0x123...` +- The Teleporter contract on Chain B stores the relayer address in a receipt for the message ID. +- Some time later, a separate Teleporter message is sent from Chain B to Chain A. The Teleporter contract on Chain B includes the receipt for the original message in this new message. +- When this new message is delivered on Chain A, the Teleporter contract on Chain A reads the receipt and marks the original message (message ID `1`) as delivered by address `0x123...`. +- Address `0x123...` may now call `redeemRelayerRewards` on Chain A, which transfers the `10` `USDC` to its address. If it tries to do this before the receipt is received on Chain A, the call will fail. + +It is possible for receipts to get "stuck" on the destination chain in the event that Teleporter traffic between two chains is skewed in one direction. In such a scenario, incoming messages on one chain may cause the rate at which receipts are generated to outpace the rate at which they are sent back to the other chain. To mitigate this, the method `sendSpecifiedReceipts` can be called to immediately send the receipts associated with the given message IDs back to the original chain. ## Required Interface -Teleporter messages are delivered by calling the `receiveTeleporterMessage` function defined by the `ITeleporterReceiver` interface. Contracts must implement this interface in order to be able to receive messages. The first two paramaters of `receiveTeleporterMessage` identify the original sender of the given message on the origin chain and are set by the `TeleporterMessenger`. The third parameter to `receiveTeleporterMessage`, is the raw message payload. Applications using Teleporter are responsible for defining the exact format of this payload in a way that can be decoded on the receiving end. For example, applications may encode and action enum value along with the target method parameters on the sending side, then decode this data and route to the target method within `receiveTeleporterMessage`. See `ERC20Bridge.sol` for an example of this approach. +Teleporter messages are delivered by calling the `receiveTeleporterMessage` function defined by the `ITeleporterReceiver` interface. Contracts must implement this interface in order to be able to receive messages. The first two parameters of `receiveTeleporterMessage` identify the original sender of the given message on the origin chain and are set by the `TeleporterMessenger`. The third parameter to `receiveTeleporterMessage`, is the raw message payload. Applications using Teleporter are responsible for defining the exact format of this payload in a way that can be decoded on the receiving end. For example, applications may encode an action enum value along with the target method parameters on the sending side, then decode this data and route to the target method within `receiveTeleporterMessage`. See `ERC20Bridge.sol` for an example of this approach. ## Teleporter Contract Deployment The `TeleporterMessenger` contract must be deployed to the same contract address on every chain. This is acheived using Nick's keyless transaction method as described [here](../../../utils/contract-deployment/README.md). As a result, Warp messages sent from the resulting contract address are ensured to have the same payload format as defined by the contract itself. ## Message Delivery and Execution -Teleporter is able to ensure that messages are considered delivered even if their execution fails (i.e. reverts) by using `evm.Call()` with a pre-defined gas limit to execute the message payload. This gas limit is specified by each message in the call to `sendCrossChainMessage`. Relayers must provide at least enough gas for the sub-call in addition to the standard gas used by a call to `receiveCrossChainMessage`. In the event that a message exeuction runs out of gas or reverts for any other reason, the hash of the message payload is stored by the receiving Teleporter contract instance. This allows for the message execution to be retried in the future, with possibly a higher gas limit. Importantly, a message is still considered delivered on its destination chain even if its execution fails. This allows the relayer of the message to redeem their reward for deliverying the message, because they have no control on whether or not its execution will succeed or not so long as they provide sufficient gas to meet the specified `requiredGasLimit`. +Teleporter is able to ensure that messages are considered delivered even if their execution fails (i.e. reverts) by using `evm.Call()` with a pre-defined gas limit to execute the message payload. This gas limit is specified by each message in the call to `sendCrossChainMessage`. Relayers must provide at least enough gas for the sub-call in addition to the standard gas used by a call to `receiveCrossChainMessage`. In the event that a message exeuction runs out of gas or reverts for any other reason, the hash of the message payload is stored by the receiving Teleporter contract instance. This allows for the message execution to be retried in the future, with possibly a higher gas limit by calling `retryMessageExecution`. Importantly, a message is still considered delivered on its destination chain even if its execution fails. This allows the relayer of the message to redeem their reward for deliverying the message, because they have no control on whether or not its execution will succeed or not so long as they provide sufficient gas to meet the specified `requiredGasLimit`. diff --git a/contracts/src/Teleporter/ReceiptQueue.sol b/contracts/src/Teleporter/ReceiptQueue.sol index b687fbef1..b7011f0bf 100644 --- a/contracts/src/Teleporter/ReceiptQueue.sol +++ b/contracts/src/Teleporter/ReceiptQueue.sol @@ -34,7 +34,7 @@ library ReceiptQueue { } /** - * @dev Removes the oldest open receipt from the queue. + * @dev Removes the oldest outstanding receipt from the queue. * * Requirements: * - The queue must be non-empty. @@ -74,7 +74,7 @@ library ReceiptQueue { } /** - * @dev Returns the number of open receipts in the queue. + * @dev Returns the number of outstanding receipts in the queue. */ function size( TeleporterMessageReceiptQueue storage queue diff --git a/contracts/src/Teleporter/TeleporterMessenger.sol b/contracts/src/Teleporter/TeleporterMessenger.sol index 8fb070b12..b44276fdd 100644 --- a/contracts/src/Teleporter/TeleporterMessenger.sol +++ b/contracts/src/Teleporter/TeleporterMessenger.sol @@ -17,7 +17,7 @@ import "./ReentrancyGuards.sol"; /** * @dev Implementation of the {ITeleporterMessenger} interface. * - * This implementation is used to send messages cross chain using the WarpMessenger precompile, + * This implementation is used to send messages cross-chain using the WarpMessenger precompile, * and to receive messages sent from other chains. Teleporter contracts should be deployed through Nick's method * of universal deployer, such that the same contract is deployed at the same address on all chains. */ @@ -33,34 +33,34 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { WarpMessenger public constant WARP_MESSENGER = WarpMessenger(0x0200000000000000000000000000000000000005); - // Tracks the latest message ID used for a given destination subnet. - // Key is the destination subnet ID, and the value is the last message ID used for that subnet. - // Note that the first message ID used for each subnet will be 1 (not 0). + // Tracks the latest message ID used for a given destination chain. + // Key is the destination chain ID, and the value is the last message ID used for that chain. + // Note that the first message ID used for each chain will be 1 (not 0). mapping(bytes32 => uint256) public latestMessageIDs; - // Tracks the outstanding receipts to send back to a given subnet in subsequent messages sent to it. - // Key is the subnet ID of the other subnet, and the value is a queue of pending receipts for messages - // we have received from that subnet. + // Tracks the outstanding receipts to send back to a given chain in subsequent messages sent to it. + // Key is the other chain ID, and the value is a queue of pending receipts for messages + // we have received from that chain. mapping(bytes32 => ReceiptQueue.TeleporterMessageReceiptQueue) public outstandingReceipts; // Tracks the message hash and fee information for each message sent that we have not yet received - // a receipt for. The messages are tracked per subnet and keyed by message ID. - // The first key is the subnet ID, the second key is the message ID, and the value is the info + // a receipt for. The messages are tracked per chain and keyed by message ID. + // The first key is the chain ID, the second key is the message ID, and the value is the info // for the uniquely identified message. mapping(bytes32 => mapping(uint256 => SentMessageInfo)) public sentMessageInfo; - // Tracks the relayer reward address for each message delivered from a given subnet. + // Tracks the relayer reward address for each message delivered from a given chain. // Note that these values are also used to determine if a given message has been delivered or not. - // The first key is the subnet ID, the second key is the message ID, and the value is the reward address + // The first key is the chain ID, the second key is the message ID, and the value is the reward address // provided by the deliverer of the uniquely identified message. mapping(bytes32 => mapping(uint256 => address)) public relayerRewardAddresses; // Tracks the hash of messages that have been received but whose execution has never succeeded. // Enables retrying of failed messages with higher gas limits. Message execution is guaranteed to - // succeed at most once. The first key is the subnet ID, the second key is the message ID, and + // succeed at most once. The first key is the chain ID, the second key is the message ID, and // the value is the hash of the uniquely identified message whose execution failed. mapping(bytes32 => mapping(uint256 => bytes32)) public receivedFailedMessageHashes; @@ -70,12 +70,6 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { // and the value is the amount of the asset owed to the relayer. mapping(address => mapping(address => uint256)) public relayerRewardAmounts; - // Teleporter delivers message by invoking functions of the form: funcName(bytes32 originChainID, address originSenderAddress, ...) - // where "..." can represent arbitrary additional parameters. Accounting for the 4 byte function selector and two 32 byte parameters, - // there is a minimum valid message length of 68 bytes. - uint256 public constant REQUIRED_ORIGIN_CHAIN_ID_START_INDEX = 4; - uint256 public constant MINIMUM_REQUIRED_CALL_DATA_LENGTH = 68; - // The blockchain ID of the chain the contract is deployed on. Initialized lazily when receiveCrossChainMessage() is called, // if the value has not already been set. bytes32 public blockchainID; @@ -83,8 +77,8 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev See {ITeleporterMessenger-sendCrossChainMessage} * - * When executed, it will kick off an asynchronous event to have the validators of the chain create an - * aggregate BLS signature of the message. + * When executed, a relayer may kick off an asynchronous event to have the validators of the + * chain create an aggregate BLS signature of the message. * * Emits a {SendCrossChainMessage} event when message successfully gets sent. */ @@ -92,7 +86,7 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { TeleporterMessageInput calldata messageInput ) external senderNonReentrant returns (uint256 messageID) { // Get the outstanding receipts for messages that have been previously received - // from the destination subnet but not yet acknowledged, and attach the receipts + // from the destination chain but not yet acknowledged, and attach the receipts // to the Teleporter message to be sent. return _sendTeleporterMessage({ @@ -110,10 +104,10 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev See {ITeleporterMessenger-retrySendCrossChainMessage} * - * Emits {SendCrossChainMessage} event. + * Emits a {SendCrossChainMessage} event. * Requirements: * - * - `message` must have been previously sent to the given destination chain ID. + * - `message` must have been previously sent to the given `destinationChainID`. * - `message` encoding mush match previously sent message. */ function retrySendCrossChainMessage( @@ -158,12 +152,12 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev See {ITeleporterMessenger-addFeeAmount} * - * Emits {AddFeeAmount} event. + * Emits an {AddFeeAmount} event. * Requirements: * * - `additionalFeeAmount` must be non-zero. - * - message must exist and not have been delivered yet. - * - `feeContractAddress` must match the fee asset contract address used in the original call to sendCrossChainMessage. + * - `message` must exist and not have been delivered yet. + * - `feeContractAddress` must match the fee asset contract address used in the original call to `sendCrossChainMessage`. */ function addFeeAmount( bytes32 destinationChainID, @@ -225,7 +219,7 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev See {ITeleporterMessenger-receiveCrossChainMessage} * - * Emits {ReceiveCrossChainMessage} event. + * Emits a {ReceiveCrossChainMessage} event. * Re-entrancy is explicitly disallowed between receiving functions. One message is not able to receive another message. * Requirements: * @@ -355,7 +349,13 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev See {ITeleporterMessenger-retryMessageExecution} * - * Reverts if the message execution fails again on specified message. + * A Teleporter message has an associated `requiredGasLimit` that is used to execute the message. + * If the `requiredGasLimit` is too low, then the message execution will fail. This method allows + * for retrying the execution of a message with a higher gas limit. Contrary to `receiveCrossChainMessage`, + * which will only use `requiredGasLimit` in the sub-call to execute the message, this method may + * use all of the gas available in the transaction. + * + * Reverts if the message execution fails again on the specified message. * Emits a {MessageExecuted} event if the retry is successful. * Requirements: * @@ -580,7 +580,7 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { /** * @dev Helper function for sending a teleporter message cross chain. - * Constructs the teleporter message and sends it through the warp messenger precompile, + * Constructs the Teleporter message and sends it through the Warp Messenger precompile, * and performs fee transfer if necessary. * * Emits a {SendCrossChainMessage} event. @@ -662,7 +662,7 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { } /** - * @dev Marks the receipt of a message from the given destination chain ID with the given message ID. + * @dev Marks the receipt of a message from the given `destinationChainID` with the given `messageID`. * * It is possible that the receipt was already received for this message, in which case we return early. * If existing message is found and not yet delivered, we delete it from state and increment the fee/reward @@ -774,7 +774,7 @@ contract TeleporterMessenger is ITeleporterMessenger, ReentrancyGuards { } /** - * @dev Returns the next message ID to be used to send a message to the given chain ID. + * @dev Returns the next message ID to be used to send a message to the given `chainID`. */ function _getNextMessageID( bytes32 chainID diff --git a/scripts/local/constants.sh b/scripts/constants.sh similarity index 96% rename from scripts/local/constants.sh rename to scripts/constants.sh index 7c995d821..ba8697a48 100755 --- a/scripts/local/constants.sh +++ b/scripts/constants.sh @@ -7,7 +7,7 @@ TELEPORTER_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" - cd ../.. && pwd + cd .. && pwd ) # Set the PATHS diff --git a/scripts/local/lint.sh b/scripts/lint.sh similarity index 94% rename from scripts/local/lint.sh rename to scripts/lint.sh index e28862434..4d98c927a 100755 --- a/scripts/local/lint.sh +++ b/scripts/lint.sh @@ -6,7 +6,7 @@ set -e TELEPORTER_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" - cd ../.. && pwd + cd .. && pwd ) cd $TELEPORTER_PATH/contracts/src diff --git a/scripts/local/e2e_test.sh b/scripts/local/e2e_test.sh index 204169de3..2b072320e 100755 --- a/scripts/local/e2e_test.sh +++ b/scripts/local/e2e_test.sh @@ -54,9 +54,9 @@ TELEPORTER_PATH=$( cd ../.. && pwd ) -source "$TELEPORTER_PATH"/scripts/local/constants.sh +source "$TELEPORTER_PATH"/scripts/constants.sh -source "$TELEPORTER_PATH"/scripts/local/versions.sh +source "$TELEPORTER_PATH"/scripts/versions.sh # Build the teleporter and cross chain apps smart contracts cwd=$(pwd) diff --git a/scripts/local/run.sh b/scripts/local/run.sh index bedb6927d..a7bf8f900 100755 --- a/scripts/local/run.sh +++ b/scripts/local/run.sh @@ -7,10 +7,12 @@ set -e # Stop on first error source ./scripts/utils.sh LOCAL_RELAYER_IMAGE= +RUN_STOP_FLAG="-c" HELP= while [ $# -gt 0 ]; do case "$1" in -l | --local-relayer-image) LOCAL_RELAYER_IMAGE=$2 ;; + -p | --pause) RUN_STOP_FLAG= ;; -h | --help) HELP=true ;; esac shift @@ -22,13 +24,14 @@ if [ "$HELP" = true ]; then echo "" echo "Options:" echo " -l, --local-relayer-image Use a local AWM Relayer image instead of pulling from dockerhub" + echo " -p, --pause Pause the network on stop. Will attempt to restart the paused network on subsequent runs" echo " -h, --help Print this help message" exit 0 fi function cleanup { echo "Shutting down network before exiting..." - ./scripts/local/run_stop.sh + ./scripts/local/run_stop.sh $RUN_STOP_FLAG echo "Network stopped" } diff --git a/scripts/local/run_stop.sh b/scripts/local/run_stop.sh index dbd76d8ff..b9b7f2c4b 100755 --- a/scripts/local/run_stop.sh +++ b/scripts/local/run_stop.sh @@ -28,6 +28,7 @@ done # If clean flag is set we remove network running file to start the network from scratch if $clean then + echo "Cleaning network" rm -f NETWORK_RUNNING fi diff --git a/scripts/local/test.sh b/scripts/local/test.sh index bd5e2b8bb..876dd38eb 100755 --- a/scripts/local/test.sh +++ b/scripts/local/test.sh @@ -8,11 +8,13 @@ source ./scripts/utils.sh TEST_TARGET= LOCAL_RELAYER_IMAGE= TESTNET= +RUN_STOP_FLAG="-c" HELP= while [ $# -gt 0 ]; do case "$1" in -t | --test) export TEST_TARGET=$2 ;; -l | --local-relayer-image) LOCAL_RELAYER_IMAGE=$2 ;; + -p | --pause) RUN_STOP_FLAG= ;; -h | --help) HELP=true ;; esac shift @@ -26,13 +28,14 @@ if [ "$HELP" = true ]; then echo " -t, --test Run a specific test. If empty, runs all tests in the ./scripts/local/integration-tests/" echo " -t, --test "test1 test2" Run multiple tests. Test names must be space delimited and enclosed in quotes" echo " -l, --local-relayer-image Use a local AWM Relayer image instead of pulling from dockerhub" + echo " -p, --pause Pause the network on stop. Will attempt to restart the paused network on subsequent runs" echo " -h, --help Print this help message" exit 0 fi function cleanup { echo "Shutting down network before exiting..." - ./scripts/local/run_stop.sh + ./scripts/local/run_stop.sh $RUN_STOP_FLAG echo "Network stopped" } @@ -84,6 +87,6 @@ do done # Stop the containers gracefully -./scripts/local/run_stop.sh +./scripts/local/run_stop.sh $RUN_STOP_FLAG code=$(docker inspect --format='{{.State.ExitCode}}' test_runner) exit $code diff --git a/scripts/local/versions.sh b/scripts/versions.sh similarity index 100% rename from scripts/local/versions.sh rename to scripts/versions.sh diff --git a/utils/contract-deployment/README.md b/utils/contract-deployment/README.md index 0c9259ee3..fad74d7ff 100644 --- a/utils/contract-deployment/README.md +++ b/utils/contract-deployment/README.md @@ -1,6 +1,6 @@ ### Contract Deployment -The Teleporter contract is designed to only send and receive Avalanche Warp messages to and from its own address on different chains. We ensure that the contract can be deployed to the same address on every EVM based chain by using [Nick's Method](https://yamenmerhi.medium.com/nicks-method-ethereum-keyless-execution-168a6659479c). Only allowing messages to be sent to and received from the same address guarantees that all messages use the same Teleporter message format because only the same exact contract bytecode could have been deployed to the same address. +The `TeleporterMessenger` contract is designed to only send and receive Avalanche Warp messages to and from its own address on different chains. We ensure that the contract can be deployed to the same address on every EVM based chain by using [Nick's Method](https://yamenmerhi.medium.com/nicks-method-ethereum-keyless-execution-168a6659479c). Only allowing messages to be sent and received by the same address guarantees that all messages use the same Teleporter message format because only the same exact contract bytecode could have been deployed to the same address. This directory contains scripts written in Golang to construct a raw transaction using Nick's method that deploys the Teleporter contract, and determine the keyless address that must be prefunded in order for the transaction to be sent. @@ -17,4 +17,4 @@ OR `go run contract-deployment/contractDeploymentTools.go deriveContractAddress 0x38545c4b331D8BFb3bee94C62D77a6735b5eF8c0 1` ## Results -The resulting raw transaction and universal deployer address are written to standard output, as well as to `UniversalTeleporterDeployerTransaction.txt` and `UniversalTeleporterDeployerAddress.txt` respectively. \ No newline at end of file +The resulting raw transaction, `TeleporterMessenger` contract address, and universal deployer address are written to standard output, as well as to `UniversalTeleporterDeployerTransaction.txt`, `UniversalTeleporterMessengerContractAddress.txt`, and `UniversalTeleporterDeployerAddress.txt` respectively. \ No newline at end of file