diff --git a/e2e-testing/.gitignore b/e2e-testing/.gitignore index 2b245ce0..c5f502f7 100644 --- a/e2e-testing/.gitignore +++ b/e2e-testing/.gitignore @@ -4,6 +4,14 @@ solana_artifacts/ evm_deploy_results/ near_deploy_results/ solana_deploy_results/ +bridge-token-near-to-evm/ + +.evm-compile.stamp +.near-token-deployment-prepare.stamp +.solana-build.stamp +*dyn_init_args.json +bridge-sdk-config.json +bridge_token_factory-keypair.json evm_scripts/build/ diff --git a/e2e-testing/Makefile b/e2e-testing/Makefile index f808e61d..4fef8259 100644 --- a/e2e-testing/Makefile +++ b/e2e-testing/Makefile @@ -1,190 +1,69 @@ -.PHONY: evm-compile evm-scripts-build - -.DELETE_ON_ERROR: - -TESTING_ROOT := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) - -TIMESTAMP := $(shell date -u +%Y%m%d-%H%M%S) - -EVM_DIR := $(TESTING_ROOT)/../evm -EVM_SCRIPT_DIR := $(TESTING_ROOT)/evm_scripts -ETHEREUM_DEPLOY_RESULTS_DIR := $(TESTING_ROOT)/ethereum_deploy_results - -NEAR_DIR := $(TESTING_ROOT)/../near -NEAR_DEPLOY_RESULTS_DIR := $(TESTING_ROOT)/near_deploy_results - -SOLANA_DIR := $(TESTING_ROOT)/../solana -SOLANA_DEPLOY_RESULTS_DIR := $(TESTING_ROOT)/solana_deploy_results - -NEAR_BRIDGE_ID_FILE := $(NEAR_DEPLOY_RESULTS_DIR)/omni_bridge.json - ########################################################## -# EVM +# Makefile prologue ########################################################## -EVM_COMPILE_STAMP := $(TESTING_ROOT)/.evm-compile.stamp -EVM_ARTIFACTS_DIR := $(TESTING_ROOT)/evm_artifacts -EVM_SCRIPT_COMPILE_STAMP := $(TESTING_ROOT)/.evm-scripts-compile.stamp - -EVM_NETWORKS := sepolia arbitrumSepolia baseSepolia - -EVM_DEPLOY_RESULTS_DIR := $(TESTING_ROOT)/evm_deploy_results - -DEPLOY_EVM_TOKEN_IMPL = yarn --silent --cwd $(EVM_DIR) hardhat deploy-token-impl --network $(1) -DEPLOY_EVM_OMNI_BRIDGE_CONTRACT = yarn --silent --cwd $(EVM_DIR) hardhat deploy-bridge-token-factory --network $(1) --bridge-token-impl $(2) --near-bridge-account-id $(3) -DEPLOY_EVM_FAKE_PROVER = yarn --silent --cwd $(EVM_DIR) hardhat deploy-fake-prover --network $(1) -DEPLOY_EVM_ENEAR_PROXY = yarn --silent --cwd $(EVM_DIR) hardhat deploy-e-near-proxy --network $(1) --enear $(2) - -DEPLOY_EVM_BYTECODE = yarn --silent --cwd $(EVM_SCRIPT_DIR) hardhat deploy-bytecode --network $(1) --bytecode $(2) -DEPLOY_EVM_TEST_TOKEN = yarn --silent --cwd $(EVM_SCRIPT_DIR) hardhat deploy-test-token --network $(1) --name $(2) --symbol $(3) - -ENEAR_CREATION_TEMPLATE_FILE := $(TESTING_ROOT)/bin/eNear_creation.template - -$(EVM_DEPLOY_RESULTS_DIR): - mkdir -p $@ - -evm-build: $(EVM_COMPILE_STAMP) -$(EVM_COMPILE_STAMP): - @echo "Compiling EVM contracts" - mkdir -p $(EVM_ARTIFACTS_DIR) && \ - yarn --cwd $(EVM_DIR) install --frozen-lockfile && \ - yarn --cwd $(EVM_DIR) hardhat compile && \ - cp -r $(EVM_DIR)/build/* $(EVM_ARTIFACTS_DIR) - touch $@ - -evm-scripts-build: $(EVM_SCRIPT_COMPILE_STAMP) -$(EVM_SCRIPT_COMPILE_STAMP): - @echo "Compiling EVM scripts" - yarn --cwd $(EVM_SCRIPT_DIR) install && \ - yarn --cwd $(EVM_SCRIPT_DIR) hardhat compile - touch $@ - -# Arguments: -# $(1) - the network name -define generate_evm_deploy_rules - -.PHONY: $(1)-deploy-fake-prover $(1)-deploy-enear $(1)-deploy-enear-proxy $(1)-deploy-bridge $(1)-deploy-token-impl $(1)-deploy-test-token - -$(1)_DEPLOY_RESULTS_DIR := $(EVM_DEPLOY_RESULTS_DIR)/$(1) - -$$($(1)_DEPLOY_RESULTS_DIR): | $(EVM_DEPLOY_RESULTS_DIR) - mkdir -p $$@ - -$(1)-deploy: $(1)-deploy-bridge $(1)-deploy-enear-proxy $(1)-deploy-test-token - -$(1)_BRIDGE_CONTRACT_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/omni_bridge.json -$(1)_TOKEN_IMPL_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/token_factory.json -$(1)_FAKE_PROVER_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/fake_prover.json - -$(1)_ENEAR_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/eNear.json -$(1)_ENEAR_PROXY_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/eNearProxy.json -$(1)_ENEAR_CREATION_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/eNear_creation - -$(1)_TEST_TOKEN_ADDRESS_FILE := $$($(1)_DEPLOY_RESULTS_DIR)/test_token.json - -$(1)-deploy-fake-prover: $$($(1)_FAKE_PROVER_ADDRESS_FILE) -$$($(1)_FAKE_PROVER_ADDRESS_FILE): $(EVM_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_FAKE_PROVER,$(1)) 2>/dev/stderr 1> $$@ - -$(1)-deploy-enear: $$($(1)_ENEAR_ADDRESS_FILE) -$$($(1)_ENEAR_ADDRESS_FILE): $$($(1)_ENEAR_CREATION_FILE) $(EVM_SCRIPT_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_BYTECODE,$(1),$$($(1)_ENEAR_CREATION_FILE)) 2>/dev/stderr 1> $$@ - -$(1)-deploy-enear-proxy: $$($(1)_ENEAR_PROXY_ADDRESS_FILE) -$$($(1)_ENEAR_PROXY_ADDRESS_FILE): $$($(1)_ENEAR_ADDRESS_FILE) $(EVM_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_ENEAR_PROXY,$(1),$$(shell cat $$($(1)_ENEAR_ADDRESS_FILE) | jq -r .contractAddress)) 2>/dev/stderr 1> $$@ - -$(1)-deploy-bridge: $$($(1)_BRIDGE_CONTRACT_ADDRESS_FILE) -$$($(1)_BRIDGE_CONTRACT_ADDRESS_FILE): $$($(1)_TOKEN_IMPL_ADDRESS_FILE) $(NEAR_BRIDGE_ID_FILE) $(EVM_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_OMNI_BRIDGE_CONTRACT,$(1),$$(shell cat $$($(1)_TOKEN_IMPL_ADDRESS_FILE) | jq -r .tokenImplAddress),$$(shell cat $(NEAR_BRIDGE_ID_FILE) | jq -r .contract_id)) 2>/dev/stderr 1> $$@ - -$(1)-deploy-token-impl: $$($(1)_TOKEN_IMPL_ADDRESS_FILE) -$$($(1)_TOKEN_IMPL_ADDRESS_FILE): $(EVM_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_TOKEN_IMPL,$(1)) 2>/dev/stderr 1> $$@ - -$$($(1)_ENEAR_CREATION_FILE): $(ENEAR_CREATION_TEMPLATE_FILE) $$($(1)_FAKE_PROVER_ADDRESS_FILE) | $$($(1)_DEPLOY_RESULTS_DIR) - cat $$< | \ - sed "s//$$(shell cat $$($(1)_FAKE_PROVER_ADDRESS_FILE) | jq -r .fakeProverAddress | sed 's/^0x//')/" > $$@ - - -$(1)-deploy-test-token: $$($(1)_TEST_TOKEN_ADDRESS_FILE) -$$($(1)_TEST_TOKEN_ADDRESS_FILE): $(EVM_SCRIPT_COMPILE_STAMP) | $$($(1)_DEPLOY_RESULTS_DIR) - $$(call DEPLOY_EVM_TEST_TOKEN,$(1),E2ETestToken-$(TIMESTAMP),E2ETT-$(TIMESTAMP)) 2>/dev/stderr 1> $$@ - -endef - -$(foreach network,$(EVM_NETWORKS),$(eval $(call generate_evm_deploy_rules,$(network)))) - -########################################################## -# NEAR -########################################################## - -NEAR_BINARY_DIR := $(TESTING_ROOT)/near_artifacts - -.PHONY: near-build near-deploy - -# List all expected WASM binaries -NEAR_BINARIES := evm_prover.wasm omni_bridge.wasm omni_prover.wasm omni_token.wasm token_deployer.wasm wormhole_omni_prover_proxy.wasm mock_token.wasm -NEAR_BINARY_PATHS := $(addprefix $(NEAR_BINARY_DIR)/,$(NEAR_BINARIES)) - -NEAR_BUILD_STAMP := $(TESTING_ROOT)/.near-build.stamp - -DEPLOY_RESULTS := $(patsubst $(NEAR_BINARY_DIR)/%.wasm,$(NEAR_DEPLOY_RESULTS_DIR)/%.json,$(NEAR_BINARY_PATHS)) - -near-deploy: $(DEPLOY_RESULTS) - -$(NEAR_DEPLOY_RESULTS_DIR): - mkdir -p $@ - -near-build: $(NEAR_BUILD_STAMP) -$(NEAR_BUILD_STAMP): - $(NEAR_DIR)/build.sh --output-dir $(NEAR_BINARY_DIR) - touch $@ - -# Arguments: -# $(1) - the path to the binary file -define generate_near_deploy_rules - -$(NEAR_DEPLOY_RESULTS_DIR)/$(basename $(notdir $(1))).json: $(1) | $(NEAR_DEPLOY_RESULTS_DIR) - ./scripts/deploy-near-contract.sh $(1) $$@ $$(basename $$(notdir $(1)))-$(TIMESTAMP).testnet - -$(1): $(NEAR_BUILD_STAMP) - -endef - -$(foreach binary,$(NEAR_BINARY_PATHS),$(eval $(call generate_near_deploy_rules,$(binary)))) - - -######################################## -# Solana -######################################## - -SOLANA_BUILD_STAMP := $(TESTING_ROOT)/.solana-build.stamp -SOLANA_ARTIFACTS_DIR := $(TESTING_ROOT)/solana_artifacts - -SOLANA_PROGRAMS := bridge_token_factory -SOLANA_PROGRAMS_KEYPAIRS := $(foreach program,$(SOLANA_PROGRAMS),$(SOLANA_DIR)/$(program)/target/deploy/$(program)-keypair.json) -SOLANA_PROGRAMS_BINARIES := $(foreach program,$(SOLANA_PROGRAMS),$(SOLANA_ARTIFACTS_DIR)/$(program)/target/deploy/$(program).so) - -solana-build: $(SOLANA_BUILD_STAMP) -$(SOLANA_BUILD_STAMP): $(SOLANA_PROGRAMS_KEYPAIRS) $(SOLANA_PROGRAMS_BINARIES) - touch $@ - -# Arguments: -# $(1) - the program name -define generate_solana_build_rules - -$(SOLANA_DIR)/$(1)/target/deploy/$(1)-keypair.json: $(TESTING_ROOT)/$(1)-keypair.json - mkdir -p $$(dir $$@) && \ - cp $$< $$@ - -$(SOLANA_ARTIFACTS_DIR)/$(1)/target/deploy/$(1).so: $(SOLANA_DIR)/$(1)/target/deploy/$(1)-keypair.json - mkdir -p $(SOLANA_ARTIFACTS_DIR)/$(1) && \ - cd $(SOLANA_DIR)/$(1) && \ - anchor build && \ - cp -r $(SOLANA_DIR)/$(1)/target/* $(SOLANA_ARTIFACTS_DIR)/$(1) - -endef - -$(foreach program,$(SOLANA_PROGRAMS),$(eval $(call generate_solana_build_rules,$(program)))) +MAKEFLAGS += --no-builtin-rules --no-builtin-variables --warn-undefined-variables --silent +unexport MAKEFLAGS +.DELETE_ON_ERROR: +.SUFFIXES: +SHELL := bash +.SHELLFLAGS := -eu -o pipefail -c +.DEFAULT_GOAL := help + +# Master clean target +.PHONY: clean +clean: clean-deploy-results clean-evm clean-near clean-solana clean-bridge-token-near-to-evm + $(call description,Cleaning all build artifacts and deploy results) + +# Include common module +include makefiles/common.mk + +# Include chain-specific modules +include makefiles/evm.mk +include makefiles/near.mk +include makefiles/solana.mk + +# Include test pipelines +include makefiles/pipelines/bridge_token_near_to_evm.mk + +# Help target +.PHONY: help +help: + $(call description,Available targets) + @echo "Build targets:" + @echo " evm-build Build EVM contracts" + @echo " evm-scripts-build Build EVM deployment scripts" + @echo " near-build Build NEAR contracts" + @echo " solana-build Build Solana programs" + @echo + @echo "Clean targets:" + @echo " clean Clean all build artifacts and deploy results" + @echo " clean-deploy-results Clean deploy results directories" + @echo " clean-evm Clean all EVM build artifacts" + @echo " clean-evm-{network} Clean specific network deploy results" + @echo " Available networks: $(evm_networks)" + @echo " clean-near Clean NEAR build artifacts" + @echo " clean-solana Clean Solana build artifacts" + @echo " clean-bridge-token-near-to-evm Clean bridge pipeline artifacts" + @echo + @echo "Account creation:" + @echo " create-near-init-account Create NEAR initialization account" + @echo " create-near-sender Create NEAR sender account" + @echo " create-near-relayer Create NEAR relayer account" + @echo " create-dao-account Create NEAR DAO account" + @echo + @echo "Deployment targets:" + @echo " near-deploy Deploy all NEAR contracts" + @echo " {network}-deploy Deploy all contracts to specific EVM network" + @echo " Available networks: $(evm_networks)" + @echo " {network}-deploy-bridge Deploy bridge contract to specific network" + @echo " {network}-deploy-enear Deploy eNEAR token to specific network" + @echo " {network}-deploy-test-token Deploy test token to specific network" + @echo + @echo "Bridge pipeline:" + @echo " bridge-token-near-to-evm Run complete NEAR to ETH bridge test" + @echo " prepare-token-deployment Prepare token deployment (Step 0)" + @echo " near-log-metadata-call Log token metadata (Step 1)" + @echo " ethereum-deploy-token Deploy token on Ethereum (Step 2)" + @echo " near-bind-token Bind token on NEAR (Step 3)" diff --git a/e2e-testing/README.md b/e2e-testing/README.md index b9b54414..3e8cac18 100644 --- a/e2e-testing/README.md +++ b/e2e-testing/README.md @@ -1,63 +1,128 @@ -# End-to-end testing +# Omni Bridge End-to-End Tests + +## General description + +The E2E tests cover an entire workflow involving multiple blockchain components (NEAR, EVM-based chains, Solana) and cross-chain communication. These tests ensure that all parts (smart contracts, scripts, etc.) integrate correctly. The Makefiles in this project orchestrate each step in the workflow, from compiling and deploying contracts on various chains to executing and verifying cross-chain transactions. ## Prerequisites -- yarn -- cargo -- [NEAR CLI RS](https://github.com/near/near-cli-rs) -- docker -- [Solana CLI and Anchor](https://solana.com/docs/intro/installation) -- Bridge SDK CLI: `cargo install --git https://github.com/Near-One/bridge-sdk-rs/ bridge-cli` +You will need the following tools installed on your environment before proceeding: +- **Yarn**. Used for installing TypeScript dependencies for EVM contracts and scripts. +- **Cargo**. The Rust package manager, required for building Rust-based components. +- **NEAR CLI RS**. A command-line interface for interacting with NEAR protocols. +- **Docker**. Required to build NEAR contracts in consistent environment. +- **Solana CLI and Anchor**. For compiling and deploying Solana programs. +- **Bridge SDK CLI**. Install with: +`cargo install --git https://github.com/Near-One/bridge-sdk-rs/ --rev e2c86d5 bridge-cli` +Enables bridging functionality for various blockchain environments. +- **jq**. A command-line JSON processor used by many scripts in this project. + +## User guide + +### How to Run Builds and Pipelines + +This repository contains multiple Makefiles, each focusing on a particular chain or pipeline. + +You can explore all the available targets by running: +``` +make help +``` + +Typical usage involves calling a specific pipeline target, for example: + +``` +make bridge-token-near-to-evm +``` + +This command triggers a multi-step process that compiles, deploys, and binds tokens across NEAR and an EVM-based network. + +### Environment variables and configuration + +You need to create a `.env` files from: +- `./evm-scripts/.env.example` +- `../evm/.env.example` (`INFURA_API_KEY` and `EVM_PRIVATE_KEY` only) -## Using the Makefile +Also you need to copy or rename the provided `bridge-sdk-config.example.json` to `bridge-sdk-config.json`. And update it with your `ETH_PRIVATE_KEY` and your `ETH_RPC` endpoint. -The `Makefile` in this project is designed to automate the deployment and compilation processes for both EVM and NEAR environments. It provides a set of predefined rules that can be executed to perform specific tasks, such as compiling contracts, deploying them to various networks, and setting up necessary infrastructure. +For Solana bulding and deployment, ensure that for every program you have a keypair in `.e2e-testing/` directory in the format of `-keypair.json`. However, this key pair is secret and should not be shared. -### Common Tasks +### Result artifacts -- **Compile EVM Contracts**: To compile the EVM contracts, run: - ```bash - make evm-compile - ``` +Throughout the pipelines, you will see JSON files containing addresses, transaction hashes, and other relevant data. +These files serve as evidence that each step or deployment was successfully executed. They are automatically generated and stored in dedicated directories such as `evm_deploy_results` or `near_deploy_results`. -- **Deploy to EVM Networks**: To deploy contracts to a specific EVM network, use the following pattern: - ```bash - make -deploy - ``` - Replace `` with the desired network name, such as `sepolia` or `arbitrumSepolia`. +### Handling Pipeline Failures -- **Build NEAR Contracts**: To build the NEAR contracts, execute: - ```bash +If a pipeline fails at a certain step, fix the underlying issue and rerun the same target. Make will pick up from the point of failure if the previous steps have created their artifact files or “stamp” files. + +### Rebuilding Binaries + +Each build step depends on “stamp” files that mark the completion of the step. Simply calling the relevant build target again will skip the build if the stamp file exists. + +To perform a clean rebuild, run the corresponding clean target. For example: +``` + make clean-near make near-build - ``` +``` + +This removes the old artifacts and stamps, forcing a complete recompile. + +## Developer Guide + +### Introduction to Make + +- If you are new to Make, the [[GNU Make Manual](https://www.gnu.org/software/make/manual/make.html)] is an excellent place to start. +- Make allows us to define rules that specify how to build or process files and manage dependencies. + + +### Structure of the Makefiles + +- Makefiles here are split into modules for different chains (e.g., `near.mk`, `evm.mk`, `solana.mk`) and pipelines (e.g., `pipelines/bridge_token_near_to_evm.mk`). +- Each of these is included into a master Makefile. +- Variables are in a global namespace. Therefore, every variable should be prefixed to avoid naming collisions, such as `evm_compile_stamp`, `solana_build_stamp`, etc. + +### Pipelines Organization -- **Deploy NEAR Contracts**: To deploy NEAR contracts, run: - ```bash - make near-deploy - ``` +- Each pipeline typically has a prefix, such as pipeline1, pipeline2, and so on. +- Consider adding new pipelines in separate `.mk` files. +- Number the generated files for clarity (e.g., `01_step.json`, `02_step.json`) to keep track of the pipeline steps. -These tasks automate the process of setting up the testing environment, ensuring that all necessary components are compiled and deployed correctly. +### Targets and Phony Targets -### Additional Requirements +Most steps have two targets: -- **Private Key Requirement**: For Ethereum deployment, ensure that you add your `EVM_PRIVATE_KEY` to the `./evm/.env` file. This key is necessary for authenticating transactions on the Ethereum network. +1. A “file” target (e.g., a JSON artifact) or a “stamp” file target to indicate completion. +2. A `.PHONY` target to run that step directly. -- **Solana Keypair Requirement**: For Solana bulding and deployment, ensure that for every program you have a keypair in `.e2e-testing/` directory in the format of `-keypair.json`. However, this key pair is secret and should not be shared. +The phony target typically depends on the file target, ensuring that the command is performed when necessary. -### Deployment Results +Each step usually prints a brief description before execution using a helper function like `description`, so you know what is happening. -- **Storage of Results**: The addresses of deployed contracts are stored in JSON files in their corresponding locations. For example, the token factory deployed on the `arbitrumSepolia` network will be stored in `evm_deploy_results/arbitrumSepolia/token_factory.json`. This is done to reuse these addresses across different runs of the tests. +### Order of prerequsites -### General Working Principles +In certain cases, the order in which prerequisites are listed (and thus passed to scripts) can matter. Pay special attention to the scripts that rely on positional arguments. -- **Phony Targets**: The `Makefile` uses `.PHONY` targets to define tasks that do not correspond to actual files. This ensures that these tasks are always executed when called, regardless of the presence of files with the same name. +### Special targets -- **Variables**: The `Makefile` defines several variables to manage paths and configurations, such as `TESTING_ROOT`, `EVM_DIR`, and `NEAR_DIR`. These variables help in organizing the file structure and making the `Makefile` adaptable to different environments. +- Every module or feature should provide a custom `clean-{custom-name}` target that removes the artifacts and stamp files it generates. +- Remember to add that clean target to the help target or a consolidated list of “clean” targets so that users can discover and run it easily. +- After adding module, don't forget to add it to the `help` target. -- **Rule Expansion**: The `Makefile` uses a combination of static and dynamic rule definitions. Dynamic rules are generated using the `define` directive, which allows for the creation of rules based on the networks specified in the `EVM_NETWORKS` variable. This approach reduces redundancy and makes it easy to add support for additional networks. +### Debugging -- **Dependencies**: Each target in the `Makefile` specifies its dependencies, ensuring that all necessary steps are completed before executing a task. For example, deploying a contract requires that it is compiled first. +- Run `make --dry-run` to print the commands that would be executed without actually running them. +- Use `make -d` for a more verbose explanation of why each command runs (or doesn’t run). It could be used with `make --dry-run` in order not to run the commands. -- **Command Execution**: The `Makefile` uses shell commands to execute tasks, such as running `yarn` for EVM contract compilation and deployment, and custom scripts for NEAR contract deployment. +## General recommendations for Makefiles in this project -In order to see which commands will be executed without actually executing them, you can add `--dry-run` to the command. \ No newline at end of file +Below are some guidelines to maintain consistency and clarity: +1. Internal variables (not environment or inherited from parent Makefile) should use lowercase. +2. Prefer `:=` (immediate assignment) over `=` (delayed assignment) for most variable definitions. +3. Use `.INTERMEDIATE` and `.PHONY` to define targets properly. +4. Make each phony target a prerequisite of `.PHONY` right before declaring the target. +5. Avoid using phony targets as prerequisites for file targets. +6. When a Makefile grows large, consider splitting it into multiple files, each handling a distinct set of tasks or a single pipeline. +7. Use automatic variables like `$^`, `$@`, `$<`, and `$(word n,$^)` wisely to simplify commands. +8. Directories often work best as order-only prerequisites (using the pipe symbol `|`) to avoid rebuilding them unnecessarily. +9. Try to avoid recursive Make patterns; a single dependency graph is often clearer. +10. Control verbosity to suit the needs of the project, for example by hiding command echoes and only printing the essential logs. diff --git a/e2e-testing/bridge-sdk-config.example.json b/e2e-testing/bridge-sdk-config.example.json new file mode 100644 index 00000000..7df84713 --- /dev/null +++ b/e2e-testing/bridge-sdk-config.example.json @@ -0,0 +1,25 @@ +{ + "near_rpc": "https://rpc.testnet.near.org/", + "near_token_locker_id": "omni-locker.testnet", + "near_light_client_eth_address": "0x202cdf10bfa45a3d2190901373edd864f071d707", + "near_signer": "omni-sender.testnet", + "near_private_key": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eth_chain_id": 11155111, + "eth_bridge_token_factory_address": "0xa9108f7F83Fb661e611991116D526fCa1a9585ab", + "eth_connector_account_id": "aurora", + "eth_custodian_address": "0xe0320b199863D7f6D5bBa62741aACB312110DEf7", + "base_rpc": "https://base-sepolia.blockpi.network/v1/rpc/public", + "base_chain_id": 84532, + "base_bridge_token_factory_address": "0x0C981337fFe39a555d3A40dbb32f21aD0eF33FFA", + "arb_rpc": "https://arbitrum-sepolia.blockpi.network/v1/rpc/public", + "arb_chain_id": 421614, + "arb_bridge_token_factory_address": "0xd565f7CcE0FA1bB8DBe73FCDA281390d545f6200", + "solana_rpc": "https://api.devnet.solana.com", + "solana_bridge_address": "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", + "solana_wormhole_address": "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", + "solana_keypair": "6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd", + "fast_bridge_account_id": "fastbridge.testnet", + "fast_bridge_address": "0x0B2C4871C9bAD795746C05c1539A8B1f26c26357", + "eth_private_key": "", + "eth_rpc": "" +} \ No newline at end of file diff --git a/e2e-testing/evm_scripts/hardhat.config.ts b/e2e-testing/evm_scripts/hardhat.config.ts index c63937da..af14f2f3 100644 --- a/e2e-testing/evm_scripts/hardhat.config.ts +++ b/e2e-testing/evm_scripts/hardhat.config.ts @@ -108,26 +108,6 @@ const config: HardhatUserConfig = { interval: 0, }, }, - mainnet: { - omniChainId: 0, - chainId: 1, - url: `https://mainnet.infura.io/v3/${INFURA_API_KEY}`, - accounts: [`${EVM_PRIVATE_KEY}`], - }, - arbitrumMainnet: { - wormholeAddress: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46", - omniChainId: 3, - chainId: 42161, - url: `https://arbitrum-mainnet.infura.io/v3/${INFURA_API_KEY}`, - accounts: [`${EVM_PRIVATE_KEY}`], - }, - baseMainnet: { - wormholeAddress: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6", - omniChainId: 4, - chainId: 8453, - url: `https://base-mainnet.infura.io/v3/${INFURA_API_KEY}`, - accounts: [`${EVM_PRIVATE_KEY}`], - }, sepolia: { omniChainId: 0, chainId: 11155111, diff --git a/e2e-testing/makefiles/common.mk b/e2e-testing/makefiles/common.mk new file mode 100644 index 00000000..ec68bfc2 --- /dev/null +++ b/e2e-testing/makefiles/common.mk @@ -0,0 +1,52 @@ +# Common variables and settings +common_testing_root := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))/.. +common_timestamp := $(shell date -u +%Y%m%d-%H%M%S) + +# ASCII box formatting for step descriptions +define description + @echo "|──────────────────────────────────────────────────────────────" + @echo "│ $(1)" + @echo "|──────────────────────────────────────────────────────────────" +endef + +# Progress bar for waiting operations +define progress_wait + @tput civis; \ + for i in $$(seq 1 $(1)); do \ + printf "\033[2K\rWaiting: ["; \ + p=$$((i * 100 / $(1))); \ + for j in $$(seq 1 $$p); do printf "="; done; \ + if [ $$p -lt 100 ]; then printf ">"; fi; \ + for j in $$(seq $$(($$p + 1)) 100); do printf " "; done; \ + printf "] $$p%% ($$i/$(1) seconds)"; \ + sleep 1; \ + done; \ + printf "\n"; \ + tput cnorm +endef + +# Common directories +common_near_deploy_results_dir := $(common_testing_root)/near_deploy_results +common_evm_deploy_results_dir := $(common_testing_root)/evm_deploy_results +common_solana_deploy_results_dir := $(common_testing_root)/solana_deploy_results + +# Common files +common_near_bridge_id_file := $(common_near_deploy_results_dir)/omni_bridge.json +common_bridge_sdk_config_file := $(common_testing_root)/bridge-sdk-config.json + +# Chain identifiers +COMMON_SEPOLIA_CHAIN_ID := 0 +COMMON_SEPOLIA_CHAIN_STR := Eth + +# Create required directories +$(common_near_deploy_results_dir) $(common_evm_deploy_results_dir) $(common_solana_deploy_results_dir): + $(call description,Creating directory to store deploy results: $@) + mkdir -p $@ + +# Clean targets +.PHONY: clean-deploy-results +clean-deploy-results: + $(call description,Cleaning deploy results directories) + rm -rf $(common_near_deploy_results_dir) + rm -rf $(common_evm_deploy_results_dir) + rm -rf $(common_solana_deploy_results_dir) \ No newline at end of file diff --git a/e2e-testing/makefiles/evm.mk b/e2e-testing/makefiles/evm.mk new file mode 100644 index 00000000..c745c4b0 --- /dev/null +++ b/e2e-testing/makefiles/evm.mk @@ -0,0 +1,117 @@ +# EVM-specific variables and rules +evm_dir := $(common_testing_root)/../evm +evm_script_dir := $(common_testing_root)/evm_scripts + +evm_compile_stamp := $(common_testing_root)/.evm-compile.stamp +evm_artifacts_dir := $(common_testing_root)/evm_artifacts +evm_script_compile_stamp := $(common_testing_root)/.evm-scripts-compile.stamp + +evm_networks := sepolia arbitrumSepolia baseSepolia + +# EVM deployment commands +EVM_DEPLOY_TOKEN_IMPL = yarn --silent --cwd $(evm_dir) hardhat deploy-token-impl --network $(1) +EVM_DEPLOY_OMNI_BRIDGE_CONTRACT = yarn --silent --cwd $(evm_dir) hardhat deploy-bridge-token-factory --network $(1) --bridge-token-impl $(2) --near-bridge-account-id $(3) +EVM_DEPLOY_FAKE_PROVER = yarn --silent --cwd $(evm_dir) hardhat deploy-fake-prover --network $(1) +EVM_DEPLOY_ENEAR_PROXY = yarn --silent --cwd $(evm_dir) hardhat deploy-e-near-proxy --network $(1) --enear $(2) + +EVM_DEPLOY_BYTECODE = yarn --silent --cwd $(evm_script_dir) hardhat deploy-bytecode --network $(1) --bytecode $(2) +EVM_DEPLOY_TEST_TOKEN = yarn --silent --cwd $(evm_script_dir) hardhat deploy-test-token --network $(1) --name $(2) --symbol $(3) + +evm_enear_creation_template_file := $(common_testing_root)/bin/eNear_creation.template + +# Clean targets +.PHONY: clean-evm +clean-evm: + $(call description,Cleaning EVM build artifacts) + rm -rf $(evm_artifacts_dir) + rm -f $(evm_compile_stamp) + rm -f $(evm_script_compile_stamp) + +# Build rules +.PHONY: evm-build +evm-build: $(evm_compile_stamp) +$(evm_compile_stamp): + $(call description,Building EVM contracts) + mkdir -p $(evm_artifacts_dir) && \ + yarn --cwd $(evm_dir) install --frozen-lockfile && \ + yarn --cwd $(evm_dir) hardhat compile && \ + cp -r $(evm_dir)/build/* $(evm_artifacts_dir) + touch $@ + +.PHONY: evm-scripts-build +evm-scripts-build: $(evm_script_compile_stamp) +$(evm_script_compile_stamp): + $(call description,Building EVM scripts) + yarn --cwd $(evm_script_dir) install && \ + yarn --cwd $(evm_script_dir) hardhat compile + touch $@ + +# Network-specific deployment rules +define generate_evm_deploy_rules + +$(1)_deploy_results_dir := $(common_evm_deploy_results_dir)/$(1) + +.PHONY: clean-evm-$(1) +clean-evm-$(1): + $(call description,Cleaning EVM deploy results for $(1)) + rm -rf $$($(1)_deploy_results_dir) + +$$($(1)_deploy_results_dir): | $(common_evm_deploy_results_dir) + $(call description,Creating deploy results directory for $(1)) + mkdir -p $$@ + +$(1)_bridge_contract_address_file := $$($(1)_deploy_results_dir)/omni_bridge.json +$(1)_token_impl_address_file := $$($(1)_deploy_results_dir)/token_impl.json +$(1)_fake_prover_address_file := $$($(1)_deploy_results_dir)/fake_prover.json +$(1)_enear_address_file := $$($(1)_deploy_results_dir)/eNear.json +$(1)_enear_proxy_address_file := $$($(1)_deploy_results_dir)/eNearProxy.json +$(1)_enear_creation_file := $$($(1)_deploy_results_dir)/eNear_creation +$(1)_test_token_address_file := $$($(1)_deploy_results_dir)/test_token.json + +.PHONY: $(1)-deploy-fake-prover +$(1)-deploy-fake-prover: $$($(1)_fake_prover_address_file) +$$($(1)_fake_prover_address_file): $(evm_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying fake prover to $(1)) + $$(call EVM_DEPLOY_FAKE_PROVER,$(1)) 2>/dev/stderr 1> $$@ + +$$($(1)_enear_creation_file): $$($(1)_fake_prover_address_file) $(evm_enear_creation_template_file) | $$($(1)_deploy_results_dir) + $(call description,Creating eNear creation file for $(1)) + cat $(evm_enear_creation_template_file) | \ + sed "s//$$(shell cat $$($(1)_fake_prover_address_file) | jq -r .fakeProverAddress | sed 's/^0x//')/" > $$@ + +.PHONY: $(1)-deploy-enear +$(1)-deploy-enear: $$($(1)_enear_address_file) +$$($(1)_enear_address_file): $$($(1)_enear_creation_file) $(evm_script_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying eNear to $(1)) + $$(call EVM_DEPLOY_BYTECODE,$(1),$$<) 2>/dev/stderr 1> $$@ + +.PHONY: $(1)-deploy-enear-proxy +$(1)-deploy-enear-proxy: $$($(1)_enear_proxy_address_file) +$$($(1)_enear_proxy_address_file): $$($(1)_enear_address_file) $(evm_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying eNear proxy to $(1)) + $$(call EVM_DEPLOY_ENEAR_PROXY,$(1),$$(shell cat $$< | jq -r .contractAddress)) 2>/dev/stderr 1> $$@ + +.PHONY: $(1)-deploy-token-impl +$(1)-deploy-token-impl: $$($(1)_token_impl_address_file) +$$($(1)_token_impl_address_file): $(evm_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying token implementation to $(1)) + $$(call EVM_DEPLOY_TOKEN_IMPL,$(1)) 2>/dev/stderr 1> $$@ + +.PHONY: $(1)-deploy-bridge +$(1)-deploy-bridge: $$($(1)_bridge_contract_address_file) +$$($(1)_bridge_contract_address_file): $$($(1)_token_impl_address_file) $(common_near_bridge_id_file) $(evm_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying bridge contract to $(1)) + $$(call EVM_DEPLOY_OMNI_BRIDGE_CONTRACT,$(1),$$(shell cat $$< | jq -r .tokenImplAddress),$$(shell cat $(common_near_bridge_id_file) | jq -r .contract_id)) 2>/dev/stderr 1> $$@ + +.PHONY: $(1)-deploy-test-token +$(1)-deploy-test-token: $$($(1)_test_token_address_file) +$$($(1)_test_token_address_file): $(evm_script_compile_stamp) | $$($(1)_deploy_results_dir) + $(call description,Deploying test token to $(1)) + $$(call EVM_DEPLOY_TEST_TOKEN,$(1),E2ETestToken-$(common_timestamp),E2ETT-$(common_timestamp)) 2>/dev/stderr 1> $$@ + +.PHONY: $(1)-deploy +$(1)-deploy: $(1)-deploy-bridge $(1)-deploy-enear-proxy $(1)-deploy-test-token + +endef + +$(foreach network,$(evm_networks),$(eval $(call generate_evm_deploy_rules,$(network)))) \ No newline at end of file diff --git a/e2e-testing/makefiles/near.mk b/e2e-testing/makefiles/near.mk new file mode 100644 index 00000000..75bfa9ab --- /dev/null +++ b/e2e-testing/makefiles/near.mk @@ -0,0 +1,125 @@ +# NEAR-specific variables and rules +near_dir := $(common_testing_root)/../near +near_binary_dir := $(common_testing_root)/near_artifacts + +near_init_params_file := $(common_testing_root)/near_init_params.json + +# List all expected WASM binaries +near_binaries := evm_prover.wasm omni_bridge.wasm omni_prover.wasm token_deployer.wasm wormhole_omni_prover_proxy.wasm mock_token.wasm +near_binary_paths := $(addprefix $(near_binary_dir)/,$(near_binaries)) + +# List of binaries that require dynamic init args +near_binaries_with_dynamic_args := token_deployer mock_token omni_bridge + +near_deploy_results := $(patsubst $(near_binary_dir)/%.wasm,$(common_near_deploy_results_dir)/%.json,$(near_binary_paths)) + +near_init_account_credentials_file := $(common_near_deploy_results_dir)/omni-init-account.json +near_dao_account_credentials_file := $(common_near_deploy_results_dir)/omni-dao-account.json + +near_prover_dau_grant_call_file := $(common_near_deploy_results_dir)/omni-prover-dau-grant-call.json + +near_evm_prover_setup_call_file := $(common_near_deploy_results_dir)/evm-prover-setup-call.json + +# Clean targets +.PHONY: clean-near +clean-near: + $(call description,Cleaning NEAR build artifacts) + rm -rf $(near_binary_dir) + rm -f $(common_testing_root)/*_dyn_init_args.json + +# Main deployment targets +.PHONY: near-deploy +near-deploy: $(near_deploy_results) + +.PHONY: near-build +near-build: $(near_binary_paths) + +$(near_binary_paths) &: + $(call description,Building NEAR contracts) + $(near_dir)/build.sh --output-dir $(near_binary_dir) + +# Account creation rules +.PHONY: create-near-init-account +create-near-init-account: $(near_init_account_credentials_file) +$(near_init_account_credentials_file): | $(common_near_deploy_results_dir) + $(call description,Creating NEAR init account) + ./scripts/create-near-account.sh omni-init-$(common_timestamp).testnet $@ + +.PHONY: create-dao-account +create-dao-account: $(near_dao_account_credentials_file) +$(near_dao_account_credentials_file): | $(common_near_deploy_results_dir) + $(call description,Creating NEAR DAO account) + ./scripts/create-near-account.sh omni-dao-$(common_timestamp).testnet $@ + +# Contract deployment rules +define generate_near_deploy_rules + +$(1)_name := $$(basename $$(notdir $(1))) + +# Check if the binary requires dynamic init args +ifeq ($$(filter $$($(1)_name),$(near_binaries_with_dynamic_args)),) + +# Rule for binaries without dynamic init args +$(common_near_deploy_results_dir)/$$($(1)_name).json: $(near_init_params_file) $(near_init_account_credentials_file) $(1) | $(common_near_deploy_results_dir) + $(call description,Deploying $$($(1)_name) contract) + ./scripts/deploy-near-contract.sh $$^ $$($(1)_name)-$(common_timestamp).testnet $$@ + +else + +# Rule for binaries with dynamic init args +$(common_near_deploy_results_dir)/$$($(1)_name).json: $(near_init_params_file) $(near_init_account_credentials_file) $(common_testing_root)/$$($(1)_name)_dyn_init_args.json $(1) | $(common_near_deploy_results_dir) + $(call description,Deploying $$($(1)_name) contract with dynamic init args) + ./scripts/deploy-near-contract.sh $$^ $$($(1)_name)-$(common_timestamp).testnet $$@ + +endif + +endef + +$(foreach binary,$(near_binary_paths),$(eval $(call generate_near_deploy_rules,$(binary)))) + +# Dynamic init args generation +$(common_testing_root)/token_deployer_dyn_init_args.json: $(common_near_deploy_results_dir)/omni_bridge.json $(near_init_account_credentials_file) + $(call description,Generating token deployer init args) + CONTROLLER_ADDRESS=$$(jq -r .contract_id $(common_near_deploy_results_dir)/omni_bridge.json) && \ + DAO_ADDRESS=$$(jq -r .account_id $(near_init_account_credentials_file)) && \ + echo "{\"controller\": \"$$CONTROLLER_ADDRESS\", \"dao\": \"$$DAO_ADDRESS\"}" > $@ + +$(common_testing_root)/mock_token_dyn_init_args.json: $(near_init_account_credentials_file) + $(call description,Generating mock token init args) + OWNER_ADDRESS=$$(jq -r .account_id $<) && \ + echo "{\"owner_id\": \"$$OWNER_ADDRESS\"}" > $@ + +$(common_testing_root)/omni_bridge_dyn_init_args.json: $(common_near_deploy_results_dir)/omni_prover.json + $(call description,Generating omni bridge init args) + PROVER_ADDRESS=$$(jq -r .contract_id $<) && \ + echo "{\"prover_account\": \"$$PROVER_ADDRESS\"}" > $@ + + +.PHONY: omni-prover-dau-grant +omni-prover-dau-grant: $(near_prover_dau_grant_call_file) +$(near_prover_dau_grant_call_file): $(near_init_account_credentials_file) $(near_dao_account_credentials_file) $(common_near_deploy_results_dir)/omni_prover.json + $(call description,Granting DAO role to omni prover) + OMNI_PROVER_ACCOUNT_ID=$$(jq -r .contract_id $(common_near_deploy_results_dir)/omni_prover.json) && \ + DAO_ACCOUNT_ID=$$(jq -r .account_id $(near_dao_account_credentials_file)) && \ + ./scripts/call-near-contract.sh -c $$OMNI_PROVER_ACCOUNT_ID \ + -m acl_grant_role \ + -a "{\"role\": \"DAO\", \"account_id\": \"$$DAO_ACCOUNT_ID\"}" \ + -f $(near_init_account_credentials_file) \ + -n testnet 2>&1 | tee $@ && \ + TX_HASH=$$(grep -o 'Transaction ID: [^ ]*' $@ | cut -d' ' -f3) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ + + +.PHONY: evm-prover-setup +evm-prover-setup: $(near_evm_prover_setup_call_file) +$(near_evm_prover_setup_call_file): $(common_near_deploy_results_dir)/omni_prover.json $(common_near_deploy_results_dir)/evm_prover.json $(near_prover_dau_grant_call_file) + $(call description,Setting up EVM prover) + OMNI_PROVER_ACCOUNT_ID=$$(jq -r .contract_id $(common_near_deploy_results_dir)/omni_prover.json) && \ + EVM_PROVER_ACCOUNT_ID=$$(jq -r .contract_id $(common_near_deploy_results_dir)/evm_prover.json) && \ + ./scripts/call-near-contract.sh -c $$OMNI_PROVER_ACCOUNT_ID \ + -m add_prover \ + -a "{\"account_id\": \"$$EVM_PROVER_ACCOUNT_ID\", \"prover_id\": \"$(COMMON_SEPOLIA_CHAIN_STR)\"}" \ + -f $(near_dao_account_credentials_file) \ + -n testnet 2>&1 | tee $@ && \ + TX_HASH=$$(grep -o 'Transaction ID: [^ ]*' $@ | cut -d' ' -f3) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ \ No newline at end of file diff --git a/e2e-testing/makefiles/pipelines/bridge_token_near_to_evm.mk b/e2e-testing/makefiles/pipelines/bridge_token_near_to_evm.mk new file mode 100644 index 00000000..b0f10ae0 --- /dev/null +++ b/e2e-testing/makefiles/pipelines/bridge_token_near_to_evm.mk @@ -0,0 +1,136 @@ +# Pipeline: Bridge NEAR Token to Ethereum +pipeline1_call_dir := $(common_testing_root)/bridge-token-near-to-evm + +# Clean target +.PHONY: clean-bridge-token-near-to-evm +clean-bridge-token-near-to-evm: + $(call description,Cleaning bridge pipeline artifacts) + rm -rf $(pipeline1_call_dir) + +# Account and contract ID files +pipeline1_sender_account_file := $(common_near_deploy_results_dir)/omni-sender.json +pipeline1_bridge_contract_file := $(common_near_deploy_results_dir)/omni_bridge.json +pipeline1_test_token_file := $(common_near_deploy_results_dir)/mock_token.json +pipeline1_relayer_account_file := $(common_near_deploy_results_dir)/omni-relayer.json +pipeline1_token_deployer_file := $(common_near_deploy_results_dir)/token_deployer.json + +# Call files +pipeline1_add_deployer_file := $(pipeline1_call_dir)/00-1_add-deployer-to-locker-call.json +pipeline1_add_factory_file := $(pipeline1_call_dir)/00-2_add-factory-to-locker-call.json +pipeline1_log_metadata_file := $(pipeline1_call_dir)/01_omni-log-metadata-call.json +pipeline1_evm_deploy_token_file := $(pipeline1_call_dir)/02_evm-deploy-token-call.json +pipeline1_near_bind_token_file := $(pipeline1_call_dir)/03_near-bind-token-call.json + +pipeline1_prepare_stamp := $(pipeline1_call_dir)/.prepare.stamp + +$(pipeline1_call_dir): + mkdir -p $@ + +# Account creation rules +.PHONY: create-near-sender +create-near-sender: $(pipeline1_sender_account_file) +$(pipeline1_sender_account_file): | $(common_near_deploy_results_dir) + $(call description,Creating NEAR sender account) + ./scripts/create-near-account.sh omni-sender-$(common_timestamp).testnet $@ + +.PHONY: create-near-relayer +create-near-relayer: $(pipeline1_relayer_account_file) +$(pipeline1_relayer_account_file): | $(common_near_deploy_results_dir) + $(call description,Creating NEAR relayer account) + ./scripts/create-near-account.sh omni-relayer-$(common_timestamp).testnet $@ + +# Main pipeline target +.PHONY: bridge-token-near-to-evm +bridge-token-near-to-evm: near-bind-token + +# Step 0: Prepare token deployment +.PHONY: prepare-token-deployment +prepare-token-deployment: $(pipeline1_prepare_stamp) +$(pipeline1_prepare_stamp): $(pipeline1_add_deployer_file) $(pipeline1_add_factory_file) $(near_evm_prover_setup_call_file) | $(pipeline1_call_dir) + $(call description,Token deployment preparation complete) + touch $@ + +# Step 0.1: Add deployer to locker +.PHONY: add-deployer-to-locker +add-deployer-to-locker: $(pipeline1_add_deployer_file) +$(pipeline1_add_deployer_file): $(pipeline1_token_deployer_file) $(pipeline1_bridge_contract_file) $(near_init_account_credentials_file) | $(pipeline1_call_dir) + $(call description,Bridge NEAR Token to Ethereum. Step 0.1: Adding token deployer to locker) + TOKEN_DEPLOYER_ID=$$(jq -r .contract_id $(pipeline1_token_deployer_file)) && \ + TOKEN_LOCKER_ID=$$(jq -r .contract_id $(pipeline1_bridge_contract_file)) && \ + ./scripts/call-near-contract.sh -c $$TOKEN_LOCKER_ID \ + -m add_token_deployer \ + -a "{\"chain\": \"$(COMMON_SEPOLIA_CHAIN_STR)\", \"account_id\": \"$$TOKEN_DEPLOYER_ID\"}" \ + -f $(near_init_account_credentials_file) \ + -n testnet 2>&1 | tee $@ && \ + TX_HASH=$$(grep -o 'Transaction ID: [^ ]*' $@ | cut -d' ' -f3) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ + +# Step 0.2: Add Ethereum factory to locker +.PHONY: add-ethereum-factory-to-locker +add-ethereum-factory-to-locker: $(pipeline1_add_factory_file) +$(pipeline1_add_factory_file): $(pipeline1_bridge_contract_file) $(near_init_account_credentials_file) $(sepolia_bridge_contract_address_file) | $(pipeline1_call_dir) + $(call description,Bridge NEAR Token to Ethereum. Step 0.2: Adding Ethereum factory to locker) + FACTORY_ADDRESS=$$(jq -r .bridgeAddress $(sepolia_bridge_contract_address_file)) && \ + TOKEN_LOCKER_ID=$$(jq -r .contract_id $(pipeline1_bridge_contract_file)) && \ + ./scripts/call-near-contract.sh -c $$TOKEN_LOCKER_ID \ + -m add_factory \ + -a "{\"address\": \"$$FACTORY_ADDRESS\"}" \ + -f $(near_init_account_credentials_file) \ + -n testnet 2>&1 | tee $@ && \ + TX_HASH=$$(grep -o 'Transaction ID: [^ ]*' $@ | cut -d' ' -f3) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ + +# Step 1: Log metadata +.PHONY: near-log-metadata-call +near-log-metadata-call: $(pipeline1_log_metadata_file) +$(pipeline1_log_metadata_file): $(pipeline1_sender_account_file) $(pipeline1_bridge_contract_file) $(pipeline1_test_token_file) $(pipeline1_prepare_stamp) | $(pipeline1_call_dir) + $(call description,Bridge NEAR Token to Ethereum. Step 1: Logging token metadata) + TOKEN_ID=$$(jq -r .contract_id $(pipeline1_test_token_file)) && \ + SENDER_ACCOUNT_ID=$$(jq -r .account_id $(pipeline1_sender_account_file)) && \ + SENDER_PRIVATE_KEY=$$(jq -r .private_key $(pipeline1_sender_account_file)) && \ + TOKEN_LOCKER_ID=$$(jq -r .contract_id $(pipeline1_bridge_contract_file)) && \ + bridge-cli testnet omni-connector log-metadata \ + --token near:$$TOKEN_ID \ + --near-signer $$SENDER_ACCOUNT_ID \ + --near-private-key $$SENDER_PRIVATE_KEY \ + --near-token-locker-id $$TOKEN_LOCKER_ID \ + --config-file $(common_bridge_sdk_config_file) > $@ && \ + TX_HASH=$$(grep -o 'tx_hash="[^"]*"' $@ | cut -d'"' -f2) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ + +# Step 2: Deploy token on Ethereum +.PHONY: ethereum-deploy-token +ethereum-deploy-token: $(pipeline1_evm_deploy_token_file) +$(pipeline1_evm_deploy_token_file): $(pipeline1_log_metadata_file) $(sepolia_bridge_contract_address_file) | $(pipeline1_call_dir) + $(call description,Bridge NEAR Token to Ethereum. Step 2: Deploying token on Ethereum) + TX_HASH=$$(jq -r .tx_hash $(pipeline1_log_metadata_file)) && \ + ETH_BRIDGE_TOKEN_FACTORY_ADDRESS=$$(jq -r .bridgeAddress $(sepolia_bridge_contract_address_file)) && \ + bridge-cli testnet omni-connector evm-deploy-token \ + --chain $(COMMON_SEPOLIA_CHAIN_STR) \ + --tx-hash $$TX_HASH \ + --eth-bridge-token-factory-address $$ETH_BRIDGE_TOKEN_FACTORY_ADDRESS \ + --config-file $(common_bridge_sdk_config_file) > $@ && \ + TX_HASH=$$(grep -o 'tx_hash="[^"]*"' $@ | cut -d'"' -f2) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ + + +# Step 3: Bind token on NEAR +.PHONY: near-bind-token +near-bind-token: $(pipeline1_near_bind_token_file) +$(pipeline1_near_bind_token_file): $(pipeline1_evm_deploy_token_file) $(pipeline1_relayer_account_file) | $(pipeline1_call_dir) + $(call description,Waiting for Ethereum transaction being captured by relayer) + $(call progress_wait,1300) + $(call description,Bridge NEAR Token to Ethereum. Step 3: Binding token on NEAR) + TX_HASH=$$(jq -r .tx_hash $(pipeline1_evm_deploy_token_file)) && \ + RELAYER_ACCOUNT_ID=$$(jq -r .account_id $(pipeline1_relayer_account_file)) && \ + RELAYER_PRIVATE_KEY=$$(jq -r .private_key $(pipeline1_relayer_account_file)) && \ + TOKEN_LOCKER_ID=$$(jq -r .contract_id $(pipeline1_bridge_contract_file)) && \ + bridge-cli testnet omni-connector near-bind-token \ + --chain $(COMMON_SEPOLIA_CHAIN_STR) \ + --tx-hash $$TX_HASH \ + --near-signer $$RELAYER_ACCOUNT_ID \ + --near-private-key $$RELAYER_PRIVATE_KEY \ + --near-token-locker-id $$TOKEN_LOCKER_ID \ + --config-file $(common_bridge_sdk_config_file) > $@ && \ + TX_HASH=$$(grep -o 'tx_hash="[^"]*"' $@ | cut -d'"' -f2) && \ + echo "{\"tx_hash\": \"$$TX_HASH\"}" > $@ \ No newline at end of file diff --git a/e2e-testing/makefiles/solana.mk b/e2e-testing/makefiles/solana.mk new file mode 100644 index 00000000..f568aa9f --- /dev/null +++ b/e2e-testing/makefiles/solana.mk @@ -0,0 +1,42 @@ +# Solana-specific variables and rules +solana_dir := $(common_testing_root)/../solana + +solana_build_stamp := $(common_testing_root)/.solana-build.stamp +solana_artifacts_dir := $(common_testing_root)/solana_artifacts + +solana_programs := bridge_token_factory +solana_programs_keypairs := $(foreach program,$(solana_programs),$(solana_dir)/$(program)/target/deploy/$(program)-keypair.json) +solana_programs_binaries := $(foreach program,$(solana_programs),$(solana_artifacts_dir)/$(program)/target/deploy/$(program).so) + +# Clean targets +.PHONY: clean-solana +clean-solana: + $(call description,Cleaning Solana build artifacts) + rm -rf $(solana_artifacts_dir) + rm -f $(solana_build_stamp) + +# Main build target +.PHONY: solana-build +solana-build: $(solana_build_stamp) +$(solana_build_stamp): $(solana_programs_keypairs) $(solana_programs_binaries) + $(call description,Solana build complete) + touch $@ + +# Program-specific build rules +define generate_solana_build_rules + +$(solana_dir)/$(1)/target/deploy/$(1)-keypair.json: $(common_testing_root)/$(1)-keypair.json + $(call description,Setting up keypair for $(1)) + mkdir -p $$(dir $$@) && \ + cp $$< $$@ + +$(solana_artifacts_dir)/$(1)/target/deploy/$(1).so: $(solana_dir)/$(1)/target/deploy/$(1)-keypair.json + $(call description,Building Solana program $(1)) + mkdir -p $(solana_artifacts_dir)/$(1) && \ + cd $(solana_dir)/$(1) && \ + anchor build && \ + cp -r $(solana_dir)/$(1)/target/* $(solana_artifacts_dir)/$(1) + +endef + +$(foreach program,$(solana_programs),$(eval $(call generate_solana_build_rules,$(program)))) \ No newline at end of file diff --git a/e2e-testing/near_init_params.json b/e2e-testing/near_init_params.json new file mode 100644 index 00000000..54e1a08b --- /dev/null +++ b/e2e-testing/near_init_params.json @@ -0,0 +1,47 @@ +{ + "evm_prover": { + "init_function": "init", + "init_args": { + "light_client": "client-eth2.sepolia.testnet", + "chain_kind": "Eth" + } + }, + "wormhole_omni_prover_proxy": { + "init_function": "init", + "init_args": { + "prover_account": "prover.sepolia.testnet" + } + }, + "omni_prover": { + "init_function": "init", + "init_args": {} + }, + "omni_bridge": { + "init_function": "new", + "init_args": { + "mpc_signer": "v1.signer-prod.testnet", + "wnear_account_id": "wrap.testnet" + } + }, + "omni_token": { + "init_function": "new", + "init_args": { + "controller": "e2e-dev.testnet", + "metadata": { + "name": "TestE2E", + "symbol": "te2e", + "decimals": 24 + } + } + }, + "token_deployer": { + "init_function": "new", + "init_args": {} + }, + "mock_token": { + "init_function": "new_default_meta", + "init_args": { + "total_supply": "1000000000" + } + } +} \ No newline at end of file diff --git a/e2e-testing/scripts/call-near-contract.sh b/e2e-testing/scripts/call-near-contract.sh new file mode 100755 index 00000000..1ddc9cdc --- /dev/null +++ b/e2e-testing/scripts/call-near-contract.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +usage() { + echo "Usage: $0 [options]" + echo "Options:" + echo " -c CONTRACT_ID Contract ID (required)" + echo " -m METHOD_NAME Method name to call (required)" + echo " -a METHOD_ARGS Method arguments in JSON format (default: {})" + echo " -g GAS Gas amount (default: 100.0 Tgas)" + echo " -d DEPOSIT Deposit amount (default: 0 NEAR)" + echo " -f CREDENTIALS Credentials JSON file with account_id, public_key, and private_key (required)" + echo " -n NETWORK Network to use (default: testnet)" + echo " -h Show this help message" + exit 1 +} + +# Default values +METHOD_ARGS="{}" +GAS="100.0 Tgas" +DEPOSIT="0 NEAR" +NETWORK="testnet" + +# Parse command line arguments +while getopts "c:m:a:g:d:f:n:h" opt; do + case $opt in + c) CONTRACT_ID="$OPTARG" ;; + m) METHOD_NAME="$OPTARG" ;; + a) METHOD_ARGS="$OPTARG" ;; + g) GAS="$OPTARG" ;; + d) DEPOSIT="$OPTARG" ;; + f) CREDENTIALS_FILE="$OPTARG" ;; + n) NETWORK="$OPTARG" ;; + h) usage ;; + ?) usage ;; + esac +done + +# Validate required parameters +if [ -z "$CONTRACT_ID" ]; then + echo "Error: CONTRACT_ID (-c) is required" + usage +fi + +if [ -z "$METHOD_NAME" ]; then + echo "Error: METHOD_NAME (-m) is required" + usage +fi + +if [ -z "$CREDENTIALS_FILE" ]; then + echo "Error: CREDENTIALS_FILE (-f) is required" + usage +fi + +if [ ! -f "$CREDENTIALS_FILE" ]; then + echo "Error: Credentials file $CREDENTIALS_FILE does not exist" + exit 1 +fi + +# Read credentials from file +SIGNER_ACCOUNT_ID=$(jq -r .account_id "$CREDENTIALS_FILE") +SIGNER_PUBLIC_KEY=$(jq -r .public_key "$CREDENTIALS_FILE") +SIGNER_PRIVATE_KEY=$(jq -r .private_key "$CREDENTIALS_FILE") + +if [ -z "$SIGNER_ACCOUNT_ID" ] || [ "$SIGNER_ACCOUNT_ID" = "null" ]; then + echo "Error: account_id not found in credentials file" + exit 1 +fi + +if [ -z "$SIGNER_PUBLIC_KEY" ] || [ "$SIGNER_PUBLIC_KEY" = "null" ] || [ -z "$SIGNER_PRIVATE_KEY" ] || [ "$SIGNER_PRIVATE_KEY" = "null" ]; then + echo "Error: public_key or private_key not found in credentials file" + exit 1 +fi + +echo "Calling contract method: $METHOD_NAME" +if ! near contract call-function as-transaction "$CONTRACT_ID" \ + "$METHOD_NAME" \ + json-args "$METHOD_ARGS" \ + prepaid-gas "$GAS" attached-deposit "$DEPOSIT" \ + sign-as "$SIGNER_ACCOUNT_ID" \ + network-config "$NETWORK" \ + sign-with-plaintext-private-key --signer-public-key "$SIGNER_PUBLIC_KEY" --signer-private-key "$SIGNER_PRIVATE_KEY" \ + send; then + echo "Failed to call method ${METHOD_NAME} on contract ${CONTRACT_ID}" + exit 1 +fi \ No newline at end of file diff --git a/e2e-testing/scripts/create-near-account.sh b/e2e-testing/scripts/create-near-account.sh new file mode 100755 index 00000000..f3f60ae5 --- /dev/null +++ b/e2e-testing/scripts/create-near-account.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +ACCOUNT_ID="$1" +OUTPUT_JSON="$2" + +echo "Creating account for ${ACCOUNT_ID}" + +if ! near account create-account sponsor-by-faucet-service "$ACCOUNT_ID" \ + autogenerate-new-keypair save-to-legacy-keychain network-config testnet create; then + echo "Failed to create account for ${ACCOUNT_ID}" + exit 1 +fi + +# Extract private key from credentials +CREDENTIALS_FILE="$HOME/.near-credentials/testnet/$ACCOUNT_ID.json" +jq -c --arg account_id "$ACCOUNT_ID" '. + {account_id: $account_id}' "$CREDENTIALS_FILE" > "$OUTPUT_JSON" + +echo "Account created successfully, saved to $OUTPUT_JSON" diff --git a/e2e-testing/scripts/deploy-near-contract.sh b/e2e-testing/scripts/deploy-near-contract.sh index dfc89423..6b307c57 100755 --- a/e2e-testing/scripts/deploy-near-contract.sh +++ b/e2e-testing/scripts/deploy-near-contract.sh @@ -1,38 +1,87 @@ #!/usr/bin/env bash set -euo pipefail -# Usage: ./deploy-near-contract.sh -# Example: ./deploy-near-contract.sh ./near_binary/omni_bridge.wasm ./near_deploy_results/omni_bridge.json omni-bridge-20240318-123456.testnet +# Usage: ./deploy-near-contract.sh [] - -if [ "$#" -ne 3 ]; then - echo "Usage: $0 " +if [ "$#" -ne 5 ] && [ "$#" -ne 6 ]; then + echo "Error: Invalid number of arguments" + echo "Usage: $0 [] " exit 1 fi -WASM_PATH="$1" -OUTPUT_JSON="$2" -CONTRACT_ID="$3" -BINARY_NAME=$(basename "$WASM_PATH" .wasm) +if [ "$#" -eq 5 ]; then + NEAR_INIT_PARAMS_FILE="$1" + INIT_ACCOUNT_CREDENTIALS_FILE="$2" + WASM_PATH="$3" + CONTRACT_ID="$4" + OUTPUT_JSON="$5" + DYN_INIT_ARGS_FILE="" +else + NEAR_INIT_PARAMS_FILE="$1" + INIT_ACCOUNT_CREDENTIALS_FILE="$2" + DYN_INIT_ARGS_FILE="$3" + WASM_PATH="$4" + CONTRACT_ID="$5" + OUTPUT_JSON="$6" +fi + +INIT_ACCOUNT_ID=$(jq -r .account_id "$INIT_ACCOUNT_CREDENTIALS_FILE") +INIT_ACCOUNT_PUBLIC_KEY=$(jq -r .public_key "$INIT_ACCOUNT_CREDENTIALS_FILE") +INIT_ACCOUNT_PRIVATE_KEY=$(jq -r .private_key "$INIT_ACCOUNT_CREDENTIALS_FILE") -echo "Deploying ${BINARY_NAME} to ${CONTRACT_ID}" +CONTRACT_NAME=$(basename "$OUTPUT_JSON" .json) + +# Extract init function and merge init args +INIT_FUNCTION=$(jq -rc ".$CONTRACT_NAME.init_function // \"\"" "$NEAR_INIT_PARAMS_FILE") +STATIC_INIT_ARGS=$(jq -rc ".$CONTRACT_NAME.init_args // {}" "$NEAR_INIT_PARAMS_FILE") + +if [ -f "$DYN_INIT_ARGS_FILE" ]; then + DYN_INIT_ARGS=$(cat "$DYN_INIT_ARGS_FILE") +else + DYN_INIT_ARGS="{}" +fi +INIT_ARGS=$(echo "$STATIC_INIT_ARGS $DYN_INIT_ARGS" | jq -s add) + +echo "Creating the contract account" # Create the contract account if ! near account create-account sponsor-by-faucet-service "$CONTRACT_ID" \ autogenerate-new-keypair save-to-keychain network-config testnet create; then - echo "Failed to create account for ${BINARY_NAME}" + echo "Failed to create account for ${CONTRACT_NAME}" exit 1 fi # Delay to allow the account to be created sleep 3 -# Deploy the contract +# Deploy the contract +echo "Deploying the contract" if ! near contract deploy "$CONTRACT_ID" use-file "$WASM_PATH" \ without-init-call network-config testnet sign-with-keychain send; then - echo "Failed to deploy ${BINARY_NAME}" + echo "Failed to deploy ${CONTRACT_NAME}" exit 1 fi +# Delay to allow the account to be deployed +sleep 3 + +# Init the contract only if init function is specified +if [ -n "$INIT_FUNCTION" ]; then + echo "Init the contract" + if ! near contract call-function as-transaction "$CONTRACT_ID" \ + "$INIT_FUNCTION" \ + json-args "$INIT_ARGS" \ + prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' \ + sign-as "$INIT_ACCOUNT_ID" \ + network-config testnet \ + sign-with-plaintext-private-key --signer-public-key "$INIT_ACCOUNT_PUBLIC_KEY" --signer-private-key "$INIT_ACCOUNT_PRIVATE_KEY" \ + send; then + echo "Failed to init ${CONTRACT_NAME}" + exit 1 + fi +else + echo "No init function specified, skipping contract initialization" +fi + echo "{\"contract_id\": \"$CONTRACT_ID\"}" > "$OUTPUT_JSON" echo "Deployment successful, saved to $OUTPUT_JSON" \ No newline at end of file diff --git a/near/res/evm_prover.wasm b/near/res/evm_prover.wasm index f408fa8a..d62777b9 100755 Binary files a/near/res/evm_prover.wasm and b/near/res/evm_prover.wasm differ diff --git a/near/res/omni_bridge.wasm b/near/res/omni_bridge.wasm index f97774a3..35019648 100755 Binary files a/near/res/omni_bridge.wasm and b/near/res/omni_bridge.wasm differ diff --git a/near/res/wormhole_omni_prover_proxy.wasm b/near/res/wormhole_omni_prover_proxy.wasm index 1193a266..12f0dd48 100755 Binary files a/near/res/wormhole_omni_prover_proxy.wasm and b/near/res/wormhole_omni_prover_proxy.wasm differ