diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f5030b1152..7a400b3f0b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -53,6 +53,10 @@ on: type: boolean required: false default: false + v2-migration-test: + type: boolean + required: false + default: false concurrency: group: e2e-${{ github.head_ref || github.sha }} @@ -74,6 +78,7 @@ jobs: TSS_MIGRATION_TESTS: ${{ steps.matrix-conditionals.outputs.TSS_MIGRATION_TESTS }} SOLANA_TESTS: ${{ steps.matrix-conditionals.outputs.SOLANA_TESTS }} V2_TESTS: ${{ steps.matrix-conditionals.outputs.V2_TESTS }} + V2_MIGRATION_TESTS: ${{ steps.matrix-conditionals.outputs.V2_MIGRATION_TESTS }} steps: # use api rather than event context to avoid race conditions (label added after push) - id: matrix-conditionals @@ -98,6 +103,7 @@ jobs: core.setOutput('TSS_MIGRATION_TESTS', labels.includes('TSS_MIGRATION_TESTS')); core.setOutput('SOLANA_TESTS', labels.includes('SOLANA_TESTS')); core.setOutput('V2_TESTS', labels.includes('V2_TESTS')); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) + core.setOutput('V2_MIGRATION_TESTS', labels.includes('V2_MIGRATION_TESTS')); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'merge_group') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_LIGHT_TESTS', true); @@ -112,6 +118,7 @@ jobs: core.setOutput('PERFORMANCE_TESTS', true); core.setOutput('STATEFUL_DATA_TESTS', true); core.setOutput('V2_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) + core.setOutput('V2_MIGRATION_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'schedule') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_TESTS', true); @@ -123,6 +130,7 @@ jobs: core.setOutput('TSS_MIGRATION_TESTS', true); core.setOutput('SOLANA_TESTS', true); core.setOutput('V2_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) + core.setOutput('V2_MIGRATION_TESTS', true); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } else if (context.eventName === 'workflow_dispatch') { core.setOutput('DEFAULT_TESTS', context.payload.inputs['default-test']); core.setOutput('UPGRADE_TESTS', context.payload.inputs['upgrade-test']); @@ -134,6 +142,7 @@ jobs: core.setOutput('TSS_MIGRATION_TESTS', context.payload.inputs['tss-migration-test']); core.setOutput('SOLANA_TESTS', context.payload.inputs['solana-test']); core.setOutput('V2_TESTS', context.payload.inputs['v2-test']); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) + core.setOutput('V2_MIGRATION_TESTS', context.payload.inputs['v2-migration-test']); // for v2 tests, TODO: remove this once we fully migrate to v2 (https://github.com/zeta-chain/node/issues/2627) } e2e: @@ -174,6 +183,9 @@ jobs: - make-target: "start-v2-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.V2_TESTS == 'true' }} + - make-target: "start-upgrade-v2-migration-test" + runs-on: ubuntu-20.04 + run: ${{ needs.matrix-conditionals.outputs.V2_MIGRATION_TESTS == 'true' }} name: ${{ matrix.make-target }} uses: ./.github/workflows/reusable-e2e.yml with: diff --git a/Makefile b/Makefile index d6a16617a7..2bdcc7b0d8 100644 --- a/Makefile +++ b/Makefile @@ -320,7 +320,7 @@ start-upgrade-v2-migration-test: zetanode-upgrade @echo "--> Starting v2 migration upgrade test" export LOCALNET_MODE=upgrade && \ export UPGRADE_HEIGHT=90 && \ - export E2E_ARGS="--skip-regular --test-v2-migration --verbose" && \ + export E2E_ARGS="--test-v2-migration" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile upgrade -f docker-compose.yml -f docker-compose-upgrade.yml up -d diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 98410bc368..6b1b9f20da 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -41,6 +41,22 @@ additional_accounts: bech32_address: "zeta1pvtxa708yvdmszn687nne6nl8qn704daf420xz" evm_address: "0x0B166ef9e7231Bb80A7A3FA73CEA7F3827E7D5BD" private_key: "0bcc2fa28b526f90e1d54648d612db901e860bf68248555593f91ea801c6b482" + user_v2_ether: + bech32_address: "zeta1erlqlpl5da7a9r3emzw60kax9fxc3h0r3z7c5e" + evm_address: "0xC8fe0F87f46F7Dd28e39D89Da7Dba62A4D88dde3" + private_key: "11c25af71c82602a681ce622bf76f4f0fbc3b7f23ce935db6249d1517322f436" + user_v2_erc20: + bech32_address: "zeta12wp6syndml6jd32m7f9mn2wscsxz6cff8nczl4" + evm_address: "0x5383A8126ddff526C55bF24Bb9a9D0c40c2d6129" + private_key: "77b0e4dcc29c5c47b6999dabd42abcfdf7750ccc86d6659c1373ec1ea3b4af6c" + user_v2_ether_revert: + bech32_address: "zeta1m7m5xd79x9qmlyfpqxcwuac04r3dewfpdcfw5e" + evm_address: "0xdFb74337c53141bf912101b0Ee770FA8e2DCB921" + private_key: "be7098604cc40f95d68298a3b4ae13972ac8a3df271ba19ddf169070d30e8ba8" + user_v2_erc20_revert: + bech32_address: "zeta1nry9yeg6njhjrp2ctppa8558vqxal9fxk69zxg" + evm_address: "0x98c852651A9CAF2185585843d3D287600Ddf9526" + private_key: "bf9456c679bb5a952a9a137fcfc920e0413efdb97c36de1e57455763084230cb" policy_accounts: emergency_policy_account: bech32_address: "zeta16m2cnrdwtgweq4njc6t470vl325gw4kp6s7tap" diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go index 772da557c7..b38be32868 100644 --- a/cmd/zetae2e/local/v2.go +++ b/cmd/zetae2e/local/v2.go @@ -15,7 +15,7 @@ import ( // startV2Tests starts v2 related tests in parallel func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner.E2ERunner, verbose bool) { // Test happy paths for gas token workflow - eg.Go(v2TestRoutine(conf, "eth", conf.AdditionalAccounts.UserEther, color.FgHiGreen, deployerRunner, verbose, + eg.Go(v2TestRoutine(conf, "eth", conf.AdditionalAccounts.UserV2Ether, color.FgHiGreen, deployerRunner, verbose, e2etests.TestV2ETHDepositName, e2etests.TestV2ETHDepositAndCallName, e2etests.TestV2ETHWithdrawName, @@ -25,7 +25,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner )) // Test happy paths for erc20 token workflow - eg.Go(v2TestRoutine(conf, "erc20", conf.AdditionalAccounts.UserERC20, color.FgHiBlue, deployerRunner, verbose, + eg.Go(v2TestRoutine(conf, "erc20", conf.AdditionalAccounts.UserV2ERC20, color.FgHiBlue, deployerRunner, verbose, e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM e2etests.TestV2ERC20DepositName, e2etests.TestV2ERC20DepositAndCallName, @@ -38,7 +38,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner v2TestRoutine( conf, "eth-revert", - conf.AdditionalAccounts.UserZetaTest, + conf.AdditionalAccounts.UserV2EtherRevert, color.FgHiYellow, deployerRunner, verbose, @@ -55,7 +55,7 @@ func startV2Tests(eg *errgroup.Group, conf config.Config, deployerRunner *runner v2TestRoutine( conf, "erc20-revert", - conf.AdditionalAccounts.UserBitcoin, + conf.AdditionalAccounts.UserV2ERC20Revert, color.FgHiRed, deployerRunner, verbose, diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index 4ee8192c6d..31e703cec2 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -91,6 +91,27 @@ address=$(yq -r '.additional_accounts.user_migration.evm_address' config.yml) echo "funding migration tester address ${address} with 10000 Ether" geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null +# unlock v2 ethers tests accounts +address=$(yq -r '.additional_accounts.user_v2_ether.evm_address' config.yml) +echo "funding v2 ethers tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 erc20 tests accounts +address=$(yq -r '.additional_accounts.user_v2_erc20.evm_address' config.yml) +echo "funding v2 erc20 tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 ethers revert tests accounts +address=$(yq -r '.additional_accounts.user_v2_ether_revert.evm_address' config.yml) +echo "funding v2 ethers revert tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + +# unlock v2 erc20 revert tests accounts +address=$(yq -r '.additional_accounts.user_v2_erc20_revert.evm_address' config.yml) +echo "funding v2 erc20 revert tester address ${address} with 10000 Ether" +geth --exec "eth.sendTransaction({from: eth.coinbase, to: '${address}', value: web3.toWei(10000,'ether')})" attach http://eth:8545 > /dev/null + + # unlock local solana relayer accounts if host solana > /dev/null; then solana_url=$(yq -r '.rpcs.solana' config.yml) diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 14980d195f..f1d3e11872 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -254,6 +254,18 @@ then # migration tester address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 ether tester + address=$(yq -r '.additional_accounts.user_v2_ether.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 erc20 tester + address=$(yq -r '.additional_accounts.user_v2_erc20.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 ether revert tester + address=$(yq -r '.additional_accounts.user_v2_ether_revert.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta +# v2 erc20 revert tester + address=$(yq -r '.additional_accounts.user_v2_erc20_revert.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta # 3. Copy the genesis.json to all the nodes .And use it to create a gentx for every node zetacored gentx operator 1000000000000000000000azeta --chain-id=$CHAINID --keyring-backend=$KEYRING --gas-prices 20000000000azeta diff --git a/e2e/config/config.go b/e2e/config/config.go index 7e721bcdd3..ded3755d65 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -61,15 +61,19 @@ type Account struct { // AdditionalAccounts are extra accounts required to run specific tests type AdditionalAccounts struct { - UserERC20 Account `yaml:"user_erc20"` - UserZetaTest Account `yaml:"user_zeta_test"` - UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` - UserBitcoin Account `yaml:"user_bitcoin"` - UserSolana Account `yaml:"user_solana"` - UserEther Account `yaml:"user_ether"` - UserMisc Account `yaml:"user_misc"` - UserAdmin Account `yaml:"user_admin"` - UserMigration Account `yaml:"user_migration"` + UserERC20 Account `yaml:"user_erc20"` + UserZetaTest Account `yaml:"user_zeta_test"` + UserZEVMMPTest Account `yaml:"user_zevm_mp_test"` + UserBitcoin Account `yaml:"user_bitcoin"` + UserSolana Account `yaml:"user_solana"` + UserEther Account `yaml:"user_ether"` + UserMisc Account `yaml:"user_misc"` + UserAdmin Account `yaml:"user_admin"` + UserMigration Account `yaml:"user_migration"` // used for TSS migration + UserV2Ether Account `yaml:"user_v2_ether"` + UserV2ERC20 Account `yaml:"user_v2_erc20"` + UserV2EtherRevert Account `yaml:"user_v2_ether_revert"` + UserV2ERC20Revert Account `yaml:"user_v2_erc20_revert"` } type PolicyAccounts struct { @@ -225,6 +229,10 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserMisc, a.UserAdmin, a.UserMigration, + a.UserV2Ether, + a.UserV2ERC20, + a.UserV2EtherRevert, + a.UserV2ERC20Revert, } } @@ -317,6 +325,22 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } + c.AdditionalAccounts.UserV2Ether, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2ERC20, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2EtherRevert, err = generateAccount() + if err != nil { + return err + } + c.AdditionalAccounts.UserV2ERC20Revert, err = generateAccount() + if err != nil { + return err + } c.PolicyAccounts.EmergencyPolicyAccount, err = generateAccount() if err != nil { diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 3b5e7f8d90..8c91bc546e 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -188,8 +188,9 @@ func (r *E2ERunner) ApproveETHZRC20(allowed ethcommon.Address) { allowance, err := r.ETHZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), r.GatewayEVMAddr) require.NoError(r, err) - // approve 1M*1e18 if allowance is zero - if allowance.Cmp(big.NewInt(0)) == 0 { + // approve 1M*1e18 if allowance is below 1k + thousand := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) + if allowance.Cmp(thousand) < 0 { tx, err := r.ETHZRC20.Approve(r.ZEVMAuth, allowed, big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000000))) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) @@ -204,8 +205,9 @@ func (r *E2ERunner) ApproveERC20ZRC20(allowed ethcommon.Address) { allowance, err := r.ERC20ZRC20.Allowance(&bind.CallOpts{}, r.Account.EVMAddress(), r.GatewayEVMAddr) require.NoError(r, err) - // approve 1M*1e18 if allowance is zero - if allowance.Cmp(big.NewInt(0)) == 0 { + // approve 1M*1e18 if allowance is below 1k + thousand := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) + if allowance.Cmp(thousand) < 0 { tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, allowed, big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000000))) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) diff --git a/e2e/runner/v2_migration.go b/e2e/runner/v2_migration.go index 00d978e90b..d54ba1a2e2 100644 --- a/e2e/runner/v2_migration.go +++ b/e2e/runner/v2_migration.go @@ -34,14 +34,14 @@ func (r *E2ERunner) RunV2Migration() { r.Logger.Info("Part 2: Deploying v2 contracts on EVM chain") r.SetupEVMV2() - // Part 3: deploy gateway on ZetaChain - r.Logger.Info("Part 3: Deploying Gateway ZEVM") - r.SetZEVMContractsV2() - - // Part 4: upgrade all ZRC20s - r.Logger.Info("Part 4: Upgrading ZRC20s") + // Part 3: upgrade all ZRC20s + r.Logger.Info("Part 3: Upgrading ZRC20s") r.upgradeZRC20s() + // Part 4: deploy gateway on ZetaChain + r.Logger.Info("Part 4: Deploying Gateway ZEVM") + r.SetZEVMContractsV2() + // Part 5: migrate ERC20 custody funds r.Logger.Info("Part 5: Migrating ERC20 custody funds") r.migrateERC20CustodyFunds() @@ -99,7 +99,7 @@ func (r *E2ERunner) upgradeZRC20( coinType, big.NewInt(100_000), r.SystemContractAddr, - r.GatewayZEVMAddr, + r.SystemContractAddr, // gateway is not deployed yet, gateway will be set during MsgUpdateGatewayContract phase by the protocol ) require.NoError(r, err) diff --git a/e2e/runner/v2_setup_evm.go b/e2e/runner/v2_setup_evm.go index 0efa4af99f..cbacc59550 100644 --- a/e2e/runner/v2_setup_evm.go +++ b/e2e/runner/v2_setup_evm.go @@ -104,5 +104,10 @@ func (r *E2ERunner) SetupEVMV2() { txWhitelist, err := r.ERC20CustodyV2.Whitelist(r.EVMAuth, r.ERC20Addr) require.NoError(r, err) + // set legacy supported (calling deposit directly in ERC20Custody) + txSetLegacySupported, err := r.ERC20CustodyV2.SetSupportsLegacy(r.EVMAuth, true) + require.NoError(r, err) + ensureTxReceipt(txWhitelist, "ERC20 whitelist failed") + ensureTxReceipt(txSetLegacySupported, "Set legacy support failed") } diff --git a/x/fungible/keeper/msg_server_update_gateway_contract.go b/x/fungible/keeper/msg_server_update_gateway_contract.go index 1fd3b95021..05ad086d75 100644 --- a/x/fungible/keeper/msg_server_update_gateway_contract.go +++ b/x/fungible/keeper/msg_server_update_gateway_contract.go @@ -5,6 +5,8 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/fungible/types" @@ -21,6 +23,16 @@ func (k msgServer) UpdateGatewayContract( return nil, cosmoserrors.Wrap(authoritytypes.ErrUnauthorized, err.Error()) } + // parse the new gateway address + gatewayAddr := ethcommon.HexToAddress(msg.NewGatewayContractAddress) + if gatewayAddr == (ethcommon.Address{}) { + return nil, cosmoserrors.Wrapf( + sdkerrors.ErrInvalidAddress, + "invalid gateway contract address (%s)", + msg.NewGatewayContractAddress, + ) + } + // The SystemContract state variable tracks the contract addresses used by the protocol // This variable is planned to be renamed ProtocolContracts in the future: // https://github.com/zeta-chain/node/issues/2576 @@ -32,10 +44,30 @@ func (k msgServer) UpdateGatewayContract( } oldGateway := protocolContracts.Gateway - // update address and save + // update all ZRC20 contracts with the new gateway address + foreignCoins := k.GetAllForeignCoins(ctx) + for _, fcoin := range foreignCoins { + zrc20Addr := ethcommon.HexToAddress(fcoin.Zrc20ContractAddress) + if zrc20Addr == (ethcommon.Address{}) { + k.Logger(ctx).Error("invalid zrc20 contract address", "address", fcoin.Zrc20ContractAddress) + continue + } + + _, err := k.CallUpdateGatewayAddress(ctx, zrc20Addr, gatewayAddr) + if err != nil { + return nil, cosmoserrors.Wrapf( + err, + "failed to call updateSystemContractAddress for ZRC20 (%s)", + fcoin.Zrc20ContractAddress, + ) + } + } + + // update in the store address and save protocolContracts.Gateway = msg.NewGatewayContractAddress k.SetSystemContract(ctx, protocolContracts) + // emit event err = ctx.EventManager().EmitTypedEvent( &types.EventGatewayContractUpdated{ MsgTypeUrl: sdk.MsgTypeURL(&types.MsgUpdateGatewayContract{}), diff --git a/x/fungible/keeper/msg_server_update_gateway_contract_test.go b/x/fungible/keeper/msg_server_update_gateway_contract_test.go index 70b0a1144c..f28b1921f3 100644 --- a/x/fungible/keeper/msg_server_update_gateway_contract_test.go +++ b/x/fungible/keeper/msg_server_update_gateway_contract_test.go @@ -1,6 +1,9 @@ package keeper_test import ( + "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" + "github.com/zeta-chain/zetacore/pkg/chains" "testing" "github.com/stretchr/testify/require" @@ -12,46 +15,81 @@ import ( ) func TestKeeper_UpdateGatewayContract(t *testing.T) { - t.Run("can update the gateway contract address stored in the module", func(t *testing.T) { - // ARRANGE - k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ - UseAuthorityMock: true, - }) - - msgServer := keeper.NewMsgServerImpl(*k) - k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - admin := sample.AccAddress() + t.Run( + "can update the gateway contract address stored in the module and update address in ZRC20s", + func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) - authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() - systemContractAddr := sample.EthAddress() - connectorAddr := sample.EthAddress() - k.SetSystemContract(ctx, types.SystemContract{ - SystemContract: systemContractAddr.Hex(), - ConnectorZevm: connectorAddr.Hex(), - Gateway: sample.EthAddress().Hex(), - }) + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + authorityMock.On("GetAdditionalChainList", ctx).Return([]chains.Chain{}) + + // setup gas coins for two chains + defaultChains := chains.DefaultChainsList() + require.True(t, len(defaultChains) > 1) + require.NotNil(t, defaultChains[0]) + require.NotNil(t, defaultChains[1]) + chainID1 := defaultChains[0].ChainId + chainID2 := defaultChains[1].ChainId + _, _, _, connectorAddr, systemContractAddr := deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + gas1 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID1, "foo", "foo") + gas2 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID2, "bar", "bar") + queryZRC20Gateway := func(contract common.Address) string { + abi, err := zrc20.ZRC20MetaData.GetAbi() + require.NoError(t, err) + res, err := k.CallEVM( + ctx, + *abi, + types.ModuleAddressEVM, + contract, + keeper.BigIntZero, + nil, + false, + false, + "gatewayAddress", + ) + require.NoError(t, err) + unpacked, err := abi.Unpack("gatewayAddress", res.Ret) + require.NoError(t, err) + address, ok := unpacked[0].(common.Address) + require.True(t, ok) + return address.Hex() + } + + // new gateway address + newGatewayAddr := sample.EthAddress() + require.NotEqual(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas1)) + require.NotEqual(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas2)) - newGatewayAddr := sample.EthAddress() + msg := types.NewMsgUpdateGatewayContract(admin, newGatewayAddr.Hex()) + keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) - msg := types.NewMsgUpdateGatewayContract(admin, newGatewayAddr.Hex()) - keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) + // ACT + _, err := msgServer.UpdateGatewayContract(ctx, msg) - // ACT - _, err := msgServer.UpdateGatewayContract(ctx, msg) + // ASSERT + require.NoError(t, err) + sc, found := k.GetSystemContract(ctx) + require.True(t, found) - // ASSERT - require.NoError(t, err) - sc, found := k.GetSystemContract(ctx) - require.True(t, found) + // gateway is updated + require.EqualValues(t, newGatewayAddr.Hex(), sc.Gateway) - // gateway is updated - require.EqualValues(t, newGatewayAddr.Hex(), sc.Gateway) + // system contract and connector remain the same + require.EqualValues(t, systemContractAddr.Hex(), sc.SystemContract) + require.EqualValues(t, connectorAddr.Hex(), sc.ConnectorZevm) - // system contract and connector remain the same - require.EqualValues(t, systemContractAddr.Hex(), sc.SystemContract) - require.EqualValues(t, connectorAddr.Hex(), sc.ConnectorZevm) - }) + // gateway address in ZRC20s is updated + require.EqualValues(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas1)) + require.EqualValues(t, newGatewayAddr.Hex(), queryZRC20Gateway(gas2)) + }, + ) t.Run( "can update and overwrite the gateway contract if system contract state variable not found", @@ -104,15 +142,37 @@ func TestKeeper_UpdateGatewayContract(t *testing.T) { admin := sample.AccAddress() authorityMock := keepertest.GetFungibleAuthorityMock(t, k) - msg := types.NewMsgUpdateSystemContract(admin, sample.EthAddress().Hex()) + msg := types.NewMsgUpdateGatewayContract(admin, sample.EthAddress().Hex()) keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, authoritytypes.ErrUnauthorized) // ACT - _, err := msgServer.UpdateSystemContract(ctx, msg) + _, err := msgServer.UpdateGatewayContract(ctx, msg) // ASSERT require.Error(t, err) require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) }) + t.Run("should prevent update the gateway contract if invalid gateway address", func(t *testing.T) { + // ARRANGE + k, ctx, _, _ := keepertest.FungibleKeeperWithMocks(t, keepertest.FungibleMockOptions{ + UseAuthorityMock: true, + }) + + msgServer := keeper.NewMsgServerImpl(*k) + k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + admin := sample.AccAddress() + + authorityMock := keepertest.GetFungibleAuthorityMock(t, k) + + msg := types.NewMsgUpdateGatewayContract(admin, "invalid") + keepertest.MockCheckAuthorization(&authorityMock.Mock, msg, nil) + + // ACT + _, err := msgServer.UpdateGatewayContract(ctx, msg) + + // ASSERT + require.Error(t, err) + require.Contains(t, err.Error(), "invalid gateway contract address") + }) } diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go index e77c04b580..48d65ee700 100644 --- a/x/fungible/keeper/v2_evm.go +++ b/x/fungible/keeper/v2_evm.go @@ -9,11 +9,38 @@ import ( "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" "github.com/zeta-chain/protocol-contracts/v2/pkg/revert.sol" "github.com/zeta-chain/protocol-contracts/v2/pkg/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/crypto" "github.com/zeta-chain/zetacore/x/fungible/types" ) +// CallUpdateGatewayAddress calls the updateGatewayAddress function on the ZRC20 contract +// function updateGatewayAddress(address addr) +func (k Keeper) CallUpdateGatewayAddress( + ctx sdk.Context, + zrc20Address common.Address, + newGatewayAddress common.Address, +) (*evmtypes.MsgEthereumTxResponse, error) { + zrc20ABI, err := zrc20.ZRC20MetaData.GetAbi() + if err != nil { + return nil, err + } + + return k.CallEVM( + ctx, + *zrc20ABI, + types.ModuleAddressEVM, + zrc20Address, + BigIntZero, + nil, + true, + false, + "updateGatewayAddress", + newGatewayAddress, + ) +} + // CallDepositAndCallZRC20 calls the depositAndCall (ZRC20 version) function on the gateway contract // Callable only by the fungible module account // returns directly CallEVM() @@ -166,13 +193,14 @@ func (k Keeper) CallExecuteRevert( // CallDepositAndRevert calls the depositAndRevert function on the gateway contract // -//function depositAndRevert( +// function depositAndRevert( +// // address zrc20, // uint256 amount, // address target, // RevertContext revertContext -//) - +// +// ) func (k Keeper) CallDepositAndRevert( ctx sdk.Context, zrc20 common.Address, diff --git a/zetaclient/chains/evm/observer/v2_inbound.go b/zetaclient/chains/evm/observer/v2_inbound.go index efbc19479b..88ddcf8017 100644 --- a/zetaclient/chains/evm/observer/v2_inbound.go +++ b/zetaclient/chains/evm/observer/v2_inbound.go @@ -90,11 +90,6 @@ func (ob *Observer) ObserveGatewayDeposit(ctx context.Context, startBlock, toBlo // increment prom counter metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() - // TODO: remove in this PR - ob.Logger(). - Inbound.Info(). - Msgf("ObserveGatewayDeposit: observing deposits with gateway contract %s", gatewayAddr.Hex()) - // post to zetacore lastScanned := uint64(0) for _, event := range events { diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index c77d4e36e6..a25c3962f5 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -115,9 +115,10 @@ func (signer *Signer) SetERC20CustodyAddress(addr ethcommon.Address) { } // SetGatewayAddress sets the gateway address -func (signer *Signer) SetGatewayAddress(_ string) { - // Note: do nothing for now - // gateway address will be needed in the future contract architecture +func (signer *Signer) SetGatewayAddress(addr string) { + signer.Lock() + defer signer.Unlock() + signer.gatewayAddress = ethcommon.HexToAddress(addr) } // GetZetaConnectorAddress returns the zeta connector address diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index 4837a8c1cb..7c7df03ca8 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -174,7 +174,7 @@ func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (inte signer.SetERC20CustodyAddress(erc20CustodyAddress) oc.logger.Info(). Str("signer.erc20_custody", erc20CustodyAddress.String()). - Msgf("updated zeta connector address for chain %d", chainID) + Msgf("updated erc20 custody address for chain %d", chainID) } if params.GatewayAddress != signer.GetGatewayAddress() { signer.SetGatewayAddress(params.GatewayAddress) @@ -374,17 +374,6 @@ func (oc *Orchestrator) runScheduler(ctx context.Context) error { chainID := chain.ID() - // get cctxs from map and set pending transactions prometheus gauge - cctxList := cctxMap[chainID] - - metrics.PendingTxsPerChain. - WithLabelValues(chain.Name()). - Set(float64(len(cctxList))) - - if len(cctxList) == 0 { - continue - } - // update chain parameters for signer and chain observer signer, err := oc.resolveSigner(app, chainID) if err != nil { @@ -400,6 +389,17 @@ func (oc *Orchestrator) runScheduler(ctx context.Context) error { continue } + // get cctxs from map and set pending transactions prometheus gauge + cctxList := cctxMap[chainID] + + metrics.PendingTxsPerChain. + WithLabelValues(chain.Name()). + Set(float64(len(cctxList))) + + if len(cctxList) == 0 { + continue + } + if !app.IsOutboundObservationEnabled() { continue }