diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d11e422b21..f5030b1152 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -49,6 +49,10 @@ on: type: boolean required: false default: false + v2-test: + type: boolean + required: false + default: false concurrency: group: e2e-${{ github.head_ref || github.sha }} @@ -69,6 +73,7 @@ jobs: STATEFUL_DATA_TESTS: ${{ steps.matrix-conditionals.outputs.STATEFUL_DATA_TESTS }} 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 }} steps: # use api rather than event context to avoid race conditions (label added after push) - id: matrix-conditionals @@ -92,6 +97,7 @@ jobs: core.setOutput('STATEFUL_DATA_TESTS', labels.includes('STATEFUL_DATA_TESTS')); 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) } else if (context.eventName === 'merge_group') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_LIGHT_TESTS', true); @@ -105,6 +111,7 @@ jobs: core.setOutput('ADMIN_TESTS', true); 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) } else if (context.eventName === 'schedule') { core.setOutput('DEFAULT_TESTS', true); core.setOutput('UPGRADE_TESTS', true); @@ -115,6 +122,7 @@ jobs: core.setOutput('STATEFUL_DATA_TESTS', true); 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) } else if (context.eventName === 'workflow_dispatch') { core.setOutput('DEFAULT_TESTS', context.payload.inputs['default-test']); core.setOutput('UPGRADE_TESTS', context.payload.inputs['upgrade-test']); @@ -125,6 +133,7 @@ jobs: core.setOutput('STATEFUL_DATA_TESTS', context.payload.inputs['stateful-data-test']); 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) } e2e: @@ -162,6 +171,9 @@ jobs: - make-target: "start-solana-test" runs-on: ubuntu-20.04 run: ${{ needs.matrix-conditionals.outputs.SOLANA_TESTS == 'true' }} + - make-target: "start-v2-test" + runs-on: ubuntu-20.04 + run: ${{ needs.matrix-conditionals.outputs.V2_TESTS == 'true' }} name: ${{ matrix.make-target }} uses: ./.github/workflows/reusable-e2e.yml with: diff --git a/Makefile b/Makefile index 0e86d8be76..51931ea89b 100644 --- a/Makefile +++ b/Makefile @@ -271,6 +271,11 @@ start-solana-test: zetanode solana export E2E_ARGS="--skip-regular --test-solana" && \ cd contrib/localnet/ && $(DOCKER_COMPOSE) --profile solana -f docker-compose.yml up -d +start-v2-test: zetanode + @echo "--> Starting e2e smart contracts v2 test" + export E2E_ARGS="--skip-regular --test-v2" && \ + cd contrib/localnet/ && $(DOCKER_COMPOSE) -f docker-compose.yml up -d + ############################################################################### ### Upgrade Tests ### ############################################################################### diff --git a/changelog.md b/changelog.md index dfd2066f08..9c8e4bef01 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ * [2578](https://github.com/zeta-chain/node/pull/2578) - add Gateway address in protocol contract list * [2630](https://github.com/zeta-chain/node/pull/2630) - implement `MsgMigrateERC20CustodyFunds` to migrate the funds from the ERC20Custody to a new contracts (to be used for the new ERC20Custody contract for smart contract V2) * [2578](https://github.com/zeta-chain/node/pull/2578) - Add Gateway address in protocol contract list +* [2594](https://github.com/zeta-chain/node/pull/2594) - Integrate Protocol Contracts V2 in the protocol * [2634](https://github.com/zeta-chain/node/pull/2634) - add support for EIP-1559 gas fees * [2597](https://github.com/zeta-chain/node/pull/2597) - Add generic rpc metrics to zetaclient * [2538](https://github.com/zeta-chain/node/pull/2538) - add background worker routines to shutdown zetaclientd when needed for tss migration diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index 8a91bc1f86..ce1b03717b 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -88,5 +88,13 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C conf.Contracts.ZEVM.ContextAppAddr = config.DoubleQuotedString(r.ContextAppAddr.Hex()) conf.Contracts.ZEVM.TestDappAddr = config.DoubleQuotedString(r.ZevmTestDAppAddr.Hex()) + // v2 + conf.Contracts.EVM.Gateway = config.DoubleQuotedString(r.GatewayEVMAddr.Hex()) + conf.Contracts.EVM.ERC20CustodyNew = config.DoubleQuotedString(r.ERC20CustodyV2Addr.Hex()) + conf.Contracts.EVM.TestDAppV2Addr = config.DoubleQuotedString(r.TestDAppV2EVMAddr.Hex()) + + conf.Contracts.ZEVM.Gateway = config.DoubleQuotedString(r.GatewayZEVMAddr.Hex()) + conf.Contracts.ZEVM.TestDAppV2Addr = config.DoubleQuotedString(r.TestDAppV2ZEVMAddr.Hex()) + return conf } diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index aa1e541957..0d554ef480 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -4,21 +4,25 @@ import ( "fmt" "github.com/gagliardetto/solana-go" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - zetaeth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" - uniswapv2router "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + zetaeth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" + uniswapv2router "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" "github.com/zeta-chain/zetacore/e2e/contracts/erc20" "github.com/zeta-chain/zetacore/e2e/contracts/zevmswap" "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" ) // setContractsFromConfig get EVM contracts from config @@ -211,5 +215,62 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { } } + // v2 contracts + + if c := conf.Contracts.EVM.Gateway; c != "" { + r.GatewayEVMAddr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid GatewayAddr: %w", err) + } + r.GatewayEVM, err = gatewayevm.NewGatewayEVM(r.GatewayEVMAddr, r.EVMClient) + if err != nil { + return err + } + } + + if c := conf.Contracts.EVM.ERC20CustodyNew; c != "" { + r.ERC20CustodyV2Addr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid ERC20CustodyV2Addr: %w", err) + } + r.ERC20CustodyV2, err = erc20custodyv2.NewERC20Custody(r.ERC20CustodyV2Addr, r.EVMClient) + if err != nil { + return err + } + } + + if c := conf.Contracts.EVM.TestDAppV2Addr; c != "" { + r.TestDAppV2EVMAddr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid TestDAppV2Addr: %w", err) + } + r.TestDAppV2EVM, err = testdappv2.NewTestDAppV2(r.TestDAppV2EVMAddr, r.EVMClient) + if err != nil { + return err + } + } + + if c := conf.Contracts.ZEVM.Gateway; c != "" { + r.GatewayZEVMAddr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid GatewayAddr: %w", err) + } + r.GatewayZEVM, err = gatewayzevm.NewGatewayZEVM(r.GatewayZEVMAddr, r.ZEVMClient) + if err != nil { + return err + } + } + + if c := conf.Contracts.ZEVM.TestDAppV2Addr; c != "" { + r.TestDAppV2ZEVMAddr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid TestDAppV2Addr: %w", err) + } + r.TestDAppV2ZEVM, err = testdappv2.NewTestDAppV2(r.TestDAppV2ZEVMAddr, r.EVMClient) + if err != nil { + return err + } + } + return nil } diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index 54534124bd..a0205ee000 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -27,9 +27,11 @@ contracts: connector_zevm: "0x239e96c8f17C85c30100AC26F635Ea15f23E9c67" wzeta: "0x5F0b1a82749cb4E2278EC87F8BF6B618dC71a8bf" test_dapp: "0xA8D5060feb6B456e886F023709A2795373691E63" + gateway: "0xa825eAa55b497AF892faca73a3797046C10B7c23" evm: zeta_eth: "0x733aB8b06DDDEf27Eaa72294B0d7c9cEF7f12db9" connector_eth: "0xD28D6A0b8189305551a0A8bd247a6ECa9CE781Ca" custody: "0xff3135df4F2775f4091b81f4c7B6359CfA07862a" erc20: "0xbD1e64A22B9F92D9Ce81aA9B4b0fFacd80215564" - test_dapp: "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed" \ No newline at end of file + test_dapp: "0xBFF76e77D56B3C1202107f059425D56f0AEF87Ed" + gateway: "0xF0deebCB0E9C829519C4baa794c5445171973826" \ No newline at end of file diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 2fdb61df17..1806f37360 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -41,6 +41,7 @@ const ( flagTestTSSMigration = "test-tss-migration" flagSkipBitcoinSetup = "skip-bitcoin-setup" flagSkipHeaderProof = "skip-header-proof" + flagTestV2 = "test-v2" flagSkipTrackerCheck = "skip-tracker-check" ) @@ -74,6 +75,7 @@ func NewLocalCmd() *cobra.Command { cmd.Flags().Bool(flagSkipBitcoinSetup, false, "set to true to skip bitcoin wallet setup") cmd.Flags().Bool(flagSkipHeaderProof, false, "set to true to skip header proof tests") cmd.Flags().Bool(flagTestTSSMigration, false, "set to true to include a migration test at the end") + cmd.Flags().Bool(flagTestV2, false, "set to true to run tests for v2 contracts") cmd.Flags().Bool(flagSkipTrackerCheck, false, "set to true to skip tracker check at the end of the tests") return cmd @@ -98,6 +100,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { skipHeaderProof = must(cmd.Flags().GetBool(flagSkipHeaderProof)) skipTrackerCheck = must(cmd.Flags().GetBool(flagSkipTrackerCheck)) testTSSMigration = must(cmd.Flags().GetBool(flagTestTSSMigration)) + testV2 = must(cmd.Flags().GetBool(flagTestV2)) ) logger := runner.NewLogger(verbose, color.FgWhite, "setup") @@ -185,13 +188,26 @@ func localE2ETest(cmd *cobra.Command, _ []string) { startTime := time.Now() deployerRunner.SetupEVM(contractsDeployed, true) - deployerRunner.SetZEVMContracts() + + if testV2 { + deployerRunner.SetupEVMV2() + } + + deployerRunner.SetZEVMSystemContracts() + + if testV2 { + // NOTE: v2 (gateway) setup called here because system contract needs to be set first, then gateway, then zrc20 + deployerRunner.SetZEVMContractsV2() + } + + deployerRunner.SetZEVMZRC20s() + if testSolana { deployerRunner.SetSolanaContracts(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()) } noError(deployerRunner.FundEmissionsPool()) - deployerRunner.MintERC20OnEvm(10000) + deployerRunner.MintERC20OnEvm(1000000) logger.Print("✅ setup completed in %s", time.Since(startTime)) } @@ -335,6 +351,66 @@ func localE2ETest(cmd *cobra.Command, _ []string) { } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } + if testV2 { + // update the ERC20 custody contract for v2 tests + deployerRunner.UpdateChainParamsERC20CustodyContract() + + //// Test happy paths for gas token workflow + eg.Go(v2TestRoutine(conf, "eth", conf.AdditionalAccounts.UserEther, color.FgHiGreen, deployerRunner, verbose, + e2etests.TestV2ETHDepositName, + e2etests.TestV2ETHDepositAndCallName, + e2etests.TestV2ETHWithdrawName, + e2etests.TestV2ETHWithdrawAndCallName, + e2etests.TestV2ZEVMToEVMCallName, + e2etests.TestV2EVMToZEVMCallName, + )) + + //// Test happy paths for erc20 token workflow + eg.Go(v2TestRoutine(conf, "erc20", conf.AdditionalAccounts.UserERC20, color.FgHiBlue, deployerRunner, verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM + e2etests.TestV2ERC20DepositName, + e2etests.TestV2ERC20DepositAndCallName, + e2etests.TestV2ERC20WithdrawName, + e2etests.TestV2ERC20WithdrawAndCallName, + )) + + // Test revert cases for gas token workflow + eg.Go( + v2TestRoutine( + conf, + "eth-revert", + conf.AdditionalAccounts.UserZetaTest, + color.FgHiYellow, + deployerRunner, + verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM and withdraw + e2etests.TestV2ETHDepositAndCallRevertName, + e2etests.TestV2ETHDepositAndCallRevertWithCallName, + e2etests.TestV2ETHWithdrawAndCallRevertName, + e2etests.TestV2ETHWithdrawAndCallRevertWithCallName, + ), + ) + + // Test revert cases for erc20 token workflow + eg.Go( + v2TestRoutine( + conf, + "erc20-revert", + conf.AdditionalAccounts.UserBitcoin, + color.FgHiRed, + deployerRunner, + verbose, + e2etests.TestV2ETHDepositName, // necessary to pay fees on ZEVM + e2etests.TestV2ERC20DepositName, // necessary to have assets to withdraw + e2etests.TestOperationAddLiquidityETHName, // liquidity with gas and ERC20 are necessary for reverts + e2etests.TestOperationAddLiquidityERC20Name, + e2etests.TestV2ERC20DepositAndCallRevertName, + e2etests.TestV2ERC20DepositAndCallRevertWithCallName, + e2etests.TestV2ERC20WithdrawAndCallRevertName, + e2etests.TestV2ERC20WithdrawAndCallRevertWithCallName, + ), + ) + } // while tests are executed, monitor blocks in parallel to check if system txs are on top and they have biggest priority txPriorityErrCh := make(chan error, 1) diff --git a/cmd/zetae2e/local/v2.go b/cmd/zetae2e/local/v2.go new file mode 100644 index 0000000000..e87785de31 --- /dev/null +++ b/cmd/zetae2e/local/v2.go @@ -0,0 +1,67 @@ +package local + +import ( + "fmt" + "time" + + "github.com/fatih/color" + + "github.com/zeta-chain/zetacore/e2e/config" + "github.com/zeta-chain/zetacore/e2e/e2etests" + "github.com/zeta-chain/zetacore/e2e/runner" +) + +// erc20TestRoutine runs v2 related e2e tests +// TODO: this routine will be broken down in the future and will replace most current tests +// we keep a single routine for v2 for simplicity +// https://github.com/zeta-chain/node/issues/2554 +func v2TestRoutine( + conf config.Config, + name string, + account config.Account, + color color.Attribute, + deployerRunner *runner.E2ERunner, + verbose bool, + testNames ...string, +) func() error { + return func() (err error) { + name = "v2-" + name + + // initialize runner for erc20 test + v2Runner, err := initTestRunner( + name, + conf, + deployerRunner, + account, + runner.NewLogger(verbose, color, name), + runner.WithZetaTxServer(deployerRunner.ZetaTxServer), + ) + if err != nil { + return err + } + + v2Runner.Logger.Print("🏃 starting %s tests", name) + startTime := time.Now() + + // funding the account + txERC20Send := deployerRunner.SendERC20OnEvm(account.EVMAddress(), 10000) + v2Runner.WaitForTxReceiptOnEvm(txERC20Send) + + // run erc20 test + testsToRun, err := v2Runner.GetE2ETestsToRunByName( + e2etests.AllE2ETests, + testNames..., + ) + if err != nil { + return fmt.Errorf("%s tests failed: %v", name, err) + } + + if err := v2Runner.RunE2ETests(testsToRun); err != nil { + return fmt.Errorf("%s tests failed: %v", name, err) + } + + v2Runner.Logger.Print("🍾 %s tests completed in %s", name, time.Since(startTime).String()) + + return err + } +} diff --git a/cmd/zetae2e/stress.go b/cmd/zetae2e/stress.go index b1d3a41bfc..e7b0c169a0 100644 --- a/cmd/zetae2e/stress.go +++ b/cmd/zetae2e/stress.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/fatih/color" "github.com/spf13/cobra" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "google.golang.org/grpc" "github.com/zeta-chain/zetacore/app" @@ -141,7 +141,8 @@ func StressTest(cmd *cobra.Command, _ []string) { switch stressTestArgs.network { case "LOCAL": // deploy and set zevm contract - e2eTest.SetZEVMContracts() + e2eTest.SetZEVMSystemContracts() + e2eTest.SetZEVMZRC20s() // deposit on ZetaChain e2eTest.DepositEther() diff --git a/cmd/zetatool/filterdeposit/evm.go b/cmd/zetatool/filterdeposit/evm.go index 427e0c421c..2fa39b8c90 100644 --- a/cmd/zetatool/filterdeposit/evm.go +++ b/cmd/zetatool/filterdeposit/evm.go @@ -13,8 +13,8 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/nanmu42/etherscan-api" "github.com/spf13/cobra" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" "github.com/zeta-chain/zetacore/cmd/zetatool/config" "github.com/zeta-chain/zetacore/pkg/constant" diff --git a/contrib/rpctest/main.go b/contrib/rpctest/main.go index 227a2c9bc0..f0a5e0d868 100644 --- a/contrib/rpctest/main.go +++ b/contrib/rpctest/main.go @@ -16,8 +16,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" - zetaeth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.eth.sol" - systemcontract "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + zetaeth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" ) var ( diff --git a/docs/cli/zetacored/cli.md b/docs/cli/zetacored/cli.md index bb8ac9f077..6e8555c166 100644 --- a/docs/cli/zetacored/cli.md +++ b/docs/cli/zetacored/cli.md @@ -9422,7 +9422,7 @@ zetacored tx crosschain vote-gas-price [chain] [price] [priorityFee] [blockNumbe Broadcast message to vote an inbound ``` -zetacored tx crosschain vote-inbound [sender] [senderChainID] [txOrigin] [receiver] [receiverChainID] [amount] [message] [inboundHash] [inBlockHeight] [coinType] [asset] [eventIndex] [flags] +zetacored tx crosschain vote-inbound [sender] [senderChainID] [txOrigin] [receiver] [receiverChainID] [amount] [message] [inboundHash] [inBlockHeight] [coinType] [asset] [eventIndex] [protocolContractVersion] [flags] ``` ### Options diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 5f431c557d..fbbcfe6b18 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -57071,11 +57071,13 @@ definitions: - Gas - ERC20 - Cmd + - NoAssetCall default: Zeta title: |- - Gas: Ether, BNB, Matic, Klay, BTC, etc - ERC20: ERC20 token - - Cmd: not a real coin, rather a command + - Cmd: no asset, used for admin command + - NoAssetCall: no asset, used for contract call crosschainCctxStatus: type: string enum: @@ -57121,6 +57123,10 @@ definitions: items: type: object $ref: '#/definitions/crosschainOutboundParams' + protocol_contract_version: + $ref: '#/definitions/crosschainProtocolContractVersion' + revert_options: + $ref: '#/definitions/crosschainRevertOptions' crosschainGasPrice: type: object properties: @@ -57325,6 +57331,15 @@ definitions: items: type: object $ref: '#/definitions/crosschainTxHash' + crosschainProtocolContractVersion: + type: string + enum: + - V1 + - V2 + default: V1 + title: |- + ProtocolContractVersion represents the version of the protocol contract used + for cctx workflow crosschainQueryAllCctxResponse: type: object properties: @@ -57547,6 +57562,21 @@ definitions: type: object $ref: '#/definitions/crosschainConversion' title: conversion in azeta per token + crosschainRevertOptions: + type: object + properties: + revert_address: + type: string + call_on_revert: + type: boolean + abort_address: + type: string + revert_message: + type: string + format: byte + revert_gas_limit: + type: string + title: RevertOptions represents the options for reverting a cctx crosschainTxFinalizationStatus: type: string enum: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index 0217520f62..f2fba7cd1d 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -187,6 +187,8 @@ message MsgVoteInbound { string tx_origin = 13; string asset = 14; uint64 event_index = 15; + ProtocolContractVersion protocol_contract_version = 16; + RevertOptions revert_options = 17; } ``` diff --git a/e2e/config/config.go b/e2e/config/config.go index e1e8d54611..7e721bcdd3 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -123,6 +123,9 @@ type EVM struct { CustodyAddr DoubleQuotedString `yaml:"custody"` ERC20 DoubleQuotedString `yaml:"erc20"` TestDappAddr DoubleQuotedString `yaml:"test_dapp"` + Gateway DoubleQuotedString `yaml:"gateway"` + ERC20CustodyNew DoubleQuotedString `yaml:"erc20_custody_new"` + TestDAppV2Addr DoubleQuotedString `yaml:"test_dapp_v2"` } // ZEVM contains the addresses of predeployed contracts on the zEVM chain @@ -139,6 +142,8 @@ type ZEVM struct { ZEVMSwapAppAddr DoubleQuotedString `yaml:"zevm_swap_app"` ContextAppAddr DoubleQuotedString `yaml:"context_app"` TestDappAddr DoubleQuotedString `yaml:"test_dapp"` + Gateway DoubleQuotedString `yaml:"gateway"` + TestDAppV2Addr DoubleQuotedString `yaml:"test_dapp_v2"` } // DefaultConfig returns the default config using values for localnet testing diff --git a/e2e/contracts/testzrc20/TestZRC20.abi b/e2e/contracts/testzrc20/TestZRC20.abi index ec8cc79f66..065326c8f7 100644 --- a/e2e/contracts/testzrc20/TestZRC20.abi +++ b/e2e/contracts/testzrc20/TestZRC20.abi @@ -431,6 +431,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "gatewayAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/e2e/contracts/testzrc20/TestZRC20.bin b/e2e/contracts/testzrc20/TestZRC20.bin index 9e74c2aaa3..1592405f01 100644 --- a/e2e/contracts/testzrc20/TestZRC20.bin +++ b/e2e/contracts/testzrc20/TestZRC20.bin @@ -1 +1 @@ -60c06040523480156200001157600080fd5b5060405162002566380380620025668339818101604052810190620000379190620000e1565b816080818152505080600281111562000055576200005462000128565b5b60a08160028111156200006d576200006c62000128565b5b81525050505062000157565b600080fd5b6000819050919050565b62000093816200007e565b81146200009f57600080fd5b50565b600081519050620000b38162000088565b92915050565b60038110620000c757600080fd5b50565b600081519050620000db81620000b9565b92915050565b60008060408385031215620000fb57620000fa62000079565b5b60006200010b85828601620000a2565b92505060206200011e85828601620000ca565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60805160a0516123db6200018b6000396000610aa40152600081816109ee01528181610f59015261107e01526123db6000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806385e1f4d0116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e146104ff578063eddeb1231461052f578063f2441b321461054b578063f687d12a14610569576101a9565b8063c701262614610494578063c835d7cc146104c4578063d9eeebed146104e0576101a9565b8063a457c2d7116100d3578063a457c2d7146103f8578063a7605f4514610428578063a9059cbb14610446578063b92894ba14610476576101a9565b806385e1f4d01461039e57806395d89b41146103bc578063a3413d03146103da576101a9565b8063395093511161016657806347e7ef241161014057806347e7ef24146103045780634d8943bb1461033457806370a0823114610352578063732bb0e414610382576101a9565b806339509351146102865780633ce4a5bc146102b657806342966c68146102d4576101a9565b806306fdde03146101ae578063091d2788146101cc578063095ea7b3146101ea57806318160ddd1461021a57806323b872dd14610238578063313ce56714610268575b600080fd5b6101b6610585565b6040516101c39190611b20565b60405180910390f35b6101d4610617565b6040516101e19190611b5b565b60405180910390f35b61020460048036038101906101ff9190611c14565b61061d565b6040516102119190611c6f565b60405180910390f35b61022261063b565b60405161022f9190611b5b565b60405180910390f35b610252600480360381019061024d9190611c8a565b610645565b60405161025f9190611c6f565b60405180910390f35b61027061073d565b60405161027d9190611cf9565b60405180910390f35b6102a0600480360381019061029b9190611c14565b610754565b6040516102ad9190611c6f565b60405180910390f35b6102be6107fa565b6040516102cb9190611d23565b60405180910390f35b6102ee60048036038101906102e99190611d3e565b610812565b6040516102fb9190611c6f565b60405180910390f35b61031e60048036038101906103199190611c14565b610827565b60405161032b9190611c6f565b60405180910390f35b61033c610993565b6040516103499190611b5b565b60405180910390f35b61036c60048036038101906103679190611d6b565b610999565b6040516103799190611b5b565b60405180910390f35b61039c60048036038101906103979190611d3e565b6109e2565b005b6103a66109ec565b6040516103b39190611b5b565b60405180910390f35b6103c4610a10565b6040516103d19190611b20565b60405180910390f35b6103e2610aa2565b6040516103ef9190611e0f565b60405180910390f35b610412600480360381019061040d9190611c14565b610ac6565b60405161041f9190611c6f565b60405180910390f35b610430610c29565b60405161043d9190611b5b565b60405180910390f35b610460600480360381019061045b9190611c14565b610c2f565b60405161046d9190611c6f565b60405180910390f35b61047e610c4d565b60405161048b9190611b20565b60405180910390f35b6104ae60048036038101906104a99190611f5f565b610cdb565b6040516104bb9190611c6f565b60405180910390f35b6104de60048036038101906104d99190611d6b565b610e22565b005b6104e8610f15565b6040516104f6929190611fbb565b60405180910390f35b61051960048036038101906105149190611fe4565b611162565b6040516105269190611b5b565b60405180910390f35b61054960048036038101906105449190611d3e565b6111e9565b005b6105536112a3565b6040516105609190611d23565b60405180910390f35b610583600480360381019061057e9190611d3e565b6112c7565b005b60606006805461059490612053565b80601f01602080910402602001604051908101604052809291908181526020018280546105c090612053565b801561060d5780601f106105e25761010080835404028352916020019161060d565b820191906000526020600020905b8154815290600101906020018083116105f057829003601f168201915b5050505050905090565b60015481565b600061063161062a611381565b8484611389565b6001905092915050565b6000600554905090565b6000610652848484611540565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061069d611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610714576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61073185610720611381565b858461072c91906120b3565b611389565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107a0611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107e991906120e7565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b600061081e338361179a565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108c5575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b156108fc576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109068383611951565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab6040516020016109639190612163565b604051602081830303815290604052846040516109819291906121d3565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b606060078054610a1f90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4b90612053565b8015610a985780601f10610a6d57610100808354040283529160200191610a98565b820191906000526020600020905b815481529060010190602001808311610a7b57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b12611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610b85576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610bcf611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c1891906120b3565b925050819055506001905092915050565b60095481565b6000610c43610c3c611381565b8484611540565b6001905092915050565b600a8054610c5a90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8690612053565b8015610cd35780601f10610ca857610100808354040283529160200191610cd3565b820191906000526020600020905b815481529060010190602001808311610cb657829003601f168201915b505050505081565b6000806000610ce8610f15565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d3d93929190612203565b6020604051808303816000875af1158015610d5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d809190612266565b610db6576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dc0338561179a565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e0e9493929190612293565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e9b576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f0a9190611d23565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401610f949190611b5b565b602060405180830381865afa158015610fb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd591906122f4565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361103d576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016110b99190611b5b565b602060405180830381865afa1580156110d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110fa9190612336565b905060008103611136576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111499190612363565b61115391906120e7565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611262576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f816040516112989190611b5b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611340576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a816040516113769190611b5b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113ef576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611455576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115339190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036115a6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361160c576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561168a576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161169691906120b3565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461172891906120e7565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161178c9190611b5b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611800576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561187e576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161188a91906120b3565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600560008282546118df91906120b3565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119449190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036119b7576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600560008282546119c991906120e7565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a1f91906120e7565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611a849190611b5b565b60405180910390a35050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611aca578082015181840152602081019050611aaf565b60008484015250505050565b6000601f19601f8301169050919050565b6000611af282611a90565b611afc8185611a9b565b9350611b0c818560208601611aac565b611b1581611ad6565b840191505092915050565b60006020820190508181036000830152611b3a8184611ae7565b905092915050565b6000819050919050565b611b5581611b42565b82525050565b6000602082019050611b706000830184611b4c565b92915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611bb582611b8a565b9050919050565b611bc581611baa565b8114611bd057600080fd5b50565b600081359050611be281611bbc565b92915050565b611bf181611b42565b8114611bfc57600080fd5b50565b600081359050611c0e81611be8565b92915050565b60008060408385031215611c2b57611c2a611b80565b5b6000611c3985828601611bd3565b9250506020611c4a85828601611bff565b9150509250929050565b60008115159050919050565b611c6981611c54565b82525050565b6000602082019050611c846000830184611c60565b92915050565b600080600060608486031215611ca357611ca2611b80565b5b6000611cb186828701611bd3565b9350506020611cc286828701611bd3565b9250506040611cd386828701611bff565b9150509250925092565b600060ff82169050919050565b611cf381611cdd565b82525050565b6000602082019050611d0e6000830184611cea565b92915050565b611d1d81611baa565b82525050565b6000602082019050611d386000830184611d14565b92915050565b600060208284031215611d5457611d53611b80565b5b6000611d6284828501611bff565b91505092915050565b600060208284031215611d8157611d80611b80565b5b6000611d8f84828501611bd3565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611dd857611dd7611d98565b5b50565b6000819050611de982611dc7565b919050565b6000611df982611ddb565b9050919050565b611e0981611dee565b82525050565b6000602082019050611e246000830184611e00565b92915050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b611e6c82611ad6565b810181811067ffffffffffffffff82111715611e8b57611e8a611e34565b5b80604052505050565b6000611e9e611b76565b9050611eaa8282611e63565b919050565b600067ffffffffffffffff821115611eca57611ec9611e34565b5b611ed382611ad6565b9050602081019050919050565b82818337600083830152505050565b6000611f02611efd84611eaf565b611e94565b905082815260208101848484011115611f1e57611f1d611e2f565b5b611f29848285611ee0565b509392505050565b600082601f830112611f4657611f45611e2a565b5b8135611f56848260208601611eef565b91505092915050565b60008060408385031215611f7657611f75611b80565b5b600083013567ffffffffffffffff811115611f9457611f93611b85565b5b611fa085828601611f31565b9250506020611fb185828601611bff565b9150509250929050565b6000604082019050611fd06000830185611d14565b611fdd6020830184611b4c565b9392505050565b60008060408385031215611ffb57611ffa611b80565b5b600061200985828601611bd3565b925050602061201a85828601611bd3565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061206b57607f821691505b60208210810361207e5761207d612024565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006120be82611b42565b91506120c983611b42565b92508282039050818111156120e1576120e0612084565b5b92915050565b60006120f282611b42565b91506120fd83611b42565b925082820190508082111561211557612114612084565b5b92915050565b60008160601b9050919050565b60006121338261211b565b9050919050565b600061214582612128565b9050919050565b61215d61215882611baa565b61213a565b82525050565b600061216f828461214c565b60148201915081905092915050565b600081519050919050565b600082825260208201905092915050565b60006121a58261217e565b6121af8185612189565b93506121bf818560208601611aac565b6121c881611ad6565b840191505092915050565b600060408201905081810360008301526121ed818561219a565b90506121fc6020830184611b4c565b9392505050565b60006060820190506122186000830186611d14565b6122256020830185611d14565b6122326040830184611b4c565b949350505050565b61224381611c54565b811461224e57600080fd5b50565b6000815190506122608161223a565b92915050565b60006020828403121561227c5761227b611b80565b5b600061228a84828501612251565b91505092915050565b600060808201905081810360008301526122ad818761219a565b90506122bc6020830186611b4c565b6122c96040830185611b4c565b6122d66060830184611b4c565b95945050505050565b6000815190506122ee81611bbc565b92915050565b60006020828403121561230a57612309611b80565b5b6000612318848285016122df565b91505092915050565b60008151905061233081611be8565b92915050565b60006020828403121561234c5761234b611b80565b5b600061235a84828501612321565b91505092915050565b600061236e82611b42565b915061237983611b42565b925082820261238781611b42565b9150828204841483151761239e5761239d612084565b5b509291505056fea264697066735822122011fda4fba218fc7f911b68b0418884c426f5ddb324eb1572e2e793fcb60edf6c64736f6c63430008150033 +60c06040523480156200001157600080fd5b5060405162002644380380620026448339818101604052810190620000379190620000aa565b8160808181525050806002811115620000555762000054620000fb565b5b60a08160028111156200006d576200006c620000fb565b5b60f81b8152505050506200015a565b6000815190506200008d816200012f565b92915050565b600081519050620000a48162000140565b92915050565b60008060408385031215620000c457620000c36200012a565b5b6000620000d48582860162000093565b9250506020620000e7858286016200007c565b9150509250929050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600080fd5b600381106200013d57600080fd5b50565b6200014b81620000f1565b81146200015757600080fd5b50565b60805160a05160f81c6124b3620001916000396000610b03015260008181610a2701528181610fc701526110fc01526124b36000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80638b851b95116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e14610538578063eddeb12314610568578063f2441b3214610584578063f687d12a146105a2576101c4565b8063c7012626146104cd578063c835d7cc146104fd578063d9eeebed14610519576101c4565b8063a457c2d7116100d3578063a457c2d714610431578063a7605f4514610461578063a9059cbb1461047f578063b92894ba146104af576101c4565b80638b851b95146103d757806395d89b41146103f5578063a3413d0314610413576101c4565b80633ce4a5bc116101665780634d8943bb116101405780634d8943bb1461034f57806370a082311461036d578063732bb0e41461039d57806385e1f4d0146103b9576101c4565b80633ce4a5bc146102d157806342966c68146102ef57806347e7ef241461031f576101c4565b806318160ddd116101a257806318160ddd1461023557806323b872dd14610253578063313ce5671461028357806339509351146102a1576101c4565b806306fdde03146101c9578063091d2788146101e7578063095ea7b314610205575b600080fd5b6101d16105be565b6040516101de9190612029565b60405180910390f35b6101ef610650565b6040516101fc919061204b565b60405180910390f35b61021f600480360381019061021a9190611cea565b610656565b60405161022c9190611f77565b60405180910390f35b61023d610674565b60405161024a919061204b565b60405180910390f35b61026d60048036038101906102689190611c97565b61067e565b60405161027a9190611f77565b60405180910390f35b61028b610776565b6040516102989190612066565b60405180910390f35b6102bb60048036038101906102b69190611cea565b61078d565b6040516102c89190611f77565b60405180910390f35b6102d9610833565b6040516102e69190611efc565b60405180910390f35b61030960048036038101906103049190611db3565b61084b565b6040516103169190611f77565b60405180910390f35b61033960048036038101906103349190611cea565b610860565b6040516103469190611f77565b60405180910390f35b6103576109cc565b604051610364919061204b565b60405180910390f35b61038760048036038101906103829190611bfd565b6109d2565b604051610394919061204b565b60405180910390f35b6103b760048036038101906103b29190611db3565b610a1b565b005b6103c1610a25565b6040516103ce919061204b565b60405180910390f35b6103df610a49565b6040516103ec9190611efc565b60405180910390f35b6103fd610a6f565b60405161040a9190612029565b60405180910390f35b61041b610b01565b604051610428919061200e565b60405180910390f35b61044b60048036038101906104469190611cea565b610b25565b6040516104589190611f77565b60405180910390f35b610469610c88565b604051610476919061204b565b60405180910390f35b61049960048036038101906104949190611cea565b610c8e565b6040516104a69190611f77565b60405180910390f35b6104b7610cac565b6040516104c49190612029565b60405180910390f35b6104e760048036038101906104e29190611d57565b610d3a565b6040516104f49190611f77565b60405180910390f35b61051760048036038101906105129190611bfd565b610e90565b005b610521610f83565b60405161052f929190611f4e565b60405180910390f35b610552600480360381019061054d9190611c57565b6111f0565b60405161055f919061204b565b60405180910390f35b610582600480360381019061057d9190611db3565b611277565b005b61058c611331565b6040516105999190611efc565b60405180910390f35b6105bc60048036038101906105b79190611db3565b611355565b005b6060600680546105cd906122af565b80601f01602080910402602001604051908101604052809291908181526020018280546105f9906122af565b80156106465780601f1061061b57610100808354040283529160200191610646565b820191906000526020600020905b81548152906001019060200180831161062957829003601f168201915b5050505050905090565b60015481565b600061066a61066361140f565b8484611417565b6001905092915050565b6000600554905090565b600061068b8484846115d0565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006106d661140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561074d576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61076a8561075961140f565b858461076591906121bf565b611417565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107d961140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610822919061210f565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b6000610857338361182c565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108fe575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b15610935576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093f83836119e4565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab60405160200161099c9190611ee1565b604051602081830303815290604052846040516109ba929190611f92565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600860019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606060078054610a7e906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610aaa906122af565b8015610af75780601f10610acc57610100808354040283529160200191610af7565b820191906000526020600020905b815481529060010190602001808311610ada57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b7161140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610be4576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610c2e61140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c7791906121bf565b925050819055506001905092915050565b60095481565b6000610ca2610c9b61140f565b84846115d0565b6001905092915050565b600a8054610cb9906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce5906122af565b8015610d325780601f10610d0757610100808354040283529160200191610d32565b820191906000526020600020905b815481529060010190602001808311610d1557829003601f168201915b505050505081565b6000806000610d47610f83565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d9c93929190611f17565b602060405180830381600087803b158015610db657600080fd5b505af1158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190611d2a565b610e24576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e2e338561182c565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e7c9493929190611fc2565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f09576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f789190611efc565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611002919061204b565b60206040518083038186803b15801561101a57600080fd5b505afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110529190611c2a565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156110bb576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611137919061204b565b60206040518083038186803b15801561114f57600080fd5b505afa158015611163573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111879190611de0565b905060008114156111c4576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111d79190612165565b6111e1919061210f565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112f0576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f81604051611326919061204b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113ce576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a81604051611404919061204b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561147e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156114e5576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115c3919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611637576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561169e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561171c576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161172891906121bf565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546117ba919061210f565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161181e919061204b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611893576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611911576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161191d91906121bf565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816005600082825461197291906121bf565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119d7919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a4b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060056000828254611a5d919061210f565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611ab3919061210f565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611b18919061204b565b60405180910390a35050565b6000611b37611b32846120a6565b612081565b905082815260208101848484011115611b5357611b526123f7565b5b611b5e84828561226d565b509392505050565b600081359050611b7581612438565b92915050565b600081519050611b8a81612438565b92915050565b600081519050611b9f8161244f565b92915050565b600082601f830112611bba57611bb96123f2565b5b8135611bca848260208601611b24565b91505092915050565b600081359050611be281612466565b92915050565b600081519050611bf781612466565b92915050565b600060208284031215611c1357611c12612401565b5b6000611c2184828501611b66565b91505092915050565b600060208284031215611c4057611c3f612401565b5b6000611c4e84828501611b7b565b91505092915050565b60008060408385031215611c6e57611c6d612401565b5b6000611c7c85828601611b66565b9250506020611c8d85828601611b66565b9150509250929050565b600080600060608486031215611cb057611caf612401565b5b6000611cbe86828701611b66565b9350506020611ccf86828701611b66565b9250506040611ce086828701611bd3565b9150509250925092565b60008060408385031215611d0157611d00612401565b5b6000611d0f85828601611b66565b9250506020611d2085828601611bd3565b9150509250929050565b600060208284031215611d4057611d3f612401565b5b6000611d4e84828501611b90565b91505092915050565b60008060408385031215611d6e57611d6d612401565b5b600083013567ffffffffffffffff811115611d8c57611d8b6123fc565b5b611d9885828601611ba5565b9250506020611da985828601611bd3565b9150509250929050565b600060208284031215611dc957611dc8612401565b5b6000611dd784828501611bd3565b91505092915050565b600060208284031215611df657611df5612401565b5b6000611e0484828501611be8565b91505092915050565b611e16816121f3565b82525050565b611e2d611e28826121f3565b612312565b82525050565b611e3c81612205565b82525050565b6000611e4d826120d7565b611e5781856120ed565b9350611e6781856020860161227c565b611e7081612406565b840191505092915050565b611e848161225b565b82525050565b6000611e95826120e2565b611e9f81856120fe565b9350611eaf81856020860161227c565b611eb881612406565b840191505092915050565b611ecc81612244565b82525050565b611edb8161224e565b82525050565b6000611eed8284611e1c565b60148201915081905092915050565b6000602082019050611f116000830184611e0d565b92915050565b6000606082019050611f2c6000830186611e0d565b611f396020830185611e0d565b611f466040830184611ec3565b949350505050565b6000604082019050611f636000830185611e0d565b611f706020830184611ec3565b9392505050565b6000602082019050611f8c6000830184611e33565b92915050565b60006040820190508181036000830152611fac8185611e42565b9050611fbb6020830184611ec3565b9392505050565b60006080820190508181036000830152611fdc8187611e42565b9050611feb6020830186611ec3565b611ff86040830185611ec3565b6120056060830184611ec3565b95945050505050565b60006020820190506120236000830184611e7b565b92915050565b600060208201905081810360008301526120438184611e8a565b905092915050565b60006020820190506120606000830184611ec3565b92915050565b600060208201905061207b6000830184611ed2565b92915050565b600061208b61209c565b905061209782826122e1565b919050565b6000604051905090565b600067ffffffffffffffff8211156120c1576120c06123c3565b5b6120ca82612406565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600061211a82612244565b915061212583612244565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561215a57612159612336565b5b828201905092915050565b600061217082612244565b915061217b83612244565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156121b4576121b3612336565b5b828202905092915050565b60006121ca82612244565b91506121d583612244565b9250828210156121e8576121e7612336565b5b828203905092915050565b60006121fe82612224565b9050919050565b60008115159050919050565b600081905061221f82612424565b919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226682612211565b9050919050565b82818337600083830152505050565b60005b8381101561229a57808201518184015260208101905061227f565b838111156122a9576000848401525b50505050565b600060028204905060018216806122c757607f821691505b602082108114156122db576122da612394565b5b50919050565b6122ea82612406565b810181811067ffffffffffffffff82111715612309576123086123c3565b5b80604052505050565b600061231d82612324565b9050919050565b600061232f82612417565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b60008160601b9050919050565b6003811061243557612434612365565b5b50565b612441816121f3565b811461244c57600080fd5b50565b61245881612205565b811461246357600080fd5b50565b61246f81612244565b811461247a57600080fd5b5056fea2646970667358221220b8278837e775bf149b356e2bff0f3e4931577c5c8ff848545ce78afb4e0ce8cb64736f6c63430008070033 diff --git a/e2e/contracts/testzrc20/TestZRC20.go b/e2e/contracts/testzrc20/TestZRC20.go index aef95ec0d1..e874699679 100644 --- a/e2e/contracts/testzrc20/TestZRC20.go +++ b/e2e/contracts/testzrc20/TestZRC20.go @@ -31,8 +31,8 @@ var ( // TestZRC20MetaData contains all meta data concerning the TestZRC20 contract. var TestZRC20MetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid_\",\"type\":\"uint256\"},{\"internalType\":\"enumCoinType\",\"name\":\"coinType_\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CallerIsNotFungibleModule\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasFeeTransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LowAllowance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LowBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroGasCoin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroGasPrice\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"from\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"UpdatedGasLimit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"UpdatedProtocolFlatFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"systemContract\",\"type\":\"address\"}],\"name\":\"UpdatedSystemContract\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"to\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasfee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"COIN_TYPE\",\"outputs\":[{\"internalType\":\"enumCoinType\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FUNGIBLE_MODULE_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GAS_LIMIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROTOCOL_FLAT_FEE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SYSTEM_CONTRACT_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"newField\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"newPublicField\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"updateGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newField_\",\"type\":\"uint256\"}],\"name\":\"updateNewField\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"updateProtocolFlatFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"updateSystemContractAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"to\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawGasFee\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x60c06040523480156200001157600080fd5b5060405162002566380380620025668339818101604052810190620000379190620000e1565b816080818152505080600281111562000055576200005462000128565b5b60a08160028111156200006d576200006c62000128565b5b81525050505062000157565b600080fd5b6000819050919050565b62000093816200007e565b81146200009f57600080fd5b50565b600081519050620000b38162000088565b92915050565b60038110620000c757600080fd5b50565b600081519050620000db81620000b9565b92915050565b60008060408385031215620000fb57620000fa62000079565b5b60006200010b85828601620000a2565b92505060206200011e85828601620000ca565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60805160a0516123db6200018b6000396000610aa40152600081816109ee01528181610f59015261107e01526123db6000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806385e1f4d0116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e146104ff578063eddeb1231461052f578063f2441b321461054b578063f687d12a14610569576101a9565b8063c701262614610494578063c835d7cc146104c4578063d9eeebed146104e0576101a9565b8063a457c2d7116100d3578063a457c2d7146103f8578063a7605f4514610428578063a9059cbb14610446578063b92894ba14610476576101a9565b806385e1f4d01461039e57806395d89b41146103bc578063a3413d03146103da576101a9565b8063395093511161016657806347e7ef241161014057806347e7ef24146103045780634d8943bb1461033457806370a0823114610352578063732bb0e414610382576101a9565b806339509351146102865780633ce4a5bc146102b657806342966c68146102d4576101a9565b806306fdde03146101ae578063091d2788146101cc578063095ea7b3146101ea57806318160ddd1461021a57806323b872dd14610238578063313ce56714610268575b600080fd5b6101b6610585565b6040516101c39190611b20565b60405180910390f35b6101d4610617565b6040516101e19190611b5b565b60405180910390f35b61020460048036038101906101ff9190611c14565b61061d565b6040516102119190611c6f565b60405180910390f35b61022261063b565b60405161022f9190611b5b565b60405180910390f35b610252600480360381019061024d9190611c8a565b610645565b60405161025f9190611c6f565b60405180910390f35b61027061073d565b60405161027d9190611cf9565b60405180910390f35b6102a0600480360381019061029b9190611c14565b610754565b6040516102ad9190611c6f565b60405180910390f35b6102be6107fa565b6040516102cb9190611d23565b60405180910390f35b6102ee60048036038101906102e99190611d3e565b610812565b6040516102fb9190611c6f565b60405180910390f35b61031e60048036038101906103199190611c14565b610827565b60405161032b9190611c6f565b60405180910390f35b61033c610993565b6040516103499190611b5b565b60405180910390f35b61036c60048036038101906103679190611d6b565b610999565b6040516103799190611b5b565b60405180910390f35b61039c60048036038101906103979190611d3e565b6109e2565b005b6103a66109ec565b6040516103b39190611b5b565b60405180910390f35b6103c4610a10565b6040516103d19190611b20565b60405180910390f35b6103e2610aa2565b6040516103ef9190611e0f565b60405180910390f35b610412600480360381019061040d9190611c14565b610ac6565b60405161041f9190611c6f565b60405180910390f35b610430610c29565b60405161043d9190611b5b565b60405180910390f35b610460600480360381019061045b9190611c14565b610c2f565b60405161046d9190611c6f565b60405180910390f35b61047e610c4d565b60405161048b9190611b20565b60405180910390f35b6104ae60048036038101906104a99190611f5f565b610cdb565b6040516104bb9190611c6f565b60405180910390f35b6104de60048036038101906104d99190611d6b565b610e22565b005b6104e8610f15565b6040516104f6929190611fbb565b60405180910390f35b61051960048036038101906105149190611fe4565b611162565b6040516105269190611b5b565b60405180910390f35b61054960048036038101906105449190611d3e565b6111e9565b005b6105536112a3565b6040516105609190611d23565b60405180910390f35b610583600480360381019061057e9190611d3e565b6112c7565b005b60606006805461059490612053565b80601f01602080910402602001604051908101604052809291908181526020018280546105c090612053565b801561060d5780601f106105e25761010080835404028352916020019161060d565b820191906000526020600020905b8154815290600101906020018083116105f057829003601f168201915b5050505050905090565b60015481565b600061063161062a611381565b8484611389565b6001905092915050565b6000600554905090565b6000610652848484611540565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061069d611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610714576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61073185610720611381565b858461072c91906120b3565b611389565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107a0611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107e991906120e7565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b600061081e338361179a565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108c5575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b156108fc576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109068383611951565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab6040516020016109639190612163565b604051602081830303815290604052846040516109819291906121d3565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b606060078054610a1f90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4b90612053565b8015610a985780601f10610a6d57610100808354040283529160200191610a98565b820191906000526020600020905b815481529060010190602001808311610a7b57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b12611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610b85576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610bcf611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c1891906120b3565b925050819055506001905092915050565b60095481565b6000610c43610c3c611381565b8484611540565b6001905092915050565b600a8054610c5a90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8690612053565b8015610cd35780601f10610ca857610100808354040283529160200191610cd3565b820191906000526020600020905b815481529060010190602001808311610cb657829003601f168201915b505050505081565b6000806000610ce8610f15565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d3d93929190612203565b6020604051808303816000875af1158015610d5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d809190612266565b610db6576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dc0338561179a565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e0e9493929190612293565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e9b576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f0a9190611d23565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401610f949190611b5b565b602060405180830381865afa158015610fb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd591906122f4565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361103d576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016110b99190611b5b565b602060405180830381865afa1580156110d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110fa9190612336565b905060008103611136576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111499190612363565b61115391906120e7565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611262576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f816040516112989190611b5b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611340576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a816040516113769190611b5b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113ef576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611455576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115339190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036115a6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361160c576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561168a576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161169691906120b3565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461172891906120e7565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161178c9190611b5b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611800576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561187e576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161188a91906120b3565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600560008282546118df91906120b3565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119449190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036119b7576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600560008282546119c991906120e7565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a1f91906120e7565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611a849190611b5b565b60405180910390a35050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611aca578082015181840152602081019050611aaf565b60008484015250505050565b6000601f19601f8301169050919050565b6000611af282611a90565b611afc8185611a9b565b9350611b0c818560208601611aac565b611b1581611ad6565b840191505092915050565b60006020820190508181036000830152611b3a8184611ae7565b905092915050565b6000819050919050565b611b5581611b42565b82525050565b6000602082019050611b706000830184611b4c565b92915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611bb582611b8a565b9050919050565b611bc581611baa565b8114611bd057600080fd5b50565b600081359050611be281611bbc565b92915050565b611bf181611b42565b8114611bfc57600080fd5b50565b600081359050611c0e81611be8565b92915050565b60008060408385031215611c2b57611c2a611b80565b5b6000611c3985828601611bd3565b9250506020611c4a85828601611bff565b9150509250929050565b60008115159050919050565b611c6981611c54565b82525050565b6000602082019050611c846000830184611c60565b92915050565b600080600060608486031215611ca357611ca2611b80565b5b6000611cb186828701611bd3565b9350506020611cc286828701611bd3565b9250506040611cd386828701611bff565b9150509250925092565b600060ff82169050919050565b611cf381611cdd565b82525050565b6000602082019050611d0e6000830184611cea565b92915050565b611d1d81611baa565b82525050565b6000602082019050611d386000830184611d14565b92915050565b600060208284031215611d5457611d53611b80565b5b6000611d6284828501611bff565b91505092915050565b600060208284031215611d8157611d80611b80565b5b6000611d8f84828501611bd3565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611dd857611dd7611d98565b5b50565b6000819050611de982611dc7565b919050565b6000611df982611ddb565b9050919050565b611e0981611dee565b82525050565b6000602082019050611e246000830184611e00565b92915050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b611e6c82611ad6565b810181811067ffffffffffffffff82111715611e8b57611e8a611e34565b5b80604052505050565b6000611e9e611b76565b9050611eaa8282611e63565b919050565b600067ffffffffffffffff821115611eca57611ec9611e34565b5b611ed382611ad6565b9050602081019050919050565b82818337600083830152505050565b6000611f02611efd84611eaf565b611e94565b905082815260208101848484011115611f1e57611f1d611e2f565b5b611f29848285611ee0565b509392505050565b600082601f830112611f4657611f45611e2a565b5b8135611f56848260208601611eef565b91505092915050565b60008060408385031215611f7657611f75611b80565b5b600083013567ffffffffffffffff811115611f9457611f93611b85565b5b611fa085828601611f31565b9250506020611fb185828601611bff565b9150509250929050565b6000604082019050611fd06000830185611d14565b611fdd6020830184611b4c565b9392505050565b60008060408385031215611ffb57611ffa611b80565b5b600061200985828601611bd3565b925050602061201a85828601611bd3565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061206b57607f821691505b60208210810361207e5761207d612024565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006120be82611b42565b91506120c983611b42565b92508282039050818111156120e1576120e0612084565b5b92915050565b60006120f282611b42565b91506120fd83611b42565b925082820190508082111561211557612114612084565b5b92915050565b60008160601b9050919050565b60006121338261211b565b9050919050565b600061214582612128565b9050919050565b61215d61215882611baa565b61213a565b82525050565b600061216f828461214c565b60148201915081905092915050565b600081519050919050565b600082825260208201905092915050565b60006121a58261217e565b6121af8185612189565b93506121bf818560208601611aac565b6121c881611ad6565b840191505092915050565b600060408201905081810360008301526121ed818561219a565b90506121fc6020830184611b4c565b9392505050565b60006060820190506122186000830186611d14565b6122256020830185611d14565b6122326040830184611b4c565b949350505050565b61224381611c54565b811461224e57600080fd5b50565b6000815190506122608161223a565b92915050565b60006020828403121561227c5761227b611b80565b5b600061228a84828501612251565b91505092915050565b600060808201905081810360008301526122ad818761219a565b90506122bc6020830186611b4c565b6122c96040830185611b4c565b6122d66060830184611b4c565b95945050505050565b6000815190506122ee81611bbc565b92915050565b60006020828403121561230a57612309611b80565b5b6000612318848285016122df565b91505092915050565b60008151905061233081611be8565b92915050565b60006020828403121561234c5761234b611b80565b5b600061235a84828501612321565b91505092915050565b600061236e82611b42565b915061237983611b42565b925082820261238781611b42565b9150828204841483151761239e5761239d612084565b5b509291505056fea264697066735822122011fda4fba218fc7f911b68b0418884c426f5ddb324eb1572e2e793fcb60edf6c64736f6c63430008150033", + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainid_\",\"type\":\"uint256\"},{\"internalType\":\"enumCoinType\",\"name\":\"coinType_\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"CallerIsNotFungibleModule\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GasFeeTransferFailed\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidSender\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LowAllowance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"LowBalance\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroAddress\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroGasCoin\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ZeroGasPrice\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"from\",\"type\":\"bytes\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Deposit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"UpdatedGasLimit\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"UpdatedProtocolFlatFee\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"systemContract\",\"type\":\"address\"}],\"name\":\"UpdatedSystemContract\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"to\",\"type\":\"bytes\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"gasfee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"Withdrawal\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CHAIN_ID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"COIN_TYPE\",\"outputs\":[{\"internalType\":\"enumCoinType\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"FUNGIBLE_MODULE_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GAS_LIMIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROTOCOL_FLAT_FEE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SYSTEM_CONTRACT_ADDRESS\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"deposit\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gatewayAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"newField\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"newPublicField\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"}],\"name\":\"updateGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newField_\",\"type\":\"uint256\"}],\"name\":\"updateNewField\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"protocolFlatFee\",\"type\":\"uint256\"}],\"name\":\"updateProtocolFlatFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"updateSystemContractAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"to\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdraw\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"withdrawGasFee\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x60c06040523480156200001157600080fd5b5060405162002644380380620026448339818101604052810190620000379190620000aa565b8160808181525050806002811115620000555762000054620000fb565b5b60a08160028111156200006d576200006c620000fb565b5b60f81b8152505050506200015a565b6000815190506200008d816200012f565b92915050565b600081519050620000a48162000140565b92915050565b60008060408385031215620000c457620000c36200012a565b5b6000620000d48582860162000093565b9250506020620000e7858286016200007c565b9150509250929050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600080fd5b600381106200013d57600080fd5b50565b6200014b81620000f1565b81146200015757600080fd5b50565b60805160a05160f81c6124b3620001916000396000610b03015260008181610a2701528181610fc701526110fc01526124b36000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80638b851b95116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e14610538578063eddeb12314610568578063f2441b3214610584578063f687d12a146105a2576101c4565b8063c7012626146104cd578063c835d7cc146104fd578063d9eeebed14610519576101c4565b8063a457c2d7116100d3578063a457c2d714610431578063a7605f4514610461578063a9059cbb1461047f578063b92894ba146104af576101c4565b80638b851b95146103d757806395d89b41146103f5578063a3413d0314610413576101c4565b80633ce4a5bc116101665780634d8943bb116101405780634d8943bb1461034f57806370a082311461036d578063732bb0e41461039d57806385e1f4d0146103b9576101c4565b80633ce4a5bc146102d157806342966c68146102ef57806347e7ef241461031f576101c4565b806318160ddd116101a257806318160ddd1461023557806323b872dd14610253578063313ce5671461028357806339509351146102a1576101c4565b806306fdde03146101c9578063091d2788146101e7578063095ea7b314610205575b600080fd5b6101d16105be565b6040516101de9190612029565b60405180910390f35b6101ef610650565b6040516101fc919061204b565b60405180910390f35b61021f600480360381019061021a9190611cea565b610656565b60405161022c9190611f77565b60405180910390f35b61023d610674565b60405161024a919061204b565b60405180910390f35b61026d60048036038101906102689190611c97565b61067e565b60405161027a9190611f77565b60405180910390f35b61028b610776565b6040516102989190612066565b60405180910390f35b6102bb60048036038101906102b69190611cea565b61078d565b6040516102c89190611f77565b60405180910390f35b6102d9610833565b6040516102e69190611efc565b60405180910390f35b61030960048036038101906103049190611db3565b61084b565b6040516103169190611f77565b60405180910390f35b61033960048036038101906103349190611cea565b610860565b6040516103469190611f77565b60405180910390f35b6103576109cc565b604051610364919061204b565b60405180910390f35b61038760048036038101906103829190611bfd565b6109d2565b604051610394919061204b565b60405180910390f35b6103b760048036038101906103b29190611db3565b610a1b565b005b6103c1610a25565b6040516103ce919061204b565b60405180910390f35b6103df610a49565b6040516103ec9190611efc565b60405180910390f35b6103fd610a6f565b60405161040a9190612029565b60405180910390f35b61041b610b01565b604051610428919061200e565b60405180910390f35b61044b60048036038101906104469190611cea565b610b25565b6040516104589190611f77565b60405180910390f35b610469610c88565b604051610476919061204b565b60405180910390f35b61049960048036038101906104949190611cea565b610c8e565b6040516104a69190611f77565b60405180910390f35b6104b7610cac565b6040516104c49190612029565b60405180910390f35b6104e760048036038101906104e29190611d57565b610d3a565b6040516104f49190611f77565b60405180910390f35b61051760048036038101906105129190611bfd565b610e90565b005b610521610f83565b60405161052f929190611f4e565b60405180910390f35b610552600480360381019061054d9190611c57565b6111f0565b60405161055f919061204b565b60405180910390f35b610582600480360381019061057d9190611db3565b611277565b005b61058c611331565b6040516105999190611efc565b60405180910390f35b6105bc60048036038101906105b79190611db3565b611355565b005b6060600680546105cd906122af565b80601f01602080910402602001604051908101604052809291908181526020018280546105f9906122af565b80156106465780601f1061061b57610100808354040283529160200191610646565b820191906000526020600020905b81548152906001019060200180831161062957829003601f168201915b5050505050905090565b60015481565b600061066a61066361140f565b8484611417565b6001905092915050565b6000600554905090565b600061068b8484846115d0565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006106d661140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561074d576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61076a8561075961140f565b858461076591906121bf565b611417565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107d961140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610822919061210f565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b6000610857338361182c565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108fe575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b15610935576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093f83836119e4565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab60405160200161099c9190611ee1565b604051602081830303815290604052846040516109ba929190611f92565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600860019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606060078054610a7e906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610aaa906122af565b8015610af75780601f10610acc57610100808354040283529160200191610af7565b820191906000526020600020905b815481529060010190602001808311610ada57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b7161140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610be4576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610c2e61140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c7791906121bf565b925050819055506001905092915050565b60095481565b6000610ca2610c9b61140f565b84846115d0565b6001905092915050565b600a8054610cb9906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce5906122af565b8015610d325780601f10610d0757610100808354040283529160200191610d32565b820191906000526020600020905b815481529060010190602001808311610d1557829003601f168201915b505050505081565b6000806000610d47610f83565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d9c93929190611f17565b602060405180830381600087803b158015610db657600080fd5b505af1158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190611d2a565b610e24576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e2e338561182c565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e7c9493929190611fc2565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f09576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f789190611efc565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611002919061204b565b60206040518083038186803b15801561101a57600080fd5b505afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110529190611c2a565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156110bb576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611137919061204b565b60206040518083038186803b15801561114f57600080fd5b505afa158015611163573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111879190611de0565b905060008114156111c4576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111d79190612165565b6111e1919061210f565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112f0576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f81604051611326919061204b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113ce576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a81604051611404919061204b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561147e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156114e5576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115c3919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611637576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561169e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561171c576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161172891906121bf565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546117ba919061210f565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161181e919061204b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611893576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611911576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161191d91906121bf565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816005600082825461197291906121bf565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119d7919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a4b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060056000828254611a5d919061210f565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611ab3919061210f565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611b18919061204b565b60405180910390a35050565b6000611b37611b32846120a6565b612081565b905082815260208101848484011115611b5357611b526123f7565b5b611b5e84828561226d565b509392505050565b600081359050611b7581612438565b92915050565b600081519050611b8a81612438565b92915050565b600081519050611b9f8161244f565b92915050565b600082601f830112611bba57611bb96123f2565b5b8135611bca848260208601611b24565b91505092915050565b600081359050611be281612466565b92915050565b600081519050611bf781612466565b92915050565b600060208284031215611c1357611c12612401565b5b6000611c2184828501611b66565b91505092915050565b600060208284031215611c4057611c3f612401565b5b6000611c4e84828501611b7b565b91505092915050565b60008060408385031215611c6e57611c6d612401565b5b6000611c7c85828601611b66565b9250506020611c8d85828601611b66565b9150509250929050565b600080600060608486031215611cb057611caf612401565b5b6000611cbe86828701611b66565b9350506020611ccf86828701611b66565b9250506040611ce086828701611bd3565b9150509250925092565b60008060408385031215611d0157611d00612401565b5b6000611d0f85828601611b66565b9250506020611d2085828601611bd3565b9150509250929050565b600060208284031215611d4057611d3f612401565b5b6000611d4e84828501611b90565b91505092915050565b60008060408385031215611d6e57611d6d612401565b5b600083013567ffffffffffffffff811115611d8c57611d8b6123fc565b5b611d9885828601611ba5565b9250506020611da985828601611bd3565b9150509250929050565b600060208284031215611dc957611dc8612401565b5b6000611dd784828501611bd3565b91505092915050565b600060208284031215611df657611df5612401565b5b6000611e0484828501611be8565b91505092915050565b611e16816121f3565b82525050565b611e2d611e28826121f3565b612312565b82525050565b611e3c81612205565b82525050565b6000611e4d826120d7565b611e5781856120ed565b9350611e6781856020860161227c565b611e7081612406565b840191505092915050565b611e848161225b565b82525050565b6000611e95826120e2565b611e9f81856120fe565b9350611eaf81856020860161227c565b611eb881612406565b840191505092915050565b611ecc81612244565b82525050565b611edb8161224e565b82525050565b6000611eed8284611e1c565b60148201915081905092915050565b6000602082019050611f116000830184611e0d565b92915050565b6000606082019050611f2c6000830186611e0d565b611f396020830185611e0d565b611f466040830184611ec3565b949350505050565b6000604082019050611f636000830185611e0d565b611f706020830184611ec3565b9392505050565b6000602082019050611f8c6000830184611e33565b92915050565b60006040820190508181036000830152611fac8185611e42565b9050611fbb6020830184611ec3565b9392505050565b60006080820190508181036000830152611fdc8187611e42565b9050611feb6020830186611ec3565b611ff86040830185611ec3565b6120056060830184611ec3565b95945050505050565b60006020820190506120236000830184611e7b565b92915050565b600060208201905081810360008301526120438184611e8a565b905092915050565b60006020820190506120606000830184611ec3565b92915050565b600060208201905061207b6000830184611ed2565b92915050565b600061208b61209c565b905061209782826122e1565b919050565b6000604051905090565b600067ffffffffffffffff8211156120c1576120c06123c3565b5b6120ca82612406565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600061211a82612244565b915061212583612244565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561215a57612159612336565b5b828201905092915050565b600061217082612244565b915061217b83612244565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156121b4576121b3612336565b5b828202905092915050565b60006121ca82612244565b91506121d583612244565b9250828210156121e8576121e7612336565b5b828203905092915050565b60006121fe82612224565b9050919050565b60008115159050919050565b600081905061221f82612424565b919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226682612211565b9050919050565b82818337600083830152505050565b60005b8381101561229a57808201518184015260208101905061227f565b838111156122a9576000848401525b50505050565b600060028204905060018216806122c757607f821691505b602082108114156122db576122da612394565b5b50919050565b6122ea82612406565b810181811067ffffffffffffffff82111715612309576123086123c3565b5b80604052505050565b600061231d82612324565b9050919050565b600061232f82612417565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b60008160601b9050919050565b6003811061243557612434612365565b5b50565b612441816121f3565b811461244c57600080fd5b50565b61245881612205565b811461246357600080fd5b50565b61246f81612244565b811461247a57600080fd5b5056fea2646970667358221220b8278837e775bf149b356e2bff0f3e4931577c5c8ff848545ce78afb4e0ce8cb64736f6c63430008070033", } // TestZRC20ABI is the input ABI used to generate the binding from. @@ -481,6 +481,37 @@ func (_TestZRC20 *TestZRC20CallerSession) Decimals() (uint8, error) { return _TestZRC20.Contract.Decimals(&_TestZRC20.CallOpts) } +// GatewayAddress is a free data retrieval call binding the contract method 0x8b851b95. +// +// Solidity: function gatewayAddress() view returns(address) +func (_TestZRC20 *TestZRC20Caller) GatewayAddress(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _TestZRC20.contract.Call(opts, &out, "gatewayAddress") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GatewayAddress is a free data retrieval call binding the contract method 0x8b851b95. +// +// Solidity: function gatewayAddress() view returns(address) +func (_TestZRC20 *TestZRC20Session) GatewayAddress() (common.Address, error) { + return _TestZRC20.Contract.GatewayAddress(&_TestZRC20.CallOpts) +} + +// GatewayAddress is a free data retrieval call binding the contract method 0x8b851b95. +// +// Solidity: function gatewayAddress() view returns(address) +func (_TestZRC20 *TestZRC20CallerSession) GatewayAddress() (common.Address, error) { + return _TestZRC20.Contract.GatewayAddress(&_TestZRC20.CallOpts) +} + // Name is a free data retrieval call binding the contract method 0x06fdde03. // // Solidity: function name() view returns(string) diff --git a/e2e/contracts/testzrc20/TestZRC20.json b/e2e/contracts/testzrc20/TestZRC20.json index b4d7b68b26..4e28aac85b 100644 --- a/e2e/contracts/testzrc20/TestZRC20.json +++ b/e2e/contracts/testzrc20/TestZRC20.json @@ -432,6 +432,19 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "gatewayAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -669,5 +682,5 @@ "type": "function" } ], - "bin": "60c06040523480156200001157600080fd5b5060405162002566380380620025668339818101604052810190620000379190620000e1565b816080818152505080600281111562000055576200005462000128565b5b60a08160028111156200006d576200006c62000128565b5b81525050505062000157565b600080fd5b6000819050919050565b62000093816200007e565b81146200009f57600080fd5b50565b600081519050620000b38162000088565b92915050565b60038110620000c757600080fd5b50565b600081519050620000db81620000b9565b92915050565b60008060408385031215620000fb57620000fa62000079565b5b60006200010b85828601620000a2565b92505060206200011e85828601620000ca565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60805160a0516123db6200018b6000396000610aa40152600081816109ee01528181610f59015261107e01526123db6000f3fe608060405234801561001057600080fd5b50600436106101a95760003560e01c806385e1f4d0116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e146104ff578063eddeb1231461052f578063f2441b321461054b578063f687d12a14610569576101a9565b8063c701262614610494578063c835d7cc146104c4578063d9eeebed146104e0576101a9565b8063a457c2d7116100d3578063a457c2d7146103f8578063a7605f4514610428578063a9059cbb14610446578063b92894ba14610476576101a9565b806385e1f4d01461039e57806395d89b41146103bc578063a3413d03146103da576101a9565b8063395093511161016657806347e7ef241161014057806347e7ef24146103045780634d8943bb1461033457806370a0823114610352578063732bb0e414610382576101a9565b806339509351146102865780633ce4a5bc146102b657806342966c68146102d4576101a9565b806306fdde03146101ae578063091d2788146101cc578063095ea7b3146101ea57806318160ddd1461021a57806323b872dd14610238578063313ce56714610268575b600080fd5b6101b6610585565b6040516101c39190611b20565b60405180910390f35b6101d4610617565b6040516101e19190611b5b565b60405180910390f35b61020460048036038101906101ff9190611c14565b61061d565b6040516102119190611c6f565b60405180910390f35b61022261063b565b60405161022f9190611b5b565b60405180910390f35b610252600480360381019061024d9190611c8a565b610645565b60405161025f9190611c6f565b60405180910390f35b61027061073d565b60405161027d9190611cf9565b60405180910390f35b6102a0600480360381019061029b9190611c14565b610754565b6040516102ad9190611c6f565b60405180910390f35b6102be6107fa565b6040516102cb9190611d23565b60405180910390f35b6102ee60048036038101906102e99190611d3e565b610812565b6040516102fb9190611c6f565b60405180910390f35b61031e60048036038101906103199190611c14565b610827565b60405161032b9190611c6f565b60405180910390f35b61033c610993565b6040516103499190611b5b565b60405180910390f35b61036c60048036038101906103679190611d6b565b610999565b6040516103799190611b5b565b60405180910390f35b61039c60048036038101906103979190611d3e565b6109e2565b005b6103a66109ec565b6040516103b39190611b5b565b60405180910390f35b6103c4610a10565b6040516103d19190611b20565b60405180910390f35b6103e2610aa2565b6040516103ef9190611e0f565b60405180910390f35b610412600480360381019061040d9190611c14565b610ac6565b60405161041f9190611c6f565b60405180910390f35b610430610c29565b60405161043d9190611b5b565b60405180910390f35b610460600480360381019061045b9190611c14565b610c2f565b60405161046d9190611c6f565b60405180910390f35b61047e610c4d565b60405161048b9190611b20565b60405180910390f35b6104ae60048036038101906104a99190611f5f565b610cdb565b6040516104bb9190611c6f565b60405180910390f35b6104de60048036038101906104d99190611d6b565b610e22565b005b6104e8610f15565b6040516104f6929190611fbb565b60405180910390f35b61051960048036038101906105149190611fe4565b611162565b6040516105269190611b5b565b60405180910390f35b61054960048036038101906105449190611d3e565b6111e9565b005b6105536112a3565b6040516105609190611d23565b60405180910390f35b610583600480360381019061057e9190611d3e565b6112c7565b005b60606006805461059490612053565b80601f01602080910402602001604051908101604052809291908181526020018280546105c090612053565b801561060d5780601f106105e25761010080835404028352916020019161060d565b820191906000526020600020905b8154815290600101906020018083116105f057829003601f168201915b5050505050905090565b60015481565b600061063161062a611381565b8484611389565b6001905092915050565b6000600554905090565b6000610652848484611540565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061069d611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082811015610714576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61073185610720611381565b858461072c91906120b3565b611389565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107a0611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546107e991906120e7565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b600061081e338361179a565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108c5575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b156108fc576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109068383611951565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab6040516020016109639190612163565b604051602081830303815290604052846040516109819291906121d3565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b606060078054610a1f90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610a4b90612053565b8015610a985780601f10610a6d57610100808354040283529160200191610a98565b820191906000526020600020905b815481529060010190602001808311610a7b57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b12611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610b85576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610bcf611381565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c1891906120b3565b925050819055506001905092915050565b60095481565b6000610c43610c3c611381565b8484611540565b6001905092915050565b600a8054610c5a90612053565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8690612053565b8015610cd35780601f10610ca857610100808354040283529160200191610cd3565b820191906000526020600020905b815481529060010190602001808311610cb657829003601f168201915b505050505081565b6000806000610ce8610f15565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d3d93929190612203565b6020604051808303816000875af1158015610d5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d809190612266565b610db6576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dc0338561179a565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e0e9493929190612293565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610e9b576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f0a9190611d23565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401610f949190611b5b565b602060405180830381865afa158015610fb1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fd591906122f4565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361103d576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b81526004016110b99190611b5b565b602060405180830381865afa1580156110d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110fa9190612336565b905060008103611136576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111499190612363565b61115391906120e7565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611262576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f816040516112989190611b5b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611340576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a816040516113769190611b5b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036113ef576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611455576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115339190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036115a6576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361160c576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561168a576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161169691906120b3565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461172891906120e7565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161178c9190611b5b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603611800576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561187e576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161188a91906120b3565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600560008282546118df91906120b3565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119449190611b5b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036119b7576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600560008282546119c991906120e7565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611a1f91906120e7565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611a849190611b5b565b60405180910390a35050565b600081519050919050565b600082825260208201905092915050565b60005b83811015611aca578082015181840152602081019050611aaf565b60008484015250505050565b6000601f19601f8301169050919050565b6000611af282611a90565b611afc8185611a9b565b9350611b0c818560208601611aac565b611b1581611ad6565b840191505092915050565b60006020820190508181036000830152611b3a8184611ae7565b905092915050565b6000819050919050565b611b5581611b42565b82525050565b6000602082019050611b706000830184611b4c565b92915050565b6000604051905090565b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000611bb582611b8a565b9050919050565b611bc581611baa565b8114611bd057600080fd5b50565b600081359050611be281611bbc565b92915050565b611bf181611b42565b8114611bfc57600080fd5b50565b600081359050611c0e81611be8565b92915050565b60008060408385031215611c2b57611c2a611b80565b5b6000611c3985828601611bd3565b9250506020611c4a85828601611bff565b9150509250929050565b60008115159050919050565b611c6981611c54565b82525050565b6000602082019050611c846000830184611c60565b92915050565b600080600060608486031215611ca357611ca2611b80565b5b6000611cb186828701611bd3565b9350506020611cc286828701611bd3565b9250506040611cd386828701611bff565b9150509250925092565b600060ff82169050919050565b611cf381611cdd565b82525050565b6000602082019050611d0e6000830184611cea565b92915050565b611d1d81611baa565b82525050565b6000602082019050611d386000830184611d14565b92915050565b600060208284031215611d5457611d53611b80565b5b6000611d6284828501611bff565b91505092915050565b600060208284031215611d8157611d80611b80565b5b6000611d8f84828501611bd3565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611dd857611dd7611d98565b5b50565b6000819050611de982611dc7565b919050565b6000611df982611ddb565b9050919050565b611e0981611dee565b82525050565b6000602082019050611e246000830184611e00565b92915050565b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b611e6c82611ad6565b810181811067ffffffffffffffff82111715611e8b57611e8a611e34565b5b80604052505050565b6000611e9e611b76565b9050611eaa8282611e63565b919050565b600067ffffffffffffffff821115611eca57611ec9611e34565b5b611ed382611ad6565b9050602081019050919050565b82818337600083830152505050565b6000611f02611efd84611eaf565b611e94565b905082815260208101848484011115611f1e57611f1d611e2f565b5b611f29848285611ee0565b509392505050565b600082601f830112611f4657611f45611e2a565b5b8135611f56848260208601611eef565b91505092915050565b60008060408385031215611f7657611f75611b80565b5b600083013567ffffffffffffffff811115611f9457611f93611b85565b5b611fa085828601611f31565b9250506020611fb185828601611bff565b9150509250929050565b6000604082019050611fd06000830185611d14565b611fdd6020830184611b4c565b9392505050565b60008060408385031215611ffb57611ffa611b80565b5b600061200985828601611bd3565b925050602061201a85828601611bd3565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061206b57607f821691505b60208210810361207e5761207d612024565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006120be82611b42565b91506120c983611b42565b92508282039050818111156120e1576120e0612084565b5b92915050565b60006120f282611b42565b91506120fd83611b42565b925082820190508082111561211557612114612084565b5b92915050565b60008160601b9050919050565b60006121338261211b565b9050919050565b600061214582612128565b9050919050565b61215d61215882611baa565b61213a565b82525050565b600061216f828461214c565b60148201915081905092915050565b600081519050919050565b600082825260208201905092915050565b60006121a58261217e565b6121af8185612189565b93506121bf818560208601611aac565b6121c881611ad6565b840191505092915050565b600060408201905081810360008301526121ed818561219a565b90506121fc6020830184611b4c565b9392505050565b60006060820190506122186000830186611d14565b6122256020830185611d14565b6122326040830184611b4c565b949350505050565b61224381611c54565b811461224e57600080fd5b50565b6000815190506122608161223a565b92915050565b60006020828403121561227c5761227b611b80565b5b600061228a84828501612251565b91505092915050565b600060808201905081810360008301526122ad818761219a565b90506122bc6020830186611b4c565b6122c96040830185611b4c565b6122d66060830184611b4c565b95945050505050565b6000815190506122ee81611bbc565b92915050565b60006020828403121561230a57612309611b80565b5b6000612318848285016122df565b91505092915050565b60008151905061233081611be8565b92915050565b60006020828403121561234c5761234b611b80565b5b600061235a84828501612321565b91505092915050565b600061236e82611b42565b915061237983611b42565b925082820261238781611b42565b9150828204841483151761239e5761239d612084565b5b509291505056fea264697066735822122011fda4fba218fc7f911b68b0418884c426f5ddb324eb1572e2e793fcb60edf6c64736f6c63430008150033" + "bin": "60c06040523480156200001157600080fd5b5060405162002644380380620026448339818101604052810190620000379190620000aa565b8160808181525050806002811115620000555762000054620000fb565b5b60a08160028111156200006d576200006c620000fb565b5b60f81b8152505050506200015a565b6000815190506200008d816200012f565b92915050565b600081519050620000a48162000140565b92915050565b60008060408385031215620000c457620000c36200012a565b5b6000620000d48582860162000093565b9250506020620000e7858286016200007c565b9150509250929050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b600080fd5b600381106200013d57600080fd5b50565b6200014b81620000f1565b81146200015757600080fd5b50565b60805160a05160f81c6124b3620001916000396000610b03015260008181610a2701528181610fc701526110fc01526124b36000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80638b851b95116100f9578063c701262611610097578063dd62ed3e11610071578063dd62ed3e14610538578063eddeb12314610568578063f2441b3214610584578063f687d12a146105a2576101c4565b8063c7012626146104cd578063c835d7cc146104fd578063d9eeebed14610519576101c4565b8063a457c2d7116100d3578063a457c2d714610431578063a7605f4514610461578063a9059cbb1461047f578063b92894ba146104af576101c4565b80638b851b95146103d757806395d89b41146103f5578063a3413d0314610413576101c4565b80633ce4a5bc116101665780634d8943bb116101405780634d8943bb1461034f57806370a082311461036d578063732bb0e41461039d57806385e1f4d0146103b9576101c4565b80633ce4a5bc146102d157806342966c68146102ef57806347e7ef241461031f576101c4565b806318160ddd116101a257806318160ddd1461023557806323b872dd14610253578063313ce5671461028357806339509351146102a1576101c4565b806306fdde03146101c9578063091d2788146101e7578063095ea7b314610205575b600080fd5b6101d16105be565b6040516101de9190612029565b60405180910390f35b6101ef610650565b6040516101fc919061204b565b60405180910390f35b61021f600480360381019061021a9190611cea565b610656565b60405161022c9190611f77565b60405180910390f35b61023d610674565b60405161024a919061204b565b60405180910390f35b61026d60048036038101906102689190611c97565b61067e565b60405161027a9190611f77565b60405180910390f35b61028b610776565b6040516102989190612066565b60405180910390f35b6102bb60048036038101906102b69190611cea565b61078d565b6040516102c89190611f77565b60405180910390f35b6102d9610833565b6040516102e69190611efc565b60405180910390f35b61030960048036038101906103049190611db3565b61084b565b6040516103169190611f77565b60405180910390f35b61033960048036038101906103349190611cea565b610860565b6040516103469190611f77565b60405180910390f35b6103576109cc565b604051610364919061204b565b60405180910390f35b61038760048036038101906103829190611bfd565b6109d2565b604051610394919061204b565b60405180910390f35b6103b760048036038101906103b29190611db3565b610a1b565b005b6103c1610a25565b6040516103ce919061204b565b60405180910390f35b6103df610a49565b6040516103ec9190611efc565b60405180910390f35b6103fd610a6f565b60405161040a9190612029565b60405180910390f35b61041b610b01565b604051610428919061200e565b60405180910390f35b61044b60048036038101906104469190611cea565b610b25565b6040516104589190611f77565b60405180910390f35b610469610c88565b604051610476919061204b565b60405180910390f35b61049960048036038101906104949190611cea565b610c8e565b6040516104a69190611f77565b60405180910390f35b6104b7610cac565b6040516104c49190612029565b60405180910390f35b6104e760048036038101906104e29190611d57565b610d3a565b6040516104f49190611f77565b60405180910390f35b61051760048036038101906105129190611bfd565b610e90565b005b610521610f83565b60405161052f929190611f4e565b60405180910390f35b610552600480360381019061054d9190611c57565b6111f0565b60405161055f919061204b565b60405180910390f35b610582600480360381019061057d9190611db3565b611277565b005b61058c611331565b6040516105999190611efc565b60405180910390f35b6105bc60048036038101906105b79190611db3565b611355565b005b6060600680546105cd906122af565b80601f01602080910402602001604051908101604052809291908181526020018280546105f9906122af565b80156106465780601f1061061b57610100808354040283529160200191610646565b820191906000526020600020905b81548152906001019060200180831161062957829003601f168201915b5050505050905090565b60015481565b600061066a61066361140f565b8484611417565b6001905092915050565b6000600554905090565b600061068b8484846115d0565b6000600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006106d661140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508281101561074d576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61076a8561075961140f565b858461076591906121bf565b611417565b60019150509392505050565b6000600860009054906101000a900460ff16905090565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107d961140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610822919061210f565b925050819055506001905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab81565b6000610857338361182c565b60019050919050565b600073735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141580156108fe575060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b15610935576040517fddb5de5e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61093f83836119e4565b8273ffffffffffffffffffffffffffffffffffffffff167f67fc7bdaed5b0ec550d8706b87d60568ab70c6b781263c70101d54cd1564aab373735b14bb79463307aacbed86daf3322b1e6226ab60405160200161099c9190611ee1565b604051602081830303815290604052846040516109ba929190611f92565b60405180910390a26001905092915050565b60025481565b6000600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b8060098190555050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600860019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606060078054610a7e906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610aaa906122af565b8015610af75780601f10610acc57610100808354040283529160200191610af7565b820191906000526020600020905b815481529060010190602001808311610ada57829003601f168201915b5050505050905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b600081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610b7161140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610be4576040517f10bad14700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000610c2e61140f565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610c7791906121bf565b925050819055506001905092915050565b60095481565b6000610ca2610c9b61140f565b84846115d0565b6001905092915050565b600a8054610cb9906122af565b80601f0160208091040260200160405190810160405280929190818152602001828054610ce5906122af565b8015610d325780601f10610d0757610100808354040283529160200191610d32565b820191906000526020600020905b815481529060010190602001808311610d1557829003601f168201915b505050505081565b6000806000610d47610f83565b915091508173ffffffffffffffffffffffffffffffffffffffff166323b872dd3373735b14bb79463307aacbed86daf3322b1e6226ab846040518463ffffffff1660e01b8152600401610d9c93929190611f17565b602060405180830381600087803b158015610db657600080fd5b505af1158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190611d2a565b610e24576040517f0a7cd6d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610e2e338561182c565b3373ffffffffffffffffffffffffffffffffffffffff167f9ffbffc04a397460ee1dbe8c9503e098090567d6b7f4b3c02a8617d800b6d955868684600254604051610e7c9493929190611fc2565b60405180910390a260019250505092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f09576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507fd55614e962c5fd6ece71614f6348d702468a997a394dd5e5c1677950226d97ae81604051610f789190611efc565b60405180910390a150565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630be155477f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611002919061204b565b60206040518083038186803b15801561101a57600080fd5b505afa15801561102e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110529190611c2a565b9050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156110bb576040517f78fff39600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d7fd7afb7f00000000000000000000000000000000000000000000000000000000000000006040518263ffffffff1660e01b8152600401611137919061204b565b60206040518083038186803b15801561114f57600080fd5b505afa158015611163573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111879190611de0565b905060008114156111c4576040517fe661aed000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600254600154836111d79190612165565b6111e1919061210f565b90508281945094505050509091565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146112f0576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806002819055507fef13af88e424b5d15f49c77758542c1938b08b8b95b91ed0751f98ba99000d8f81604051611326919061204b565b60405180910390a150565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b73735b14bb79463307aacbed86daf3322b1e6226ab73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146113ce576040517f2b2add3d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b806001819055507fff5788270f43bfc1ca41c503606d2594aa3023a1a7547de403a3e2f146a4a80a81604051611404919061204b565b60405180910390a150565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561147e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156114e5576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516115c3919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415611637576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16141561169e576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101561171c576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161172891906121bf565b600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546117ba919061210f565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161181e919061204b565b60405180910390a350505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611893576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015611911576040517ffe382aa700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b818161191d91906121bf565b600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550816005600082825461197291906121bf565b92505081905550600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516119d7919061204b565b60405180910390a3505050565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff161415611a4b576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060056000828254611a5d919061210f565b9250508190555080600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254611ab3919061210f565b925050819055508173ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051611b18919061204b565b60405180910390a35050565b6000611b37611b32846120a6565b612081565b905082815260208101848484011115611b5357611b526123f7565b5b611b5e84828561226d565b509392505050565b600081359050611b7581612438565b92915050565b600081519050611b8a81612438565b92915050565b600081519050611b9f8161244f565b92915050565b600082601f830112611bba57611bb96123f2565b5b8135611bca848260208601611b24565b91505092915050565b600081359050611be281612466565b92915050565b600081519050611bf781612466565b92915050565b600060208284031215611c1357611c12612401565b5b6000611c2184828501611b66565b91505092915050565b600060208284031215611c4057611c3f612401565b5b6000611c4e84828501611b7b565b91505092915050565b60008060408385031215611c6e57611c6d612401565b5b6000611c7c85828601611b66565b9250506020611c8d85828601611b66565b9150509250929050565b600080600060608486031215611cb057611caf612401565b5b6000611cbe86828701611b66565b9350506020611ccf86828701611b66565b9250506040611ce086828701611bd3565b9150509250925092565b60008060408385031215611d0157611d00612401565b5b6000611d0f85828601611b66565b9250506020611d2085828601611bd3565b9150509250929050565b600060208284031215611d4057611d3f612401565b5b6000611d4e84828501611b90565b91505092915050565b60008060408385031215611d6e57611d6d612401565b5b600083013567ffffffffffffffff811115611d8c57611d8b6123fc565b5b611d9885828601611ba5565b9250506020611da985828601611bd3565b9150509250929050565b600060208284031215611dc957611dc8612401565b5b6000611dd784828501611bd3565b91505092915050565b600060208284031215611df657611df5612401565b5b6000611e0484828501611be8565b91505092915050565b611e16816121f3565b82525050565b611e2d611e28826121f3565b612312565b82525050565b611e3c81612205565b82525050565b6000611e4d826120d7565b611e5781856120ed565b9350611e6781856020860161227c565b611e7081612406565b840191505092915050565b611e848161225b565b82525050565b6000611e95826120e2565b611e9f81856120fe565b9350611eaf81856020860161227c565b611eb881612406565b840191505092915050565b611ecc81612244565b82525050565b611edb8161224e565b82525050565b6000611eed8284611e1c565b60148201915081905092915050565b6000602082019050611f116000830184611e0d565b92915050565b6000606082019050611f2c6000830186611e0d565b611f396020830185611e0d565b611f466040830184611ec3565b949350505050565b6000604082019050611f636000830185611e0d565b611f706020830184611ec3565b9392505050565b6000602082019050611f8c6000830184611e33565b92915050565b60006040820190508181036000830152611fac8185611e42565b9050611fbb6020830184611ec3565b9392505050565b60006080820190508181036000830152611fdc8187611e42565b9050611feb6020830186611ec3565b611ff86040830185611ec3565b6120056060830184611ec3565b95945050505050565b60006020820190506120236000830184611e7b565b92915050565b600060208201905081810360008301526120438184611e8a565b905092915050565b60006020820190506120606000830184611ec3565b92915050565b600060208201905061207b6000830184611ed2565b92915050565b600061208b61209c565b905061209782826122e1565b919050565b6000604051905090565b600067ffffffffffffffff8211156120c1576120c06123c3565b5b6120ca82612406565b9050602081019050919050565b600081519050919050565b600081519050919050565b600082825260208201905092915050565b600082825260208201905092915050565b600061211a82612244565b915061212583612244565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561215a57612159612336565b5b828201905092915050565b600061217082612244565b915061217b83612244565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156121b4576121b3612336565b5b828202905092915050565b60006121ca82612244565b91506121d583612244565b9250828210156121e8576121e7612336565b5b828203905092915050565b60006121fe82612224565b9050919050565b60008115159050919050565b600081905061221f82612424565b919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226682612211565b9050919050565b82818337600083830152505050565b60005b8381101561229a57808201518184015260208101905061227f565b838111156122a9576000848401525b50505050565b600060028204905060018216806122c757607f821691505b602082108114156122db576122da612394565b5b50919050565b6122ea82612406565b810181811067ffffffffffffffff82111715612309576123086123c3565b5b80604052505050565b600061231d82612324565b9050919050565b600061232f82612417565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b60008160601b9050919050565b6003811061243557612434612365565b5b50565b612441816121f3565b811461244c57600080fd5b50565b61245881612205565b811461246357600080fd5b50565b61246f81612244565b811461247a57600080fd5b5056fea2646970667358221220b8278837e775bf149b356e2bff0f3e4931577c5c8ff848545ce78afb4e0ce8cb64736f6c63430008070033" } diff --git a/e2e/contracts/testzrc20/TestZRC20.sol b/e2e/contracts/testzrc20/TestZRC20.sol index c31ccb4686..3447949d06 100644 --- a/e2e/contracts/testzrc20/TestZRC20.sol +++ b/e2e/contracts/testzrc20/TestZRC20.sol @@ -85,6 +85,8 @@ contract TestZRC20 is IZRC20, IZRC20Metadata, ZRC20Errors { string private _symbol; uint8 private _decimals; + address public gatewayAddress; + /// @notice extend the contract with new fields to test contract bytecode upgrade uint256 public newField; string public newPublicField; diff --git a/e2e/contracts/testzrc20/bindings.go b/e2e/contracts/testzrc20/bindings.go index dc615b9770..934a84aad9 100644 --- a/e2e/contracts/testzrc20/bindings.go +++ b/e2e/contracts/testzrc20/bindings.go @@ -1,4 +1,4 @@ -//go:generate sh -c "solc --evm-version paris TestZRC20.sol --combined-json abi,bin | jq '.contracts.\"TestZRC20.sol:TestZRC20\"' > TestZRC20.json" +//go:generate sh -c "solc --evm-version london TestZRC20.sol --combined-json abi,bin | jq '.contracts.\"TestZRC20.sol:TestZRC20\"' > TestZRC20.json" //go:generate sh -c "cat TestZRC20.json | jq .abi > TestZRC20.abi" //go:generate sh -c "cat TestZRC20.json | jq .bin | tr -d '\"' > TestZRC20.bin" //go:generate sh -c "abigen --abi TestZRC20.abi --bin TestZRC20.bin --pkg testzrc20 --type TestZRC20 --out TestZRC20.go" diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 7cf7b946ed..fe053119db 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -113,10 +113,34 @@ const ( TestMigrateTSSName = "migrate_TSS" /* - Special tests + V2 smart contract tests + */ + TestV2ETHDepositName = "v2_eth_deposit" + TestV2ETHDepositAndCallName = "v2_eth_deposit_and_call" + TestV2ETHDepositAndCallRevertName = "v2_eth_deposit_and_call_revert" + TestV2ETHDepositAndCallRevertWithCallName = "v2_eth_deposit_and_call_revert_with_call" + TestV2ETHWithdrawName = "v2_eth_withdraw" + TestV2ETHWithdrawAndCallName = "v2_eth_withdraw_and_call" + TestV2ETHWithdrawAndCallRevertName = "v2_eth_withdraw_and_call_revert" + TestV2ETHWithdrawAndCallRevertWithCallName = "v2_eth_withdraw_and_call_revert_with_call" + TestV2ERC20DepositName = "v2_erc20_deposit" + TestV2ERC20DepositAndCallName = "v2_erc20_deposit_and_call" + TestV2ERC20DepositAndCallRevertName = "v2_erc20_deposit_and_call_revert" + TestV2ERC20DepositAndCallRevertWithCallName = "v2_erc20_deposit_and_call_revert_with_call" + TestV2ERC20WithdrawName = "v2_erc20_withdraw" + TestV2ERC20WithdrawAndCallName = "v2_erc20_withdraw_and_call" + TestV2ERC20WithdrawAndCallRevertName = "v2_erc20_withdraw_and_call_revert" + TestV2ERC20WithdrawAndCallRevertWithCallName = "v2_erc20_withdraw_and_call_revert_with_call" + TestV2ZEVMToEVMCallName = "v2_zevm_to_evm_call" + TestV2EVMToZEVMCallName = "v2_evm_to_zevm_call" + + /* + Operational tests Not used to test functionalities but do various interactions with the netwoks */ - TestDeploy = "deploy" + TestDeploy = "deploy" + TestOperationAddLiquidityETHName = "add_liquidity_eth" + TestOperationAddLiquidityERC20Name = "add_liquidity_erc20" ) // AllE2ETests is an ordered list of all e2e tests @@ -573,6 +597,12 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestCriticalAdminTransactions, ), + runner.NewE2ETest( + TestMigrateTSSName, + "migrate TSS funds", + []runner.ArgDefinition{}, + TestMigrateTSS, + ), runner.NewE2ETest( TestPauseERC20CustodyName, "pausing ERC20 custody on ZetaChain", @@ -585,6 +615,149 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestMigrateERC20CustodyFunds, ), + /* + V2 smart contract tests + */ + runner.NewE2ETest( + TestV2ETHDepositName, + "deposit Ether into ZEVM using V2 contract", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000000000000000000"}, + }, + TestV2ETHDeposit, + ), + runner.NewE2ETest( + TestV2ETHDepositAndCallName, + "deposit Ether into ZEVM and call a contract using V2 contract", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestV2ETHDepositAndCall, + ), + runner.NewE2ETest( + TestV2ETHDepositAndCallRevertName, + "deposit Ether into ZEVM and call a contract using V2 contract that reverts", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestV2ETHDepositAndCallRevert, + ), + runner.NewE2ETest( + TestV2ETHDepositAndCallRevertWithCallName, + "deposit Ether into ZEVM and call a contract using V2 contract that reverts with a onRevert call", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "10000000000000000"}, + }, + TestV2ETHDepositAndCallRevertWithCall, + ), + runner.NewE2ETest( + TestV2ETHWithdrawName, + "withdraw Ether from ZEVM using V2 contract", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000"}, + }, + TestV2ETHWithdraw, + ), + runner.NewE2ETest( + TestV2ETHWithdrawAndCallName, + "withdraw Ether from ZEVM and call a contract using V2 contract", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000"}, + }, + TestV2ETHWithdrawAndCall, + ), + runner.NewE2ETest( + TestV2ETHWithdrawAndCallRevertName, + "withdraw Ether from ZEVM and call a contract using V2 contract that reverts", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000"}, + }, + TestV2ETHWithdrawAndCallRevert, + ), + runner.NewE2ETest( + TestV2ETHWithdrawAndCallRevertWithCallName, + "withdraw Ether from ZEVM and call a contract using V2 contract that reverts with a onRevert call", + []runner.ArgDefinition{ + {Description: "amount in wei", DefaultValue: "100000"}, + }, + TestV2ETHWithdrawAndCallRevertWithCall, + ), + runner.NewE2ETest( + TestV2ERC20DepositName, + "deposit ERC20 into ZEVM using V2 contract", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "100000000000000000000"}, + }, + TestV2ERC20Deposit, + ), + runner.NewE2ETest( + TestV2ERC20DepositAndCallName, + "deposit ERC20 into ZEVM and call a contract using V2 contract", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "100000"}, + }, + TestV2ERC20DepositAndCall, + ), + runner.NewE2ETest( + TestV2ERC20DepositAndCallRevertName, + "deposit ERC20 into ZEVM and call a contract using V2 contract that reverts", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "10000000000000000000"}, + }, + TestV2ERC20DepositAndCallRevert, + ), + runner.NewE2ETest( + TestV2ERC20DepositAndCallRevertWithCallName, + "deposit ERC20 into ZEVM and call a contract using V2 contract that reverts with a onRevert call", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "10000000000000000000"}, + }, + TestV2ERC20DepositAndCallRevertWithCall, + ), + runner.NewE2ETest( + TestV2ERC20WithdrawName, + "withdraw ERC20 from ZEVM using V2 contract", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, + TestV2ERC20Withdraw, + ), + runner.NewE2ETest( + TestV2ERC20WithdrawAndCallName, + "withdraw ERC20 from ZEVM and call a contract using V2 contract", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, + TestV2ERC20WithdrawAndCall, + ), + runner.NewE2ETest( + TestV2ERC20WithdrawAndCallRevertName, + "withdraw ERC20 from ZEVM and call a contract using V2 contract that reverts", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, + TestV2ERC20WithdrawAndCallRevert, + ), + runner.NewE2ETest( + TestV2ERC20WithdrawAndCallRevertWithCallName, + "withdraw ERC20 from ZEVM and call a contract using V2 contract that reverts with a onRevert call", + []runner.ArgDefinition{ + {Description: "amount", DefaultValue: "1000"}, + }, + TestV2ERC20WithdrawAndCallRevertWithCall, + ), + runner.NewE2ETest( + TestV2ZEVMToEVMCallName, + "zevm -> evm call using V2 contract", + []runner.ArgDefinition{}, + TestV2ZEVMToEVMCall, + ), + runner.NewE2ETest( + TestV2EVMToZEVMCallName, + "evm -> zevm call using V2 contract", + []runner.ArgDefinition{}, + TestV2EVMToZEVMCall, + ), /* Special tests */ @@ -597,9 +770,21 @@ var AllE2ETests = []runner.E2ETest{ TestDeployContract, ), runner.NewE2ETest( - TestMigrateTSSName, - "migrate TSS funds", - []runner.ArgDefinition{}, - TestMigrateTSS, + TestOperationAddLiquidityETHName, + "add liquidity to the ZETA/ETH pool", + []runner.ArgDefinition{ + {Description: "amountZETA", DefaultValue: "50000000000000000000"}, + {Description: "amountETH", DefaultValue: "50000000000000000000"}, + }, + TestOperationAddLiquidityETH, + ), + runner.NewE2ETest( + TestOperationAddLiquidityERC20Name, + "add liquidity to the ZETA/ERC20 pool", + []runner.ArgDefinition{ + {Description: "amountZETA", DefaultValue: "50000000000000000000"}, + {Description: "amountERC20", DefaultValue: "50000000000000000000"}, + }, + TestOperationAddLiquidityERC20, ), } diff --git a/e2e/e2etests/test_message_passing_external_chains.go b/e2e/e2etests/test_message_passing_external_chains.go index 2c2ed65997..a533256794 100644 --- a/e2e/e2etests/test_message_passing_external_chains.go +++ b/e2e/e2etests/test_message_passing_external_chains.go @@ -5,7 +5,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" diff --git a/e2e/e2etests/test_message_passing_external_chains_revert_fail.go b/e2e/e2etests/test_message_passing_external_chains_revert_fail.go index 9cc1ee8d8f..12e4589a3d 100644 --- a/e2e/e2etests/test_message_passing_external_chains_revert_fail.go +++ b/e2e/e2etests/test_message_passing_external_chains_revert_fail.go @@ -6,7 +6,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" diff --git a/e2e/e2etests/test_migrate_chain_support.go b/e2e/e2etests/test_migrate_chain_support.go index b8a92cd472..62c9bac84f 100644 --- a/e2e/e2etests/test_migrate_chain_support.go +++ b/e2e/e2etests/test_migrate_chain_support.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/fatih/color" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/runner" diff --git a/e2e/e2etests/test_operation_add_liquidity_erc20.go b/e2e/e2etests/test_operation_add_liquidity_erc20.go new file mode 100644 index 0000000000..b52d22391e --- /dev/null +++ b/e2e/e2etests/test_operation_add_liquidity_erc20.go @@ -0,0 +1,25 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" +) + +// TestOperationAddLiquidityERC20 is an operational test to add liquidity in erc20 token +func TestOperationAddLiquidityERC20(r *runner.E2ERunner, args []string) { + require.Len(r, args, 2) + + liqZETA := big.NewInt(0) + _, ok := liqZETA.SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestOperationAddLiquidityERC20") + + liqERC20 := big.NewInt(0) + _, ok = liqERC20.SetString(args[1], 10) + require.True(r, ok, "Invalid amount specified for TestOperationAddLiquidityERC20") + + // perform the add liquidity + r.AddLiquidityERC20(liqZETA, liqERC20) +} diff --git a/e2e/e2etests/test_operation_add_liquidity_eth.go b/e2e/e2etests/test_operation_add_liquidity_eth.go new file mode 100644 index 0000000000..dae3ae4c65 --- /dev/null +++ b/e2e/e2etests/test_operation_add_liquidity_eth.go @@ -0,0 +1,25 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" +) + +// TestOperationAddLiquidityETH is an operational test to add liquidity in gas token +func TestOperationAddLiquidityETH(r *runner.E2ERunner, args []string) { + require.Len(r, args, 2) + + liqZETA := big.NewInt(0) + _, ok := liqZETA.SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestOperationAddLiquidityETH") + + liqETH := big.NewInt(0) + _, ok = liqETH.SetString(args[1], 10) + require.True(r, ok, "Invalid amount specified for TestOperationAddLiquidityETH") + + // perform the add liquidity + r.AddLiquidityETH(liqZETA, liqETH) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit.go b/e2e/e2etests/test_v2_erc20_deposit.go new file mode 100644 index 0000000000..2dd83c6b3a --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_deposit.go @@ -0,0 +1,38 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ERC20Deposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20Deposit") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + oldBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + + // perform the deposit + tx := r.V2ERC20Deposit(r.EVMAddress(), amount, gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the balance was updated + newBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + require.Equal(r, new(big.Int).Add(oldBalance, amount), newBalance) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call.go b/e2e/e2etests/test_v2_erc20_deposit_and_call.go new file mode 100644 index 0000000000..b6529840ec --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call.go @@ -0,0 +1,50 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageDepositERC20 = "this is a test ERC20 deposit and call payload" + +func TestV2ERC20DepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20DepositAndCall") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + r.AssertTestDAppZEVMCalled(false, payloadMessageDepositERC20, amount) + + oldBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + // perform the deposit + tx := r.V2ERC20DepositAndCall( + r.TestDAppV2ZEVMAddr, + amount, + []byte(payloadMessageDepositERC20), + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppZEVMCalled(true, payloadMessageDepositERC20, amount) + + // check the balance was updated + newBalance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.Equal(r, new(big.Int).Add(oldBalance, amount), newBalance) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go new file mode 100644 index 0000000000..3f15808303 --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert.go @@ -0,0 +1,45 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ERC20DepositAndCallRevert(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20DepositAndCallRevert") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + // use a random address to get the revert amount + revertAddress := sample.EthAddress() + balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + // perform the deposit + tx := r.V2ERC20DepositAndCall(r.TestDAppV2ZEVMAddr, amount, []byte("revert"), gatewayevm.RevertOptions{ + RevertAddress: revertAddress, + OnRevertGasLimit: big.NewInt(0), + }) + + // wait for the cctx to be reverted + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the balance is more than 0 + balance, err = r.ERC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.True(r, balance.Cmp(big.NewInt(0)) > 0) +} diff --git a/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go new file mode 100644 index 0000000000..790434f58c --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_deposit_and_call_revert_with_call.go @@ -0,0 +1,41 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageDepositOnRevertERC20 = "this is a test ERC20 deposit and call on revert" + +func TestV2ERC20DepositAndCallRevertWithCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20DepositAndCallRevertWithCall") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + r.AssertTestDAppEVMCalled(false, payloadMessageDepositOnRevertERC20, amount) + + // perform the deposit + tx := r.V2ERC20DepositAndCall(r.TestDAppV2ZEVMAddr, amount, []byte("revert"), gatewayevm.RevertOptions{ + RevertAddress: r.TestDAppV2EVMAddr, + CallOnRevert: true, + RevertMessage: []byte(payloadMessageDepositOnRevertERC20), + OnRevertGasLimit: big.NewInt(200000), + }) + + // wait for the cctx to be reverted + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppEVMCalled(true, payloadMessageDepositOnRevertERC20, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw.go b/e2e/e2etests/test_v2_erc20_withdraw.go new file mode 100644 index 0000000000..28b0c1ed80 --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw.go @@ -0,0 +1,30 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ERC20Withdraw(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20Withdraw") + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ERC20Withdraw(r.EVMAddress(), amount, gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go new file mode 100644 index 0000000000..609d74a8b9 --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call.go @@ -0,0 +1,41 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageWithdrawERC20 = "this is a test ERC20 withdraw and call payload" + +func TestV2ERC20WithdrawAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCall") + + r.AssertTestDAppEVMCalled(false, payloadMessageWithdrawERC20, amount) + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ERC20WithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeERC20Call(r.ERC20Addr, amount, payloadMessageWithdrawERC20), + gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + r.AssertTestDAppEVMCalled(true, payloadMessageWithdrawERC20, amount) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go new file mode 100644 index 0000000000..d5fe206aab --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert.go @@ -0,0 +1,51 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ERC20WithdrawAndCallRevert(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCallRevert") + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // use a random address to get the revert amount + revertAddress := sample.EthAddress() + balance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + // perform the withdraw + tx := r.V2ERC20WithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeERC20CallRevert(r.ERC20Addr, amount), + gatewayzevm.RevertOptions{ + RevertAddress: revertAddress, + OnRevertGasLimit: big.NewInt(0), + }, + ) + + // wait for the cctx to be reverted + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the balance is more than 0 + balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.True(r, balance.Cmp(big.NewInt(0)) > 0) +} diff --git a/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go new file mode 100644 index 0000000000..3d34bea2f7 --- /dev/null +++ b/e2e/e2etests/test_v2_erc20_withdraw_and_call_revert_with_call.go @@ -0,0 +1,46 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageWithdrawOnRevertERC20 = "this is a test ERC20 withdraw and call on revert" + +func TestV2ERC20WithdrawAndCallRevertWithCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ERC20WithdrawAndCallRevertWithCall") + + r.AssertTestDAppZEVMCalled(false, payloadMessageWithdrawOnRevertERC20, amount) + + r.ApproveERC20ZRC20(r.GatewayZEVMAddr) + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ERC20WithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeERC20CallRevert(r.ERC20Addr, amount), + gatewayzevm.RevertOptions{ + RevertAddress: r.TestDAppV2ZEVMAddr, + CallOnRevert: true, + RevertMessage: []byte(payloadMessageWithdrawOnRevertERC20), + OnRevertGasLimit: big.NewInt(0), + }, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + r.AssertTestDAppZEVMCalled(true, payloadMessageWithdrawOnRevertERC20, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_v2_eth_deposit.go b/e2e/e2etests/test_v2_eth_deposit.go new file mode 100644 index 0000000000..d99db8dcc3 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_deposit.go @@ -0,0 +1,29 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ETHDeposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHDeposit") + + r.Logger.Info("starting v2 eth deposit test") + + // perform the deposit + tx := r.V2ETHDeposit(r.EVMAddress(), amount, gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) +} diff --git a/e2e/e2etests/test_v2_eth_deposit_and_call.go b/e2e/e2etests/test_v2_eth_deposit_and_call.go new file mode 100644 index 0000000000..657d3068f7 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_deposit_and_call.go @@ -0,0 +1,48 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageDepositETH = "this is a test ETH deposit and call payload" + +func TestV2ETHDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHDepositAndCall") + + r.AssertTestDAppZEVMCalled(false, payloadMessageDepositETH, amount) + + oldBalance, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + // perform the deposit and call to the TestDAppV2ZEVMAddr + tx := r.V2ETHDepositAndCall( + r.TestDAppV2ZEVMAddr, + amount, + []byte(payloadMessageDepositETH), + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit_and_call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppZEVMCalled(true, payloadMessageDepositETH, amount) + + // check the balance was updated + newBalance, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.Equal(r, new(big.Int).Add(oldBalance, amount), newBalance) +} diff --git a/e2e/e2etests/test_v2_eth_deposit_and_call_revert.go b/e2e/e2etests/test_v2_eth_deposit_and_call_revert.go new file mode 100644 index 0000000000..f8093f629a --- /dev/null +++ b/e2e/e2etests/test_v2_eth_deposit_and_call_revert.go @@ -0,0 +1,44 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ETHDepositAndCallRevert(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHDepositAndCallRevert") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + // use a random address to get the revert amount + revertAddress := sample.EthAddress() + balance, err := r.EVMClient.BalanceAt(r.Ctx, revertAddress, nil) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + // perform the deposit + tx := r.V2ETHDepositAndCall(r.TestDAppV2ZEVMAddr, amount, []byte("revert"), gatewayevm.RevertOptions{ + RevertAddress: revertAddress, + OnRevertGasLimit: big.NewInt(0), + }) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the balance is more than 0 + balance, err = r.EVMClient.BalanceAt(r.Ctx, revertAddress, nil) + require.NoError(r, err) + require.True(r, balance.Cmp(big.NewInt(0)) > 0) +} diff --git a/e2e/e2etests/test_v2_eth_deposit_and_call_revert_with_call.go b/e2e/e2etests/test_v2_eth_deposit_and_call_revert_with_call.go new file mode 100644 index 0000000000..ad23989e38 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_deposit_and_call_revert_with_call.go @@ -0,0 +1,41 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageDepositOnRevertETH = "this is a test ETH deposit and call on revert" + +func TestV2ETHDepositAndCallRevertWithCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHDepositAndCallRevertWithCall") + + r.ApproveERC20OnEVM(r.GatewayEVMAddr) + + r.AssertTestDAppEVMCalled(false, payloadMessageDepositOnRevertETH, amount) + + // perform the deposit + tx := r.V2ETHDepositAndCall(r.TestDAppV2ZEVMAddr, amount, []byte("revert"), gatewayevm.RevertOptions{ + RevertAddress: r.TestDAppV2EVMAddr, + CallOnRevert: true, + RevertMessage: []byte(payloadMessageDepositOnRevertETH), + OnRevertGasLimit: big.NewInt(200000), + }) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppEVMCalled(true, payloadMessageDepositOnRevertETH, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw.go b/e2e/e2etests/test_v2_eth_withdraw.go new file mode 100644 index 0000000000..5445040b08 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_withdraw.go @@ -0,0 +1,37 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ETHWithdraw(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHWithdraw") + + oldBalance, err := r.EVMClient.BalanceAt(r.Ctx, r.EVMAddress(), nil) + require.NoError(r, err) + + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ETHWithdraw(r.EVMAddress(), amount, gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the balance was updated, we just check newBalance is greater than oldBalance because of the gas fee + newBalance, err := r.EVMClient.BalanceAt(r.Ctx, r.EVMAddress(), nil) + require.NoError(r, err) + require.Greater(r, newBalance.Uint64(), oldBalance.Uint64()) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call.go b/e2e/e2etests/test_v2_eth_withdraw_and_call.go new file mode 100644 index 0000000000..5a54092288 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call.go @@ -0,0 +1,40 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageWithdrawETH = "this is a test ETH withdraw and call payload" + +func TestV2ETHWithdrawAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHWithdrawAndCall") + + r.AssertTestDAppEVMCalled(false, payloadMessageWithdrawETH, amount) + + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ETHWithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeGasCall(payloadMessageWithdrawETH), + gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + r.AssertTestDAppEVMCalled(true, payloadMessageWithdrawETH, amount) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go new file mode 100644 index 0000000000..7f4ce48224 --- /dev/null +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert.go @@ -0,0 +1,45 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestV2ETHWithdrawAndCallRevert(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHWithdrawAndCall") + + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // use a random address to get the revert amount + revertAddress := sample.EthAddress() + balance, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + // perform the withdraw + tx := r.V2ETHWithdrawAndCall(r.TestDAppV2EVMAddr, amount, r.EncodeGasCall("revert"), gatewayzevm.RevertOptions{ + RevertAddress: revertAddress, + OnRevertGasLimit: big.NewInt(0), + }) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + // check the balance is more than 0 + balance, err = r.ETHZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.True(r, balance.Cmp(big.NewInt(0)) > 0) +} diff --git a/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go new file mode 100644 index 0000000000..638e50a39c --- /dev/null +++ b/e2e/e2etests/test_v2_eth_withdraw_and_call_revert_with_call.go @@ -0,0 +1,45 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageWithdrawOnRevertETH = "this is a test ETH withdraw and call on revert" + +func TestV2ETHWithdrawAndCallRevertWithCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount, ok := big.NewInt(0).SetString(args[0], 10) + require.True(r, ok, "Invalid amount specified for TestV2ETHWithdrawAndCallRevertWithCall") + + r.AssertTestDAppZEVMCalled(false, payloadMessageWithdrawOnRevertETH, amount) + + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ETHWithdrawAndCall( + r.TestDAppV2EVMAddr, + amount, + r.EncodeGasCall("revert"), + gatewayzevm.RevertOptions{ + RevertAddress: r.TestDAppV2ZEVMAddr, + CallOnRevert: true, + RevertMessage: []byte(payloadMessageWithdrawOnRevertETH), + OnRevertGasLimit: big.NewInt(0), + }, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "withdraw") + require.Equal(r, crosschaintypes.CctxStatus_Reverted, cctx.CctxStatus.Status) + + r.AssertTestDAppZEVMCalled(true, payloadMessageWithdrawOnRevertETH, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_v2_evm_to_zevm_call.go b/e2e/e2etests/test_v2_evm_to_zevm_call.go new file mode 100644 index 0000000000..44fce408d1 --- /dev/null +++ b/e2e/e2etests/test_v2_evm_to_zevm_call.go @@ -0,0 +1,35 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageZEVMCall = "this is a test ZEVM call payload" + +func TestV2EVMToZEVMCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 0) + + r.AssertTestDAppZEVMCalled(false, payloadMessageZEVMCall, big.NewInt(0)) + + // perform the withdraw + tx := r.V2EVMToZEMVCall( + r.TestDAppV2ZEVMAddr, + []byte(payloadMessageZEVMCall), + gatewayevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + ) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppZEVMCalled(true, payloadMessageZEVMCall, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_v2_zevm_to_evm_call.go b/e2e/e2etests/test_v2_zevm_to_evm_call.go new file mode 100644 index 0000000000..9132e72837 --- /dev/null +++ b/e2e/e2etests/test_v2_zevm_to_evm_call.go @@ -0,0 +1,36 @@ +package e2etests + +import ( + "math/big" + + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +const payloadMessageEVMCall = "this is a test EVM call payload" + +func TestV2ZEVMToEVMCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 0) + + r.AssertTestDAppEVMCalled(false, payloadMessageEVMCall, big.NewInt(0)) + + // Necessary approval for fee payment + r.ApproveETHZRC20(r.GatewayZEVMAddr) + + // perform the withdraw + tx := r.V2ZEVMToEMVCall(r.TestDAppV2EVMAddr, r.EncodeSimpleCall(payloadMessageEVMCall), gatewayzevm.RevertOptions{ + OnRevertGasLimit: big.NewInt(0), + }) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, tx.Hash().Hex(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "call") + require.Equal(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + + // check the payload was received on the contract + r.AssertTestDAppEVMCalled(true, payloadMessageEVMCall, big.NewInt(0)) +} diff --git a/e2e/e2etests/test_whitelist_erc20.go b/e2e/e2etests/test_whitelist_erc20.go index 5a09decd71..0fa043fe03 100644 --- a/e2e/e2etests/test_whitelist_erc20.go +++ b/e2e/e2etests/test_whitelist_erc20.go @@ -7,7 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/e2e/contracts/erc20" "github.com/zeta-chain/zetacore/e2e/runner" diff --git a/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go b/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go index 3685dccfb3..6f9586de45 100644 --- a/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go +++ b/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go @@ -4,7 +4,7 @@ import ( "math/big" "github.com/stretchr/testify/require" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" "github.com/zeta-chain/zetacore/e2e/runner" "github.com/zeta-chain/zetacore/e2e/utils" diff --git a/e2e/runner/accounting.go b/e2e/runner/accounting.go index 6b70e5f19c..7f357a69ab 100644 --- a/e2e/runner/accounting.go +++ b/e2e/runner/accounting.go @@ -166,18 +166,24 @@ func (r *E2ERunner) CheckSolanaTSSBalance() error { } func (r *E2ERunner) checkERC20TSSBalance() error { - erc20Balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyAddr) + custodyBalance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyAddr) if err != nil { return err } + custodyV2Balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyV2Addr) + if err != nil { + return err + } + custodyFullBalance := big.NewInt(0).Add(custodyBalance, custodyV2Balance) + erc20zrc20Supply, err := r.ERC20ZRC20.TotalSupply(&bind.CallOpts{}) if err != nil { return err } - if erc20Balance.Cmp(erc20zrc20Supply) < 0 { - return fmt.Errorf("ERC20: TSS balance (%d) < ZRC20 TotalSupply (%d) ", erc20Balance, erc20zrc20Supply) + if custodyFullBalance.Cmp(erc20zrc20Supply) < 0 { + return fmt.Errorf("ERC20: TSS balance (%d) < ZRC20 TotalSupply (%d) ", custodyFullBalance, erc20zrc20Supply) } - r.Logger.Info("ERC20: TSS balance (%d) >= ERC20 ZRC20 TotalSupply (%d)", erc20Balance, erc20zrc20Supply) + r.Logger.Info("ERC20: TSS balance (%d) >= ERC20 ZRC20 TotalSupply (%d)", custodyFullBalance, erc20zrc20Supply) return nil } diff --git a/e2e/runner/evm.go b/e2e/runner/evm.go index 10fd599d63..3b5e7f8d90 100644 --- a/e2e/runner/evm.go +++ b/e2e/runner/evm.go @@ -5,6 +5,7 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" @@ -164,6 +165,54 @@ func (r *E2ERunner) SendEther(_ ethcommon.Address, value *big.Int, data []byte) return signedTx, nil } +// ApproveERC20OnEVM approves ERC20 on EVM to a specific address +// check if allowance is zero before calling this method +// allow a high amount to avoid multiple approvals +func (r *E2ERunner) ApproveERC20OnEVM(allowed ethcommon.Address) { + allowance, err := r.ERC20.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 { + tx, err := r.ERC20.Approve(r.EVMAuth, allowed, big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000000))) + require.NoError(r, err) + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + require.True(r, receipt.Status == 1, "approval failed") + } +} + +// ApproveETHZRC20 approves ETH ZRC20 on EVM to a specific address +// check if allowance is zero before calling this method +// allow a high amount to avoid multiple approvals +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 { + 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) + require.True(r, receipt.Status == 1, "approval failed") + } +} + +// ApproveERC20ZRC20 approves ERC20 ZRC20 on EVM to a specific address +// check if allowance is zero before calling this method +// allow a high amount to avoid multiple approvals +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 { + 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) + require.True(r, receipt.Status == 1, "approval failed") + } +} + // AnvilMineBlocks mines blocks on Anvil localnet // the block time is provided in seconds // the method returns a function to stop the mining diff --git a/e2e/runner/liquidity.go b/e2e/runner/liquidity.go new file mode 100644 index 0000000000..05f83e6c44 --- /dev/null +++ b/e2e/runner/liquidity.go @@ -0,0 +1,82 @@ +package runner + +import ( + "math/big" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/utils" +) + +// AddLiquidityETH adds liquidity token to the uniswap pool ZETA/ETH +// we use the provided amount of ZETA and ETH to add liquidity as wanted amount +// 0 is used for the minimum amount of ZETA and ETH +func (r *E2ERunner) AddLiquidityETH(amountZETA, amountETH *big.Int) { + // approve uni router + r.ApproveETHZRC20(r.UniswapV2RouterAddr) + + previousValue := r.ZEVMAuth.Value + r.ZEVMAuth.Value = amountZETA + defer func() { + r.ZEVMAuth.Value = previousValue + }() + + r.Logger.Info("Adding liquidity to ZETA/ETH pool") + tx, err := r.UniswapV2Router.AddLiquidityETH( + r.ZEVMAuth, + r.ETHZRC20Addr, + amountETH, + big.NewInt(0), + big.NewInt(0), + r.EVMAddress(), + big.NewInt(time.Now().Add(10*time.Minute).Unix()), + ) + require.NoError(r, err) + + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + require.EqualValues(r, types.ReceiptStatusSuccessful, receipt.Status, "add liquidity failed") + + // get the pair address + pairAddress, err := r.UniswapV2Factory.GetPair(&bind.CallOpts{}, r.WZetaAddr, r.ETHZRC20Addr) + require.NoError(r, err) + + r.Logger.Info("ZETA/ETH pair address: %s", pairAddress.Hex()) +} + +// AddLiquidityERC20 adds liquidity token to the uniswap pool ZETA/ERC20 +// we use the provided amount of ZETA and ERC20 to add liquidity as wanted amount +// 0 is used for the minimum amount of ZETA and ERC20 +func (r *E2ERunner) AddLiquidityERC20(amountZETA, amountERC20 *big.Int) { + // approve uni router + r.ApproveERC20ZRC20(r.UniswapV2RouterAddr) + + previousValue := r.ZEVMAuth.Value + r.ZEVMAuth.Value = amountZETA + defer func() { + r.ZEVMAuth.Value = previousValue + }() + + r.Logger.Info("Adding liquidity to ZETA/ERC20 pool") + tx, err := r.UniswapV2Router.AddLiquidityETH( + r.ZEVMAuth, + r.ERC20ZRC20Addr, + amountERC20, + big.NewInt(0), + big.NewInt(0), + r.EVMAddress(), + big.NewInt(time.Now().Add(10*time.Minute).Unix()), + ) + require.NoError(r, err) + + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + require.EqualValues(r, types.ReceiptStatusSuccessful, receipt.Status, "add liquidity failed") + + // get the pair address + pairAddress, err := r.UniswapV2Factory.GetPair(&bind.CallOpts{}, r.WZetaAddr, r.ERC20ZRC20Addr) + require.NoError(r, err) + + r.Logger.Info("ZETA/ERC20 pair address: %s", pairAddress.Hex()) +} diff --git a/e2e/runner/logger.go b/e2e/runner/logger.go index 49ab8af29c..683154f056 100644 --- a/e2e/runner/logger.go +++ b/e2e/runner/logger.go @@ -1,12 +1,14 @@ package runner import ( + "encoding/hex" "fmt" "sync" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/fatih/color" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" ) @@ -194,11 +196,36 @@ func (l *Logger) ZRC20Withdrawal( event.From.Hex(), event.To, event.Value, - event.Gasfee, + event.GasFee, ) } } +type depositParser interface { + ParseDeposited(ethtypes.Log) (*gatewayevm.GatewayEVMDeposited, error) +} + +// GatewayDeposit prints a GatewayDeposit event +func (l *Logger) GatewayDeposit( + contract depositParser, + receipt ethtypes.Receipt, + name string, +) { + for _, log := range receipt.Logs { + event, err := contract.ParseDeposited(*log) + if err != nil { + continue + } + + l.Info(" Gateway Deposit: %s", name) + l.Info(" Sender: %s", event.Sender.Hex()) + l.Info(" Receiver: %s", event.Receiver.Hex()) + l.Info(" Amount: %s", event.Amount.String()) + l.Info(" Asset: %s", event.Asset.Hex()) + l.Info(" Payload: %s", hex.EncodeToString(event.Payload)) + } +} + func (l *Logger) getPrefixWithPadding() string { // add padding to prefix prefix := l.prefix diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index 1dfe0d3fb8..77d068c398 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -17,15 +17,18 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - zetaeth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" - uniswapv2router "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + zetaeth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" + uniswapv2router "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" @@ -33,6 +36,7 @@ import ( "github.com/zeta-chain/zetacore/e2e/contracts/zevmswap" "github.com/zeta-chain/zetacore/e2e/txserver" "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" @@ -140,6 +144,20 @@ type E2ERunner struct { Logger *Logger BitcoinParams *chaincfg.Params mutex sync.Mutex + + // evm v2 + GatewayEVMAddr ethcommon.Address + GatewayEVM *gatewayevm.GatewayEVM + ERC20CustodyV2Addr ethcommon.Address + ERC20CustodyV2 *erc20custodyv2.ERC20Custody + TestDAppV2EVMAddr ethcommon.Address + TestDAppV2EVM *testdappv2.TestDAppV2 + + // zevm v2 + GatewayZEVMAddr ethcommon.Address + GatewayZEVM *gatewayzevm.GatewayZEVM + TestDAppV2ZEVMAddr ethcommon.Address + TestDAppV2ZEVM *testdappv2.TestDAppV2 } func NewE2ERunner( @@ -285,6 +303,35 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { if err != nil { return err } + + // v2 contracts + r.GatewayEVMAddr = other.GatewayEVMAddr + r.GatewayEVM, err = gatewayevm.NewGatewayEVM(r.GatewayEVMAddr, r.EVMClient) + if err != nil { + return err + } + r.ERC20CustodyV2Addr = other.ERC20CustodyV2Addr + r.ERC20CustodyV2, err = erc20custodyv2.NewERC20Custody(r.ERC20CustodyV2Addr, r.EVMClient) + if err != nil { + return err + } + r.TestDAppV2EVMAddr = other.TestDAppV2EVMAddr + r.TestDAppV2EVM, err = testdappv2.NewTestDAppV2(r.TestDAppV2EVMAddr, r.EVMClient) + if err != nil { + return err + } + + r.GatewayZEVMAddr = other.GatewayZEVMAddr + r.GatewayZEVM, err = gatewayzevm.NewGatewayZEVM(r.GatewayZEVMAddr, r.ZEVMClient) + if err != nil { + return err + } + r.TestDAppV2ZEVMAddr = other.TestDAppV2ZEVMAddr + r.TestDAppV2ZEVM, err = testdappv2.NewTestDAppV2(r.TestDAppV2ZEVMAddr, r.ZEVMClient) + if err != nil { + return err + } + return nil } @@ -327,6 +374,17 @@ func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print("ERC20Custody: %s", r.ERC20CustodyAddr.Hex()) r.Logger.Print("ERC20: %s", r.ERC20Addr.Hex()) r.Logger.Print("TestDappEVM: %s", r.EvmTestDAppAddr.Hex()) + + // v2 contracts + + r.Logger.Print(" --- 📜zEVM v2 contracts ---") + r.Logger.Print("GatewayZEVM: %s", r.GatewayZEVMAddr.Hex()) + r.Logger.Print("TestDAppV2ZEVM: %s", r.TestDAppV2ZEVMAddr.Hex()) + + r.Logger.Print(" --- 📜EVM v2 contracts ---") + r.Logger.Print("GatewayEVM: %s", r.GatewayEVMAddr.Hex()) + r.Logger.Print("ERC20CustodyV2: %s", r.ERC20CustodyV2Addr.Hex()) + r.Logger.Print("TestDAppV2EVM: %s", r.TestDAppV2EVMAddr.Hex()) } // IsRunningUpgrade returns true if the test is running an upgrade test suite. diff --git a/e2e/runner/setup_evm.go b/e2e/runner/setup_evm.go index 74652ed163..ab8d3edd16 100644 --- a/e2e/runner/setup_evm.go +++ b/e2e/runner/setup_evm.go @@ -7,9 +7,9 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - zetaeth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + zetaeth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/contracts/erc20" diff --git a/e2e/runner/setup_zeta.go b/e2e/runner/setup_zeta.go index eaee86bdbb..4f72f72108 100644 --- a/e2e/runner/setup_zeta.go +++ b/e2e/runner/setup_zeta.go @@ -8,12 +8,12 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" - uniswapv2router "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" + uniswapv2router "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/e2e/contracts/contextapp" "github.com/zeta-chain/zetacore/e2e/contracts/testdapp" @@ -65,8 +65,8 @@ func (r *E2ERunner) SetTSSAddresses() error { return nil } -// SetZEVMContracts set contracts for the ZEVM -func (r *E2ERunner) SetZEVMContracts() { +// SetZEVMSystemContracts set system contracts for the ZEVM +func (r *E2ERunner) SetZEVMSystemContracts() { r.Logger.Print("⚙️ deploying system contracts and ZRC20s on ZEVM") startTime := time.Now() defer func() { @@ -74,18 +74,12 @@ func (r *E2ERunner) SetZEVMContracts() { }() // deploy system contracts and ZRC20 contracts on ZetaChain - addresses, err := r.ZetaTxServer.DeploySystemContractsAndZRC20( + addresses, err := r.ZetaTxServer.DeploySystemContracts( e2eutils.OperationalPolicyName, e2eutils.AdminPolicyName, - r.ERC20Addr.Hex(), ) require.NoError(r, err) - // Set ERC20ZRC20Addr - r.ERC20ZRC20Addr = ethcommon.HexToAddress(addresses.ERC20zrc20Addr) - r.ERC20ZRC20, err = zrc20.NewZRC20(r.ERC20ZRC20Addr, r.ZEVMClient) - require.NoError(r, err) - // UniswapV2FactoryAddr r.UniswapV2FactoryAddr = ethcommon.HexToAddress(addresses.UniswapV2FactoryAddr) r.UniswapV2Factory, err = uniswapv2factory.NewUniswapV2Factory(r.UniswapV2FactoryAddr, r.ZEVMClient) @@ -123,11 +117,6 @@ func (r *E2ERunner) SetZEVMContracts() { r.SystemContract = systemContract r.SystemContractAddr = systemContractAddr - // set ZRC20 contracts - r.SetupETHZRC20() - r.SetupBTCZRC20() - r.SetupSOLZRC20() - // deploy TestDApp contract on zEVM appAddr, txApp, _, err := testdapp.DeployTestDApp( r.ZEVMAuth, @@ -177,6 +166,33 @@ func (r *E2ERunner) SetZEVMContracts() { r.ContextApp = contextApp } +// SetZEVMZRC20s set ZRC20 for the ZEVM +func (r *E2ERunner) SetZEVMZRC20s() { + r.Logger.Print("⚙️ deploying system contracts and ZRC20s on ZEVM") + startTime := time.Now() + defer func() { + r.Logger.Info("System contract deployments took %s\n", time.Since(startTime)) + }() + + // deploy system contracts and ZRC20 contracts on ZetaChain + erc20zrc20Addr, err := r.ZetaTxServer.DeployZRC20s( + e2eutils.OperationalPolicyName, + e2eutils.AdminPolicyName, + r.ERC20Addr.Hex(), + ) + require.NoError(r, err) + + // Set ERC20ZRC20Addr + r.ERC20ZRC20Addr = ethcommon.HexToAddress(erc20zrc20Addr) + r.ERC20ZRC20, err = zrc20.NewZRC20(r.ERC20ZRC20Addr, r.ZEVMClient) + require.NoError(r, err) + + // set ZRC20 contracts + r.SetupETHZRC20() + r.SetupBTCZRC20() + r.SetupSOLZRC20() +} + // SetupETHZRC20 sets up the ETH ZRC20 in the runner from the values queried from the chain func (r *E2ERunner) SetupETHZRC20() { ethZRC20Addr, err := r.SystemContract.GasCoinZRC20ByChainId( diff --git a/e2e/runner/testdapp.go b/e2e/runner/testdapp.go new file mode 100644 index 0000000000..5515067aae --- /dev/null +++ b/e2e/runner/testdapp.go @@ -0,0 +1,103 @@ +package runner + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" +) + +// AssertTestDAppZEVMCalled is a function that asserts the values of the test dapp on the ZEVM +// this function uses TestDAppV2 for the assertions, in the future we should only use this contracts for all tests +// https://github.com/zeta-chain/node/issues/2655 +func (r *E2ERunner) AssertTestDAppZEVMCalled(expectedCalled bool, message string, amount *big.Int) { + r.assertTestDAppCalled(r.TestDAppV2ZEVM, message, expectedCalled, amount) +} + +// AssertTestDAppEVMCalled is a function that asserts the values of the test dapp on the external EVM +func (r *E2ERunner) AssertTestDAppEVMCalled(expectedCalled bool, message string, amount *big.Int) { + r.assertTestDAppCalled(r.TestDAppV2EVM, message, expectedCalled, amount) +} + +func (r *E2ERunner) assertTestDAppCalled( + testDApp *testdappv2.TestDAppV2, + message string, + expectedCalled bool, + expectedAmount *big.Int, +) { + // check the payload was received on the contract + called, err := testDApp.GetCalledWithMessage(&bind.CallOpts{}, message) + require.NoError(r, err) + require.EqualValues(r, expectedCalled, called) + + if expectedCalled { + amount, err := testDApp.GetAmountWithMessage(&bind.CallOpts{}, message) + require.NoError(r, err) + require.EqualValues( + r, + expectedAmount.Uint64(), + amount.Uint64(), + "Amounts do not match, expected %s, actual %s", + expectedAmount.String(), + amount.String(), + ) + } +} + +// EncodeGasCall encodes the payload for the gasCall function +func (r *E2ERunner) EncodeGasCall(message string) []byte { + abi, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(r, err) + + // encode the message + encoded, err := abi.Pack("gasCall", message) + require.NoError(r, err) + return encoded +} + +// EncodeGasCallRevert encodes the payload for the gasCall function that reverts +func (r *E2ERunner) EncodeGasCallRevert() []byte { + abi, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(r, err) + + // encode the message + encoded, err := abi.Pack("gasCall", "revert") + require.NoError(r, err) + return encoded +} + +// EncodeERC20Call encodes the payload for the erc20Call function +func (r *E2ERunner) EncodeERC20Call(erc20Addr ethcommon.Address, amount *big.Int, message string) []byte { + abi, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(r, err) + + // encode the message + encoded, err := abi.Pack("erc20Call", erc20Addr, amount, message) + require.NoError(r, err) + return encoded +} + +// EncodeERC20CallRevert encodes the payload for the erc20Call function that reverts +func (r *E2ERunner) EncodeERC20CallRevert(erc20Addr ethcommon.Address, amount *big.Int) []byte { + abi, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(r, err) + + // encode the message + encoded, err := abi.Pack("erc20Call", erc20Addr, amount, "revert") + require.NoError(r, err) + return encoded +} + +// EncodeSimpleCall encodes the payload for the simpleCall function +func (r *E2ERunner) EncodeSimpleCall(message string) []byte { + abi, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(r, err) + + // encode the message + encoded, err := abi.Pack("simpleCall", message) + require.NoError(r, err) + return encoded +} diff --git a/e2e/runner/v2_evm.go b/e2e/runner/v2_evm.go new file mode 100644 index 0000000000..09a8ed7c46 --- /dev/null +++ b/e2e/runner/v2_evm.go @@ -0,0 +1,117 @@ +package runner + +import ( + "math/big" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/utils" +) + +// V2ETHDeposit calls Deposit of Gateway with gas token on EVM +func (r *E2ERunner) V2ETHDeposit( + receiver ethcommon.Address, + amount *big.Int, + revertOptions gatewayevm.RevertOptions, +) *ethtypes.Transaction { + // set the value of the transaction + previousValue := r.EVMAuth.Value + defer func() { + r.EVMAuth.Value = previousValue + }() + r.EVMAuth.Value = amount + + tx, err := r.GatewayEVM.Deposit0(r.EVMAuth, receiver, revertOptions) + require.NoError(r, err) + + logDepositInfoAndWaitForTxReceipt(r, tx, "eth_deposit") + + return tx +} + +// V2ETHDepositAndCall calls DepositAndCall of Gateway with gas token on EVM +func (r *E2ERunner) V2ETHDepositAndCall( + receiver ethcommon.Address, + amount *big.Int, + payload []byte, + revertOptions gatewayevm.RevertOptions, +) *ethtypes.Transaction { + // set the value of the transaction + previousValue := r.EVMAuth.Value + defer func() { + r.EVMAuth.Value = previousValue + }() + r.EVMAuth.Value = amount + + tx, err := r.GatewayEVM.DepositAndCall(r.EVMAuth, receiver, payload, revertOptions) + require.NoError(r, err) + + logDepositInfoAndWaitForTxReceipt(r, tx, "eth_deposit_and_call") + + return tx +} + +// V2ERC20Deposit calls Deposit of Gateway with erc20 token on EVM +func (r *E2ERunner) V2ERC20Deposit( + receiver ethcommon.Address, + amount *big.Int, + revertOptions gatewayevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayEVM.Deposit(r.EVMAuth, receiver, amount, r.ERC20Addr, revertOptions) + require.NoError(r, err) + + logDepositInfoAndWaitForTxReceipt(r, tx, "erc20_deposit") + + return tx +} + +// V2ERC20DepositAndCall calls DepositAndCall of Gateway with erc20 token on EVM +func (r *E2ERunner) V2ERC20DepositAndCall( + receiver ethcommon.Address, + amount *big.Int, + payload []byte, + revertOptions gatewayevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayEVM.DepositAndCall0( + r.EVMAuth, + receiver, + amount, + r.ERC20Addr, + payload, + revertOptions, + ) + require.NoError(r, err) + + logDepositInfoAndWaitForTxReceipt(r, tx, "erc20_deposit_and_call") + + return tx +} + +// V2EVMToZEMVCall calls Call of Gateway on EVM +func (r *E2ERunner) V2EVMToZEMVCall( + receiver ethcommon.Address, + payload []byte, + revertOptions gatewayevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayEVM.Call(r.EVMAuth, receiver, payload, revertOptions) + require.NoError(r, err) + + return tx +} + +func logDepositInfoAndWaitForTxReceipt( + r *E2ERunner, + tx *ethtypes.Transaction, + name string, +) { + r.Logger.EVMTransaction(*tx, name) + + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, name+" failed") + + r.Logger.EVMReceipt(*receipt, name) + r.Logger.GatewayDeposit(r.GatewayEVM, *receipt, name) +} diff --git a/e2e/runner/v2_setup_evm.go b/e2e/runner/v2_setup_evm.go new file mode 100644 index 0000000000..001da12e04 --- /dev/null +++ b/e2e/runner/v2_setup_evm.go @@ -0,0 +1,108 @@ +package runner + +import ( + "math/big" + "time" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/contracts/erc1967proxy" + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" +) + +// SetupEVMV2 setup contracts on EVM with v2 contracts +func (r *E2ERunner) SetupEVMV2() { + ensureTxReceipt := func(tx *ethtypes.Transaction, failMessage string) { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, failMessage) + } + + r.Logger.Print("⚙️ setting up EVM v2 network") + startTime := time.Now() + defer func() { + r.Logger.Info("EVM v2 setup took %s\n", time.Since(startTime)) + }() + + r.Logger.InfoLoud("Deploy Gateway and ERC20Custody ERC20\n") + + // donate to the TSS address to avoid account errors because deploying gas token ZRC20 will automatically mint + // gas token on ZetaChain to initialize the pool + txDonation, err := r.SendEther(r.TSSAddress, big.NewInt(101000000000000000), []byte(constant.DonationMessage)) + require.NoError(r, err) + + r.Logger.Info("Deploying Gateway EVM") + gatewayEVMAddr, txGateway, _, err := gatewayevm.DeployGatewayEVM(r.EVMAuth, r.EVMClient) + require.NoError(r, err) + + ensureTxReceipt(txGateway, "Gateway deployment failed") + + gatewayEVMABI, err := gatewayevm.GatewayEVMMetaData.GetAbi() + require.NoError(r, err) + + // Encode the initializer data + initializerData, err := gatewayEVMABI.Pack("initialize", r.TSSAddress, r.ZetaEthAddr, r.Account.EVMAddress()) + require.NoError(r, err) + + // Deploy the proxy contract + proxyAddress, txProxy, _, err := erc1967proxy.DeployERC1967Proxy( + r.EVMAuth, + r.EVMClient, + gatewayEVMAddr, + initializerData, + ) + require.NoError(r, err) + + r.GatewayEVMAddr = proxyAddress + r.GatewayEVM, err = gatewayevm.NewGatewayEVM(proxyAddress, r.EVMClient) + require.NoError(r, err) + r.Logger.Info("Gateway EVM contract address: %s, tx hash: %s", gatewayEVMAddr.Hex(), txGateway.Hash().Hex()) + + r.Logger.Info("Deploying ERC20Custody contract") + erc20CustodyNewAddr, txCustody, erc20CustodyNew, err := erc20custodyv2.DeployERC20Custody( + r.EVMAuth, + r.EVMClient, + r.GatewayEVMAddr, + r.TSSAddress, + r.Account.EVMAddress(), + ) + require.NoError(r, err) + + r.ERC20CustodyV2Addr = erc20CustodyNewAddr + r.ERC20CustodyV2 = erc20CustodyNew + r.Logger.Info( + "ERC20CustodyV2 contract address: %s, tx hash: %s", + erc20CustodyNewAddr.Hex(), + txCustody.Hash().Hex(), + ) + + ensureTxReceipt(txCustody, "ERC20CustodyV2 deployment failed") + + // set custody contract in gateway + txSetCustody, err := r.GatewayEVM.SetCustody(r.EVMAuth, erc20CustodyNewAddr) + require.NoError(r, err) + + // deploy test dapp v2 + testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.EVMAuth, r.EVMClient) + require.NoError(r, err) + + r.TestDAppV2EVMAddr = testDAppV2Addr + r.TestDAppV2EVM, err = testdappv2.NewTestDAppV2(testDAppV2Addr, r.EVMClient) + require.NoError(r, err) + + // check contract deployment receipt + ensureTxReceipt(txDonation, "EVM donation tx failed") + ensureTxReceipt(txProxy, "Gateway proxy deployment failed") + ensureTxReceipt(txSetCustody, "Set custody in Gateway failed") + ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") + + // whitelist the ERC20 + txWhitelist, err := r.ERC20CustodyV2.Whitelist(r.EVMAuth, r.ERC20Addr) + require.NoError(r, err) + + ensureTxReceipt(txWhitelist, "ERC20 whitelist failed") +} diff --git a/e2e/runner/v2_setup_zeta.go b/e2e/runner/v2_setup_zeta.go new file mode 100644 index 0000000000..c82e8fa741 --- /dev/null +++ b/e2e/runner/v2_setup_zeta.go @@ -0,0 +1,110 @@ +package runner + +import ( + "time" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/pkg/contracts/erc1967proxy" + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// SetZEVMContractsV2 set contracts for the ZEVM +func (r *E2ERunner) SetZEVMContractsV2() { + ensureTxReceipt := func(tx *ethtypes.Transaction, failMessage string) { + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) + r.requireTxSuccessful(receipt, failMessage+" tx hash: "+tx.Hash().Hex()) + } + + r.Logger.Print("⚙️ setting up ZEVM v2 network") + startTime := time.Now() + defer func() { + r.Logger.Info("ZEVM v2 network took %s\n", time.Since(startTime)) + }() + + r.Logger.Info("Deploying Gateway ZEVM") + gatewayZEVMAddr, txGateway, _, err := gatewayzevm.DeployGatewayZEVM(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + + ensureTxReceipt(txGateway, "Gateway deployment failed") + + gatewayZEVMABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + require.NoError(r, err) + + // Encode the initializer data + initializerData, err := gatewayZEVMABI.Pack("initialize", r.WZetaAddr, r.Account.EVMAddress()) + require.NoError(r, err) + + // Deploy the proxy contract + r.Logger.Info( + "Deploying proxy with %s and %s, address: %s", + r.WZetaAddr.Hex(), + r.Account.EVMAddress().Hex(), + gatewayZEVMAddr.Hex(), + ) + proxyAddress, txProxy, _, err := erc1967proxy.DeployERC1967Proxy( + r.ZEVMAuth, + r.ZEVMClient, + gatewayZEVMAddr, + initializerData, + ) + require.NoError(r, err) + + r.GatewayZEVMAddr = proxyAddress + r.GatewayZEVM, err = gatewayzevm.NewGatewayZEVM(proxyAddress, r.ZEVMClient) + require.NoError(r, err) + r.Logger.Info("Gateway ZEVM contract address: %s, tx hash: %s", gatewayZEVMAddr.Hex(), txGateway.Hash().Hex()) + + // Set the gateway address in the protocol + err = r.ZetaTxServer.UpdateGatewayAddress(utils.AdminPolicyName, r.GatewayZEVMAddr.Hex()) + require.NoError(r, err) + + // deploy test dapp v2 + testDAppV2Addr, txTestDAppV2, _, err := testdappv2.DeployTestDAppV2(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + + r.TestDAppV2ZEVMAddr = testDAppV2Addr + r.TestDAppV2ZEVM, err = testdappv2.NewTestDAppV2(testDAppV2Addr, r.ZEVMClient) + require.NoError(r, err) + + ensureTxReceipt(txProxy, "Gateway proxy deployment failed") + ensureTxReceipt(txTestDAppV2, "TestDAppV2 deployment failed") +} + +// UpdateChainParamsERC20CustodyContract update the erc20 custody contract in the chain params +// this operation is used when transitioning to new smart contract architecture where a new ERC20 custody contract is deployed +func (r *E2ERunner) UpdateChainParamsERC20CustodyContract() { + res, err := r.ObserverClient.GetChainParams(r.Ctx, &observertypes.QueryGetChainParamsRequest{}) + require.NoError(r, err) + + evmChainID, err := r.EVMClient.ChainID(r.Ctx) + require.NoError(r, err) + + // find old chain params + var ( + chainParams *observertypes.ChainParams + found bool + ) + for _, cp := range res.ChainParams.ChainParams { + if cp.ChainId == evmChainID.Int64() { + chainParams = cp + found = true + break + } + } + require.True(r, found, "Chain params not found for chain id %d", evmChainID) + + // update with the new ERC20 custody contract address + chainParams.Erc20CustodyContractAddress = r.ERC20CustodyV2Addr.Hex() + + // update the chain params + _, err = r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, observertypes.NewMsgUpdateChainParams( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName), + chainParams, + )) + require.NoError(r, err) +} diff --git a/e2e/runner/v2_zevm.go b/e2e/runner/v2_zevm.go new file mode 100644 index 0000000000..681696b195 --- /dev/null +++ b/e2e/runner/v2_zevm.go @@ -0,0 +1,117 @@ +package runner + +import ( + "math/big" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" +) + +var gasLimit = big.NewInt(1000000) + +// V2ETHWithdraw calls Withdraw of Gateway with gas token on ZEVM +func (r *E2ERunner) V2ETHWithdraw( + receiver ethcommon.Address, + amount *big.Int, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayZEVM.Withdraw( + r.ZEVMAuth, + receiver.Bytes(), + amount, + r.ETHZRC20Addr, + revertOptions, + ) + require.NoError(r, err) + + return tx +} + +// V2ETHWithdrawAndCall calls WithdrawAndCall of Gateway with gas token on ZEVM +func (r *E2ERunner) V2ETHWithdrawAndCall( + receiver ethcommon.Address, + amount *big.Int, + payload []byte, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayZEVM.WithdrawAndCall( + r.ZEVMAuth, + receiver.Bytes(), + amount, + r.ETHZRC20Addr, + payload, + gasLimit, + revertOptions, + ) + require.NoError(r, err) + + return tx +} + +// V2ERC20Withdraw calls Withdraw of Gateway with erc20 token on ZEVM +func (r *E2ERunner) V2ERC20Withdraw( + receiver ethcommon.Address, + amount *big.Int, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayZEVM.Withdraw( + r.ZEVMAuth, + receiver.Bytes(), + amount, + r.ERC20ZRC20Addr, + revertOptions, + ) + require.NoError(r, err) + + return tx +} + +// V2ERC20WithdrawAndCall calls WithdrawAndCall of Gateway with erc20 token on ZEVM +func (r *E2ERunner) V2ERC20WithdrawAndCall( + receiver ethcommon.Address, + amount *big.Int, + payload []byte, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + // this function take more gas than default 500k + // so we need to increase the gas limit + previousGasLimit := r.ZEVMAuth.GasLimit + r.ZEVMAuth.GasLimit = 10000000 + defer func() { + r.ZEVMAuth.GasLimit = previousGasLimit + }() + + tx, err := r.GatewayZEVM.WithdrawAndCall( + r.ZEVMAuth, + receiver.Bytes(), + amount, + r.ERC20ZRC20Addr, + payload, + gasLimit, + revertOptions, + ) + require.NoError(r, err) + + return tx +} + +// V2ZEVMToEMVCall calls Call of Gateway on ZEVM +func (r *E2ERunner) V2ZEVMToEMVCall( + receiver ethcommon.Address, + payload []byte, + revertOptions gatewayzevm.RevertOptions, +) *ethtypes.Transaction { + tx, err := r.GatewayZEVM.Call( + r.ZEVMAuth, + receiver.Bytes(), + r.ETHZRC20Addr, + payload, + gasLimit, + revertOptions, + ) + require.NoError(r, err) + + return tx +} diff --git a/e2e/runner/zeta.go b/e2e/runner/zeta.go index 95f160a801..fec82f459a 100644 --- a/e2e/runner/zeta.go +++ b/e2e/runner/zeta.go @@ -9,8 +9,8 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/pkg/retry" @@ -244,7 +244,7 @@ func (r *E2ERunner) WithdrawERC20(amount *big.Int) *ethtypes.Transaction { event.From.Hex(), event.To, event.Value, - event.Gasfee, + event.GasFee, ) } diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 8ec4496539..f6723f6575 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -298,10 +298,29 @@ func (zts ZetaTxServer) EnableHeaderVerification(account string, chainIDList []i return err } -// DeploySystemContractsAndZRC20 deploys the system contracts and ZRC20 contracts -// returns the addresses of uniswap factory, router and erc20 zrc20 -func (zts ZetaTxServer) DeploySystemContractsAndZRC20( - accountOperational, accountAdmin, erc20Addr string, +// UpdateGatewayAddress updates the gateway address +func (zts ZetaTxServer) UpdateGatewayAddress(account, gatewayAddr string) error { + // retrieve account + acc, err := zts.clientCtx.Keyring.Key(account) + if err != nil { + return err + } + addr, err := acc.GetAddress() + if err != nil { + return err + } + + _, err = zts.BroadcastTx(account, fungibletypes.NewMsgUpdateGatewayContract( + addr.String(), + gatewayAddr, + )) + return err +} + +// DeploySystemContracts deploys the system contracts +// returns the addresses of uniswap factory, router +func (zts ZetaTxServer) DeploySystemContracts( + accountOperational, accountAdmin string, ) (SystemContractAddresses, error) { // retrieve account accOperational, err := zts.clientCtx.Keyring.Key(accountOperational) @@ -375,6 +394,37 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( ) } + return SystemContractAddresses{ + UniswapV2FactoryAddr: uniswapV2FactoryAddr, + UniswapV2RouterAddr: uniswapV2RouterAddr, + ZEVMConnectorAddr: zevmConnectorAddr, + WZETAAddr: wzetaAddr, + }, nil +} + +// DeployZRC20s deploys the ZRC20 contracts +// returns the addresses of erc20 zrc20 +func (zts ZetaTxServer) DeployZRC20s( + accountOperational, accountAdmin, erc20Addr string, +) (string, error) { + // retrieve account + accOperational, err := zts.clientCtx.Keyring.Key(accountOperational) + if err != nil { + return "", err + } + addrOperational, err := accOperational.GetAddress() + if err != nil { + return "", err + } + accAdmin, err := zts.clientCtx.Keyring.Key(accountAdmin) + if err != nil { + return "", err + } + addrAdmin, err := accAdmin.GetAddress() + if err != nil { + return "", err + } + // authorization for deploying new ZRC20 has changed from accountOperational to accountAdmin in v19 // we use this query to check the current authorization for the message // if pre v19 the query is not implement and authorization is operational @@ -382,7 +432,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( deployerAddr := addrAdmin.String() authorization, preV19, err := zts.fetchMessagePermissions(&fungibletypes.MsgDeployFungibleCoinZRC20{}) if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to fetch message permissions: %s", err.Error()) + return "", fmt.Errorf("failed to fetch message permissions: %s", err.Error()) } if preV19 || authorization == authoritytypes.PolicyType_groupOperational { deployerAccount = accountOperational @@ -401,7 +451,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( 100000, )) if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) + return "", fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) } // deploy btc zrc20 @@ -416,7 +466,7 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( 100000, )) if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) + return "", fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) } // deploy sol zrc20 @@ -431,11 +481,11 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( 100000, )) if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to deploy sol zrc20: %s", err.Error()) + return "", fmt.Errorf("failed to deploy sol zrc20: %s", err.Error()) } // deploy erc20 zrc20 - res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + res, err := zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, erc20Addr, chains.GoerliLocalnet.ChainId, @@ -446,25 +496,19 @@ func (zts ZetaTxServer) DeploySystemContractsAndZRC20( 100000, )) if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to deploy erc20 zrc20: %s", err.Error()) + return "", fmt.Errorf("failed to deploy erc20 zrc20: %s", err.Error()) } // fetch the erc20 zrc20 contract address and remove the quotes erc20zrc20Addr, err := FetchAttributeFromTxResponse(res, "Contract") if err != nil { - return SystemContractAddresses{}, fmt.Errorf("failed to fetch erc20 zrc20 contract address: %s", err.Error()) + return "", fmt.Errorf("failed to fetch erc20 zrc20 contract address: %s, %s", err.Error(), res.String()) } if !ethcommon.IsHexAddress(erc20zrc20Addr) { - return SystemContractAddresses{}, fmt.Errorf("invalid address in event: %s", erc20zrc20Addr) + return "", fmt.Errorf("invalid address in event: %s", erc20zrc20Addr) } - return SystemContractAddresses{ - uniswapV2FactoryAddr, - uniswapV2RouterAddr, - zevmConnectorAddr, - wzetaAddr, - erc20zrc20Addr, - }, nil + return erc20zrc20Addr, nil } // FundEmissionsPool funds the emissions pool with the given amount diff --git a/go.mod b/go.mod index 7f952f66a6..97c95d67c5 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 - github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240418181724-c222fd3ae1f5 + github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890 google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 gopkg.in/yaml.v2 v2.4.0 ) @@ -231,7 +231,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/holiman/uint256 v1.2.2 // indirect + github.com/holiman/uint256 v1.2.3 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -316,8 +316,8 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.6.0 // indirect - github.com/tklauser/go-sysconf v0.3.10 // indirect - github.com/tklauser/numcpus v0.4.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect diff --git a/go.sum b/go.sum index 36e3a288c5..bb816367cf 100644 --- a/go.sum +++ b/go.sum @@ -942,8 +942,8 @@ github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZ github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.2 h1:TXKcSGc2WaxPD2+bmzAsVthL4+pEN0YwXcL5qED83vk= -github.com/holiman/uint256 v1.2.2/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= +github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c= github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U= @@ -1600,11 +1600,11 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= -github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= -github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= -github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= -github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -1673,8 +1673,8 @@ github.com/zeta-chain/go-tss v0.0.0-20240729195411-9f5ae8189449 h1:4U+4g2QQjbrme github.com/zeta-chain/go-tss v0.0.0-20240729195411-9f5ae8189449/go.mod h1:LN1IBRN8xQkKgdgLhl5BDGZyPm70QOTbVLejdS2FVpo= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2 h1:gd2uE0X+ZbdFJ8DubxNqLbOVlCB12EgWdzSNRAR82tM= github.com/zeta-chain/keystone/keys v0.0.0-20231105174229-903bc9405da2/go.mod h1:x7Bkwbzt2W2lQfjOirnff0Dj+tykdbTG1FMJPVPZsvE= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240418181724-c222fd3ae1f5 h1:ljM7xka3WZvth9k1uYxrG3/FKQQTkR96FZlIjUKOoYw= -github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240418181724-c222fd3ae1f5/go.mod h1:v79f+eY6PMpmLv188FAubst4XV2Mm8mUmx1OgmdFG3c= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890 h1:y2TNtm9ZF/GjJIg40wiZ/IqoeDouFfqi27Uu3xSQaVE= +github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20240816144801-7eb673cf8890/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= @@ -2071,6 +2071,8 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/pkg/coin/coin.go b/pkg/coin/coin.go index 4c5c86889b..223c2cbed9 100644 --- a/pkg/coin/coin.go +++ b/pkg/coin/coin.go @@ -16,9 +16,12 @@ func GetCoinType(coin string) (CoinType, error) { if err != nil { return CoinType_Cmd, err } - if coinInt < 0 || coinInt > 3 { + + // check boundaries of the enum + if coinInt < 0 || coinInt > int64(len(CoinType_name)) { return CoinType_Cmd, fmt.Errorf("invalid coin type %d", coinInt) } + // #nosec G115 always in range return CoinType(coinInt), nil } diff --git a/pkg/coin/coin.pb.go b/pkg/coin/coin.pb.go index b3c3999173..a974356d66 100644 --- a/pkg/coin/coin.pb.go +++ b/pkg/coin/coin.pb.go @@ -24,10 +24,11 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type CoinType int32 const ( - CoinType_Zeta CoinType = 0 - CoinType_Gas CoinType = 1 - CoinType_ERC20 CoinType = 2 - CoinType_Cmd CoinType = 3 + CoinType_Zeta CoinType = 0 + CoinType_Gas CoinType = 1 + CoinType_ERC20 CoinType = 2 + CoinType_Cmd CoinType = 3 + CoinType_NoAssetCall CoinType = 4 ) var CoinType_name = map[int32]string{ @@ -35,13 +36,15 @@ var CoinType_name = map[int32]string{ 1: "Gas", 2: "ERC20", 3: "Cmd", + 4: "NoAssetCall", } var CoinType_value = map[string]int32{ - "Zeta": 0, - "Gas": 1, - "ERC20": 2, - "Cmd": 3, + "Zeta": 0, + "Gas": 1, + "ERC20": 2, + "Cmd": 3, + "NoAssetCall": 4, } func (x CoinType) String() string { @@ -61,17 +64,18 @@ func init() { } var fileDescriptor_924f8c5071ab3892 = []byte{ - // 190 bytes of a gzipped FileDescriptorProto + // 207 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0xab, 0x4a, 0x2d, 0x49, 0x4c, 0xce, 0x48, 0xcc, 0xcc, 0xd3, 0x07, 0xb3, 0xf2, 0x8b, 0x52, 0xf5, 0x0b, 0xb2, 0xd3, 0xf5, 0x93, 0xf3, 0x33, 0xf3, 0xc0, 0x84, 0x5e, 0x41, 0x51, 0x7e, 0x49, 0xbe, 0x90, 0x34, 0x5c, 0x9d, 0x1e, 0x4c, 0x9d, 0x5e, 0x41, 0x76, 0xba, 0x1e, 0x48, 0x89, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, - 0x58, 0x9d, 0x3e, 0x88, 0x05, 0xd1, 0xa2, 0x65, 0xce, 0xc5, 0xe1, 0x9c, 0x9f, 0x99, 0x17, 0x52, + 0x58, 0x9d, 0x3e, 0x88, 0x05, 0xd1, 0xa2, 0xe5, 0xc1, 0xc5, 0xe1, 0x9c, 0x9f, 0x99, 0x17, 0x52, 0x59, 0x90, 0x2a, 0xc4, 0xc1, 0xc5, 0x12, 0x95, 0x5a, 0x92, 0x28, 0xc0, 0x20, 0xc4, 0xce, 0xc5, 0xec, 0x9e, 0x58, 0x2c, 0xc0, 0x28, 0xc4, 0xc9, 0xc5, 0xea, 0x1a, 0xe4, 0x6c, 0x64, 0x20, 0xc0, - 0x04, 0x12, 0x73, 0xce, 0x4d, 0x11, 0x60, 0x96, 0x62, 0x59, 0xb1, 0x44, 0x8e, 0xd1, 0xc9, 0xf1, - 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, - 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xd4, 0xd3, 0x33, 0x4b, 0x32, 0x4a, - 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xc1, 0xce, 0xd5, 0xc5, 0xe1, 0xf2, 0x24, 0x36, 0xb0, 0x13, 0x8c, - 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x52, 0x5c, 0x7d, 0x09, 0xdf, 0x00, 0x00, 0x00, + 0x04, 0x12, 0x73, 0xce, 0x4d, 0x11, 0x60, 0x16, 0xe2, 0xe7, 0xe2, 0xf6, 0xcb, 0x77, 0x2c, 0x2e, + 0x4e, 0x2d, 0x71, 0x4e, 0xcc, 0xc9, 0x11, 0x60, 0x91, 0x62, 0x59, 0xb1, 0x44, 0x8e, 0xd1, 0xc9, + 0xf1, 0xc4, 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, + 0x8e, 0xe1, 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0xd4, 0xd3, 0x33, 0x4b, 0x32, + 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xc1, 0xee, 0xd7, 0xc5, 0xe1, 0x95, 0x24, 0x36, 0xb0, 0x9b, + 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x45, 0xfd, 0x2c, 0xf4, 0xf0, 0x00, 0x00, 0x00, } diff --git a/pkg/coin/coin_test.go b/pkg/coin/coin_test.go index be7808d7a2..9f930039f7 100644 --- a/pkg/coin/coin_test.go +++ b/pkg/coin/coin_test.go @@ -101,19 +101,16 @@ func TestGetCoinType(t *testing.T) { { name: "invalid coin type negative", coin: "-1", - want: CoinType_Cmd, wantErr: true, }, { - name: "invalid coin type large number", - coin: "4", - want: CoinType_Cmd, - wantErr: true, + name: "invalid coin type large number", + coin: "4", + want: CoinType(4), }, { name: "invalid coin type non-integer", coin: "abc", - want: CoinType_Cmd, wantErr: true, }, } diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go index 9d524622ad..e0d2e16a5d 100644 --- a/pkg/constant/constant.go +++ b/pkg/constant/constant.go @@ -32,6 +32,9 @@ const ( // The number 890880 comes from CLI command `solana rent 0` and has been verified on devnet gateway program SolanaWalletRentExempt = 1_000_000 + // EVMZeroAddress is the zero address for EVM address format + EVMZeroAddress = "0x0000000000000000000000000000000000000000" + // OptionPause is the argument used in CmdUpdateERC20CustodyPauseStatus to pause the ERC20 custody contract OptionPause = "pause" diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.abi b/pkg/contracts/erc1967proxy/ERC1967Proxy.abi new file mode 100644 index 0000000000..9dadb5f5fa --- /dev/null +++ b/pkg/contracts/erc1967proxy/ERC1967Proxy.abi @@ -0,0 +1,71 @@ +[ + { + "type": "constructor", + "inputs": [ + { + "name": "_logic", + "type": "address", + "internalType": "address" + }, + { + "name": "_data", + "type": "bytes", + "internalType": "bytes" + } + ], + "stateMutability": "payable" + }, + { + "type": "fallback", + "stateMutability": "payable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "event", + "name": "AdminChanged", + "inputs": [ + { + "name": "previousAdmin", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "newAdmin", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BeaconUpgraded", + "inputs": [ + { + "name": "beacon", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "Upgraded", + "inputs": [ + { + "name": "implementation", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + } +] diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.bin b/pkg/contracts/erc1967proxy/ERC1967Proxy.bin new file mode 100644 index 0000000000..d5f2d8838f --- /dev/null +++ b/pkg/contracts/erc1967proxy/ERC1967Proxy.bin @@ -0,0 +1 @@ +0x60806040526040516107bb3803806107bb833981016040819052610022916102dc565b61002e82826000610035565b505061043b565b61003e8361006b565b60008251118061004b5750805b156100665761006483836100ab60201b6100291760201c565b505b505050565b610074816100d7565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100d08383604051806060016040528060278152602001610794602791396101a9565b9392505050565b6100ea8161022260201b6100551760201c565b6101515760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806101887f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61023160201b6100711760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060600080856001600160a01b0316856040516101c691906103aa565b600060405180830381855af49150503d8060008114610201576040519150601f19603f3d011682016040523d82523d6000602084013e610206565b606091505b50909250905061021886838387610234565b9695505050505050565b6001600160a01b03163b151590565b90565b606083156102a0578251610299576001600160a01b0385163b6102995760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610148565b50816102aa565b6102aa83836102b2565b949350505050565b8151156102c25781518083602001fd5b8060405162461bcd60e51b815260040161014891906103c6565b600080604083850312156102ef57600080fd5b82516001600160a01b038116811461030657600080fd5b60208401519092506001600160401b038082111561032357600080fd5b818501915085601f83011261033757600080fd5b81518181111561034957610349610425565b604051601f8201601f19908116603f0116810190838211818310171561037157610371610425565b8160405282815288602084870101111561038a57600080fd5b61039b8360208301602088016103f9565b80955050505050509250929050565b600082516103bc8184602087016103f9565b9190910192915050565b60208152600082518060208401526103e58160408501602087016103f9565b601f01601f19169190910160400192915050565b60005b838110156104145781810151838201526020016103fc565b838111156100645750506000910152565b634e487b7160e01b600052604160045260246000fd5b61034a8061044a6000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610074565b6100b9565b565b606061004e83836040518060600160405280602781526020016102ee602791396100dd565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b90565b60006100b47f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156100d8573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516101079190610250565b600060405180830381855af49150503d8060008114610142576040519150601f19603f3d011682016040523d82523d6000602084013e610147565b606091505b509150915061015886838387610162565b9695505050505050565b606083156101fa5782516101f35773ffffffffffffffffffffffffffffffffffffffff85163b6101f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610204565b610204838361020c565b949350505050565b81511561021c5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101ea919061026c565b600082516102628184602087016102bd565b9190910192915050565b602081526000825180602084015261028b8160408501602087016102bd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60005b838110156102d85781810151838201526020016102c0565b838111156102e7576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220721016d1ac0a7f9891c702fb3aea5ab01194272fdd3832cd3f3c7073e88919c564736f6c63430008070033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564 diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.go b/pkg/contracts/erc1967proxy/ERC1967Proxy.go new file mode 100644 index 0000000000..74e88b4fbf --- /dev/null +++ b/pkg/contracts/erc1967proxy/ERC1967Proxy.go @@ -0,0 +1,668 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package erc1967proxy + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// ERC1967ProxyMetaData contains all meta data concerning the ERC1967Proxy contract. +var ERC1967ProxyMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_logic\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"stateMutability\":\"payable\"},{\"type\":\"fallback\",\"stateMutability\":\"payable\"},{\"type\":\"receive\",\"stateMutability\":\"payable\"},{\"type\":\"event\",\"name\":\"AdminChanged\",\"inputs\":[{\"name\":\"previousAdmin\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"newAdmin\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BeaconUpgraded\",\"inputs\":[{\"name\":\"beacon\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Upgraded\",\"inputs\":[{\"name\":\"implementation\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", + Bin: "0x60806040526040516107bb3803806107bb833981016040819052610022916102dc565b61002e82826000610035565b505061043b565b61003e8361006b565b60008251118061004b5750805b156100665761006483836100ab60201b6100291760201c565b505b505050565b610074816100d7565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606100d08383604051806060016040528060278152602001610794602791396101a9565b9392505050565b6100ea8161022260201b6100551760201c565b6101515760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084015b60405180910390fd5b806101887f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b61023160201b6100711760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6060600080856001600160a01b0316856040516101c691906103aa565b600060405180830381855af49150503d8060008114610201576040519150601f19603f3d011682016040523d82523d6000602084013e610206565b606091505b50909250905061021886838387610234565b9695505050505050565b6001600160a01b03163b151590565b90565b606083156102a0578251610299576001600160a01b0385163b6102995760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610148565b50816102aa565b6102aa83836102b2565b949350505050565b8151156102c25781518083602001fd5b8060405162461bcd60e51b815260040161014891906103c6565b600080604083850312156102ef57600080fd5b82516001600160a01b038116811461030657600080fd5b60208401519092506001600160401b038082111561032357600080fd5b818501915085601f83011261033757600080fd5b81518181111561034957610349610425565b604051601f8201601f19908116603f0116810190838211818310171561037157610371610425565b8160405282815288602084870101111561038a57600080fd5b61039b8360208301602088016103f9565b80955050505050509250929050565b600082516103bc8184602087016103f9565b9190910192915050565b60208152600082518060208401526103e58160408501602087016103f9565b601f01601f19169190910160400192915050565b60005b838110156104145781810151838201526020016103fc565b838111156100645750506000910152565b634e487b7160e01b600052604160045260246000fd5b61034a8061044a6000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610074565b6100b9565b565b606061004e83836040518060600160405280602781526020016102ee602791396100dd565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b90565b60006100b47f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b3660008037600080366000845af43d6000803e8080156100d8573d6000f35b3d6000fd5b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516101079190610250565b600060405180830381855af49150503d8060008114610142576040519150601f19603f3d011682016040523d82523d6000602084013e610147565b606091505b509150915061015886838387610162565b9695505050505050565b606083156101fa5782516101f35773ffffffffffffffffffffffffffffffffffffffff85163b6101f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610204565b610204838361020c565b949350505050565b81511561021c5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101ea919061026c565b600082516102628184602087016102bd565b9190910192915050565b602081526000825180602084015261028b8160408501602087016102bd565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60005b838110156102d85781810151838201526020016102c0565b838111156102e7576000848401525b5050505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220721016d1ac0a7f9891c702fb3aea5ab01194272fdd3832cd3f3c7073e88919c564736f6c63430008070033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564", +} + +// ERC1967ProxyABI is the input ABI used to generate the binding from. +// Deprecated: Use ERC1967ProxyMetaData.ABI instead. +var ERC1967ProxyABI = ERC1967ProxyMetaData.ABI + +// ERC1967ProxyBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use ERC1967ProxyMetaData.Bin instead. +var ERC1967ProxyBin = ERC1967ProxyMetaData.Bin + +// DeployERC1967Proxy deploys a new Ethereum contract, binding an instance of ERC1967Proxy to it. +func DeployERC1967Proxy(auth *bind.TransactOpts, backend bind.ContractBackend, _logic common.Address, _data []byte) (common.Address, *types.Transaction, *ERC1967Proxy, error) { + parsed, err := ERC1967ProxyMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(ERC1967ProxyBin), backend, _logic, _data) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &ERC1967Proxy{ERC1967ProxyCaller: ERC1967ProxyCaller{contract: contract}, ERC1967ProxyTransactor: ERC1967ProxyTransactor{contract: contract}, ERC1967ProxyFilterer: ERC1967ProxyFilterer{contract: contract}}, nil +} + +// ERC1967Proxy is an auto generated Go binding around an Ethereum contract. +type ERC1967Proxy struct { + ERC1967ProxyCaller // Read-only binding to the contract + ERC1967ProxyTransactor // Write-only binding to the contract + ERC1967ProxyFilterer // Log filterer for contract events +} + +// ERC1967ProxyCaller is an auto generated read-only Go binding around an Ethereum contract. +type ERC1967ProxyCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1967ProxyTransactor is an auto generated write-only Go binding around an Ethereum contract. +type ERC1967ProxyTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1967ProxyFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type ERC1967ProxyFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// ERC1967ProxySession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type ERC1967ProxySession struct { + Contract *ERC1967Proxy // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC1967ProxyCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type ERC1967ProxyCallerSession struct { + Contract *ERC1967ProxyCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// ERC1967ProxyTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type ERC1967ProxyTransactorSession struct { + Contract *ERC1967ProxyTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// ERC1967ProxyRaw is an auto generated low-level Go binding around an Ethereum contract. +type ERC1967ProxyRaw struct { + Contract *ERC1967Proxy // Generic contract binding to access the raw methods on +} + +// ERC1967ProxyCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type ERC1967ProxyCallerRaw struct { + Contract *ERC1967ProxyCaller // Generic read-only contract binding to access the raw methods on +} + +// ERC1967ProxyTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type ERC1967ProxyTransactorRaw struct { + Contract *ERC1967ProxyTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewERC1967Proxy creates a new instance of ERC1967Proxy, bound to a specific deployed contract. +func NewERC1967Proxy(address common.Address, backend bind.ContractBackend) (*ERC1967Proxy, error) { + contract, err := bindERC1967Proxy(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &ERC1967Proxy{ERC1967ProxyCaller: ERC1967ProxyCaller{contract: contract}, ERC1967ProxyTransactor: ERC1967ProxyTransactor{contract: contract}, ERC1967ProxyFilterer: ERC1967ProxyFilterer{contract: contract}}, nil +} + +// NewERC1967ProxyCaller creates a new read-only instance of ERC1967Proxy, bound to a specific deployed contract. +func NewERC1967ProxyCaller(address common.Address, caller bind.ContractCaller) (*ERC1967ProxyCaller, error) { + contract, err := bindERC1967Proxy(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &ERC1967ProxyCaller{contract: contract}, nil +} + +// NewERC1967ProxyTransactor creates a new write-only instance of ERC1967Proxy, bound to a specific deployed contract. +func NewERC1967ProxyTransactor(address common.Address, transactor bind.ContractTransactor) (*ERC1967ProxyTransactor, error) { + contract, err := bindERC1967Proxy(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &ERC1967ProxyTransactor{contract: contract}, nil +} + +// NewERC1967ProxyFilterer creates a new log filterer instance of ERC1967Proxy, bound to a specific deployed contract. +func NewERC1967ProxyFilterer(address common.Address, filterer bind.ContractFilterer) (*ERC1967ProxyFilterer, error) { + contract, err := bindERC1967Proxy(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &ERC1967ProxyFilterer{contract: contract}, nil +} + +// bindERC1967Proxy binds a generic wrapper to an already deployed contract. +func bindERC1967Proxy(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := ERC1967ProxyMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC1967Proxy *ERC1967ProxyRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC1967Proxy.Contract.ERC1967ProxyCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC1967Proxy *ERC1967ProxyRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.ERC1967ProxyTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC1967Proxy *ERC1967ProxyRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.ERC1967ProxyTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_ERC1967Proxy *ERC1967ProxyCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _ERC1967Proxy.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_ERC1967Proxy *ERC1967ProxyTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_ERC1967Proxy *ERC1967ProxyTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.contract.Transact(opts, method, params...) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_ERC1967Proxy *ERC1967ProxyTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _ERC1967Proxy.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_ERC1967Proxy *ERC1967ProxySession) Fallback(calldata []byte) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.Fallback(&_ERC1967Proxy.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_ERC1967Proxy *ERC1967ProxyTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _ERC1967Proxy.Contract.Fallback(&_ERC1967Proxy.TransactOpts, calldata) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_ERC1967Proxy *ERC1967ProxyTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _ERC1967Proxy.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_ERC1967Proxy *ERC1967ProxySession) Receive() (*types.Transaction, error) { + return _ERC1967Proxy.Contract.Receive(&_ERC1967Proxy.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_ERC1967Proxy *ERC1967ProxyTransactorSession) Receive() (*types.Transaction, error) { + return _ERC1967Proxy.Contract.Receive(&_ERC1967Proxy.TransactOpts) +} + +// ERC1967ProxyAdminChangedIterator is returned from FilterAdminChanged and is used to iterate over the raw logs and unpacked data for AdminChanged events raised by the ERC1967Proxy contract. +type ERC1967ProxyAdminChangedIterator struct { + Event *ERC1967ProxyAdminChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC1967ProxyAdminChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyAdminChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC1967ProxyAdminChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1967ProxyAdminChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1967ProxyAdminChanged represents a AdminChanged event raised by the ERC1967Proxy contract. +type ERC1967ProxyAdminChanged struct { + PreviousAdmin common.Address + NewAdmin common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterAdminChanged is a free log retrieval operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_ERC1967Proxy *ERC1967ProxyFilterer) FilterAdminChanged(opts *bind.FilterOpts) (*ERC1967ProxyAdminChangedIterator, error) { + + logs, sub, err := _ERC1967Proxy.contract.FilterLogs(opts, "AdminChanged") + if err != nil { + return nil, err + } + return &ERC1967ProxyAdminChangedIterator{contract: _ERC1967Proxy.contract, event: "AdminChanged", logs: logs, sub: sub}, nil +} + +// WatchAdminChanged is a free log subscription operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_ERC1967Proxy *ERC1967ProxyFilterer) WatchAdminChanged(opts *bind.WatchOpts, sink chan<- *ERC1967ProxyAdminChanged) (event.Subscription, error) { + + logs, sub, err := _ERC1967Proxy.contract.WatchLogs(opts, "AdminChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC1967ProxyAdminChanged) + if err := _ERC1967Proxy.contract.UnpackLog(event, "AdminChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseAdminChanged is a log parse operation binding the contract event 0x7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f. +// +// Solidity: event AdminChanged(address previousAdmin, address newAdmin) +func (_ERC1967Proxy *ERC1967ProxyFilterer) ParseAdminChanged(log types.Log) (*ERC1967ProxyAdminChanged, error) { + event := new(ERC1967ProxyAdminChanged) + if err := _ERC1967Proxy.contract.UnpackLog(event, "AdminChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC1967ProxyBeaconUpgradedIterator is returned from FilterBeaconUpgraded and is used to iterate over the raw logs and unpacked data for BeaconUpgraded events raised by the ERC1967Proxy contract. +type ERC1967ProxyBeaconUpgradedIterator struct { + Event *ERC1967ProxyBeaconUpgraded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC1967ProxyBeaconUpgradedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyBeaconUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyBeaconUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC1967ProxyBeaconUpgradedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1967ProxyBeaconUpgradedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1967ProxyBeaconUpgraded represents a BeaconUpgraded event raised by the ERC1967Proxy contract. +type ERC1967ProxyBeaconUpgraded struct { + Beacon common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterBeaconUpgraded is a free log retrieval operation binding the contract event 0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e. +// +// Solidity: event BeaconUpgraded(address indexed beacon) +func (_ERC1967Proxy *ERC1967ProxyFilterer) FilterBeaconUpgraded(opts *bind.FilterOpts, beacon []common.Address) (*ERC1967ProxyBeaconUpgradedIterator, error) { + + var beaconRule []interface{} + for _, beaconItem := range beacon { + beaconRule = append(beaconRule, beaconItem) + } + + logs, sub, err := _ERC1967Proxy.contract.FilterLogs(opts, "BeaconUpgraded", beaconRule) + if err != nil { + return nil, err + } + return &ERC1967ProxyBeaconUpgradedIterator{contract: _ERC1967Proxy.contract, event: "BeaconUpgraded", logs: logs, sub: sub}, nil +} + +// WatchBeaconUpgraded is a free log subscription operation binding the contract event 0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e. +// +// Solidity: event BeaconUpgraded(address indexed beacon) +func (_ERC1967Proxy *ERC1967ProxyFilterer) WatchBeaconUpgraded(opts *bind.WatchOpts, sink chan<- *ERC1967ProxyBeaconUpgraded, beacon []common.Address) (event.Subscription, error) { + + var beaconRule []interface{} + for _, beaconItem := range beacon { + beaconRule = append(beaconRule, beaconItem) + } + + logs, sub, err := _ERC1967Proxy.contract.WatchLogs(opts, "BeaconUpgraded", beaconRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC1967ProxyBeaconUpgraded) + if err := _ERC1967Proxy.contract.UnpackLog(event, "BeaconUpgraded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseBeaconUpgraded is a log parse operation binding the contract event 0x1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e. +// +// Solidity: event BeaconUpgraded(address indexed beacon) +func (_ERC1967Proxy *ERC1967ProxyFilterer) ParseBeaconUpgraded(log types.Log) (*ERC1967ProxyBeaconUpgraded, error) { + event := new(ERC1967ProxyBeaconUpgraded) + if err := _ERC1967Proxy.contract.UnpackLog(event, "BeaconUpgraded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// ERC1967ProxyUpgradedIterator is returned from FilterUpgraded and is used to iterate over the raw logs and unpacked data for Upgraded events raised by the ERC1967Proxy contract. +type ERC1967ProxyUpgradedIterator struct { + Event *ERC1967ProxyUpgraded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ERC1967ProxyUpgradedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ERC1967ProxyUpgraded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ERC1967ProxyUpgradedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ERC1967ProxyUpgradedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ERC1967ProxyUpgraded represents a Upgraded event raised by the ERC1967Proxy contract. +type ERC1967ProxyUpgraded struct { + Implementation common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterUpgraded is a free log retrieval operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_ERC1967Proxy *ERC1967ProxyFilterer) FilterUpgraded(opts *bind.FilterOpts, implementation []common.Address) (*ERC1967ProxyUpgradedIterator, error) { + + var implementationRule []interface{} + for _, implementationItem := range implementation { + implementationRule = append(implementationRule, implementationItem) + } + + logs, sub, err := _ERC1967Proxy.contract.FilterLogs(opts, "Upgraded", implementationRule) + if err != nil { + return nil, err + } + return &ERC1967ProxyUpgradedIterator{contract: _ERC1967Proxy.contract, event: "Upgraded", logs: logs, sub: sub}, nil +} + +// WatchUpgraded is a free log subscription operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_ERC1967Proxy *ERC1967ProxyFilterer) WatchUpgraded(opts *bind.WatchOpts, sink chan<- *ERC1967ProxyUpgraded, implementation []common.Address) (event.Subscription, error) { + + var implementationRule []interface{} + for _, implementationItem := range implementation { + implementationRule = append(implementationRule, implementationItem) + } + + logs, sub, err := _ERC1967Proxy.contract.WatchLogs(opts, "Upgraded", implementationRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ERC1967ProxyUpgraded) + if err := _ERC1967Proxy.contract.UnpackLog(event, "Upgraded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseUpgraded is a log parse operation binding the contract event 0xbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b. +// +// Solidity: event Upgraded(address indexed implementation) +func (_ERC1967Proxy *ERC1967ProxyFilterer) ParseUpgraded(log types.Log) (*ERC1967ProxyUpgraded, error) { + event := new(ERC1967ProxyUpgraded) + if err := _ERC1967Proxy.contract.UnpackLog(event, "Upgraded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pkg/contracts/erc1967proxy/bindings.go b/pkg/contracts/erc1967proxy/bindings.go new file mode 100644 index 0000000000..ed88d93cc2 --- /dev/null +++ b/pkg/contracts/erc1967proxy/bindings.go @@ -0,0 +1,3 @@ +//go:generate sh -c "abigen --bin ERC1967Proxy.bin --abi ERC1967Proxy.abi --pkg erc1967proxy --type ERC1967Proxy --out ERC1967Proxy.go" + +package erc1967proxy diff --git a/pkg/contracts/testdappv2/TestDAppV2.abi b/pkg/contracts/testdappv2/TestDAppV2.abi new file mode 100644 index 0000000000..568085dcc8 --- /dev/null +++ b/pkg/contracts/testdappv2/TestDAppV2.abi @@ -0,0 +1,206 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "amountWithMessage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "calledWithMessage", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "erc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "erc20Call", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "gasCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "getAmountWithMessage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "getCalledWithMessage", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestDAppV2.zContext", + "name": "_context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCrossChainCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "revertMessage", + "type": "bytes" + } + ], + "internalType": "struct TestDAppV2.RevertContext", + "name": "revertContext", + "type": "tuple" + } + ], + "name": "onRevert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "simpleCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/pkg/contracts/testdappv2/TestDAppV2.bin b/pkg/contracts/testdappv2/TestDAppV2.bin new file mode 100644 index 0000000000..2c35399c09 --- /dev/null +++ b/pkg/contracts/testdappv2/TestDAppV2.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50610e3a806100206000396000f3fe60806040526004361061008a5760003560e01c8063a799911f11610059578063a799911f14610162578063c7a339a91461017e578063de43156e146101a7578063e2842ed7146101d0578063f592cbfb1461020d57610091565b806336e980a0146100965780634297a263146100bf578063660b9de0146100fc5780639291fe261461012557610091565b3661009157005b600080fd5b3480156100a257600080fd5b506100bd60048036038101906100b89190610900565b61024a565b005b3480156100cb57600080fd5b506100e660048036038101906100e19190610864565b610274565b6040516100f39190610b35565b60405180910390f35b34801561010857600080fd5b50610123600480360381019061011e9190610949565b61028c565b005b34801561013157600080fd5b5061014c60048036038101906101479190610900565b610347565b6040516101599190610b35565b60405180910390f35b61017c60048036038101906101779190610900565b61038a565b005b34801561018a57600080fd5b506101a560048036038101906101a09190610891565b6103b3565b005b3480156101b357600080fd5b506101ce60048036038101906101c99190610992565b610476565b005b3480156101dc57600080fd5b506101f760048036038101906101f29190610864565b61056f565b6040516102049190610b1a565b60405180910390f35b34801561021957600080fd5b50610234600480360381019061022f9190610900565b61058f565b6040516102419190610b1a565b60405180910390f35b610253816105de565b1561025d57600080fd5b61026681610634565b610271816000610688565b50565b60016020528060005260406000206000915090505481565b6102e781806040019061029f9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b6103448180604001906102fa9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610688565b50565b6000600160008360405160200161035e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610393816105de565b1561039d57600080fd5b6103a681610634565b6103b08134610688565b50565b6103bc816105de565b156103c657600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161040393929190610ae3565b602060405180830381600087803b15801561041d57600080fd5b505af1158015610431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104559190610837565b61045e57600080fd5b61046781610634565b6104718183610688565b505050565b6104c382828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506105de565b156104cd57600080fd5b61051a82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b61056882828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610688565b5050505050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016105a59190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b60006040516020016105ef90610ace565b60405160208183030381529060405280519060200120826040516020016106169190610ab7565b60405160208183030381529060405280519060200120149050919050565b60016000808360405160200161064a9190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b80600160008460405160200161069e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006106dd6106d884610bd8565b610bb3565b9050828152602081018484840111156106f9576106f8610d48565b5b610704848285610c83565b509392505050565b60008135905061071b81610d91565b92915050565b60008151905061073081610da8565b92915050565b60008135905061074581610dbf565b92915050565b60008083601f84011261076157610760610d2a565b5b8235905067ffffffffffffffff81111561077e5761077d610d25565b5b60208301915083600182028301111561079a57610799610d3e565b5b9250929050565b6000813590506107b081610dd6565b92915050565b600082601f8301126107cb576107ca610d2a565b5b81356107db8482602086016106ca565b91505092915050565b6000606082840312156107fa576107f9610d34565b5b81905092915050565b60006060828403121561081957610818610d34565b5b81905092915050565b60008135905061083181610ded565b92915050565b60006020828403121561084d5761084c610d52565b5b600061085b84828501610721565b91505092915050565b60006020828403121561087a57610879610d52565b5b600061088884828501610736565b91505092915050565b6000806000606084860312156108aa576108a9610d52565b5b60006108b8868287016107a1565b93505060206108c986828701610822565b925050604084013567ffffffffffffffff8111156108ea576108e9610d4d565b5b6108f6868287016107b6565b9150509250925092565b60006020828403121561091657610915610d52565b5b600082013567ffffffffffffffff81111561093457610933610d4d565b5b610940848285016107b6565b91505092915050565b60006020828403121561095f5761095e610d52565b5b600082013567ffffffffffffffff81111561097d5761097c610d4d565b5b610989848285016107e4565b91505092915050565b6000806000806000608086880312156109ae576109ad610d52565b5b600086013567ffffffffffffffff8111156109cc576109cb610d4d565b5b6109d888828901610803565b95505060206109e98882890161070c565b94505060406109fa88828901610822565b935050606086013567ffffffffffffffff811115610a1b57610a1a610d4d565b5b610a278882890161074b565b92509250509295509295909350565b610a3f81610c1f565b82525050565b610a4e81610c31565b82525050565b6000610a5f82610c09565b610a698185610c14565b9350610a79818560208601610c92565b80840191505092915050565b6000610a92600683610c14565b9150610a9d82610d68565b600682019050919050565b610ab181610c79565b82525050565b6000610ac38284610a54565b915081905092915050565b6000610ad982610a85565b9150819050919050565b6000606082019050610af86000830186610a36565b610b056020830185610a36565b610b126040830184610aa8565b949350505050565b6000602082019050610b2f6000830184610a45565b92915050565b6000602082019050610b4a6000830184610aa8565b92915050565b60008083356001602003843603038112610b6d57610b6c610d39565b5b80840192508235915067ffffffffffffffff821115610b8f57610b8e610d2f565b5b602083019250600182023603831315610bab57610baa610d43565b5b509250929050565b6000610bbd610bce565b9050610bc98282610cc5565b919050565b6000604051905090565b600067ffffffffffffffff821115610bf357610bf2610cf6565b5b610bfc82610d57565b9050602081019050919050565b600081519050919050565b600081905092915050565b6000610c2a82610c59565b9050919050565b60008115159050919050565b6000819050919050565b6000610c5282610c1f565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610cb0578082015181840152602081019050610c95565b83811115610cbf576000848401525b50505050565b610cce82610d57565b810181811067ffffffffffffffff82111715610ced57610cec610cf6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b610d9a81610c1f565b8114610da557600080fd5b50565b610db181610c31565b8114610dbc57600080fd5b50565b610dc881610c3d565b8114610dd357600080fd5b50565b610ddf81610c47565b8114610dea57600080fd5b50565b610df681610c79565b8114610e0157600080fd5b5056fea2646970667358221220d6765b67214e8cadf15b569ed63c4c4628bd833acf6926d71b9b6a6011fbb78064736f6c63430008070033 diff --git a/pkg/contracts/testdappv2/TestDAppV2.go b/pkg/contracts/testdappv2/TestDAppV2.go new file mode 100644 index 0000000000..9afe3b5c9d --- /dev/null +++ b/pkg/contracts/testdappv2/TestDAppV2.go @@ -0,0 +1,467 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package testdappv2 + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// TestDAppV2RevertContext is an auto generated low-level Go binding around an user-defined struct. +type TestDAppV2RevertContext struct { + Asset common.Address + Amount uint64 + RevertMessage []byte +} + +// TestDAppV2zContext is an auto generated low-level Go binding around an user-defined struct. +type TestDAppV2zContext struct { + Origin []byte + Sender common.Address + ChainID *big.Int +} + +// TestDAppV2MetaData contains all meta data concerning the TestDAppV2 contract. +var TestDAppV2MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"amountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"calledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"contractIERC20\",\"name\":\"erc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"erc20Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"gasCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getAmountWithMessage\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"getCalledWithMessage\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestDAppV2.zContext\",\"name\":\"_context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCrossChainCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"asset\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"amount\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"revertMessage\",\"type\":\"bytes\"}],\"internalType\":\"structTestDAppV2.RevertContext\",\"name\":\"revertContext\",\"type\":\"tuple\"}],\"name\":\"onRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"simpleCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x608060405234801561001057600080fd5b50610e3a806100206000396000f3fe60806040526004361061008a5760003560e01c8063a799911f11610059578063a799911f14610162578063c7a339a91461017e578063de43156e146101a7578063e2842ed7146101d0578063f592cbfb1461020d57610091565b806336e980a0146100965780634297a263146100bf578063660b9de0146100fc5780639291fe261461012557610091565b3661009157005b600080fd5b3480156100a257600080fd5b506100bd60048036038101906100b89190610900565b61024a565b005b3480156100cb57600080fd5b506100e660048036038101906100e19190610864565b610274565b6040516100f39190610b35565b60405180910390f35b34801561010857600080fd5b50610123600480360381019061011e9190610949565b61028c565b005b34801561013157600080fd5b5061014c60048036038101906101479190610900565b610347565b6040516101599190610b35565b60405180910390f35b61017c60048036038101906101779190610900565b61038a565b005b34801561018a57600080fd5b506101a560048036038101906101a09190610891565b6103b3565b005b3480156101b357600080fd5b506101ce60048036038101906101c99190610992565b610476565b005b3480156101dc57600080fd5b506101f760048036038101906101f29190610864565b61056f565b6040516102049190610b1a565b60405180910390f35b34801561021957600080fd5b50610234600480360381019061022f9190610900565b61058f565b6040516102419190610b1a565b60405180910390f35b610253816105de565b1561025d57600080fd5b61026681610634565b610271816000610688565b50565b60016020528060005260406000206000915090505481565b6102e781806040019061029f9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b6103448180604001906102fa9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610688565b50565b6000600160008360405160200161035e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610393816105de565b1561039d57600080fd5b6103a681610634565b6103b08134610688565b50565b6103bc816105de565b156103c657600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161040393929190610ae3565b602060405180830381600087803b15801561041d57600080fd5b505af1158015610431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104559190610837565b61045e57600080fd5b61046781610634565b6104718183610688565b505050565b6104c382828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506105de565b156104cd57600080fd5b61051a82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b61056882828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610688565b5050505050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016105a59190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b60006040516020016105ef90610ace565b60405160208183030381529060405280519060200120826040516020016106169190610ab7565b60405160208183030381529060405280519060200120149050919050565b60016000808360405160200161064a9190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b80600160008460405160200161069e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006106dd6106d884610bd8565b610bb3565b9050828152602081018484840111156106f9576106f8610d48565b5b610704848285610c83565b509392505050565b60008135905061071b81610d91565b92915050565b60008151905061073081610da8565b92915050565b60008135905061074581610dbf565b92915050565b60008083601f84011261076157610760610d2a565b5b8235905067ffffffffffffffff81111561077e5761077d610d25565b5b60208301915083600182028301111561079a57610799610d3e565b5b9250929050565b6000813590506107b081610dd6565b92915050565b600082601f8301126107cb576107ca610d2a565b5b81356107db8482602086016106ca565b91505092915050565b6000606082840312156107fa576107f9610d34565b5b81905092915050565b60006060828403121561081957610818610d34565b5b81905092915050565b60008135905061083181610ded565b92915050565b60006020828403121561084d5761084c610d52565b5b600061085b84828501610721565b91505092915050565b60006020828403121561087a57610879610d52565b5b600061088884828501610736565b91505092915050565b6000806000606084860312156108aa576108a9610d52565b5b60006108b8868287016107a1565b93505060206108c986828701610822565b925050604084013567ffffffffffffffff8111156108ea576108e9610d4d565b5b6108f6868287016107b6565b9150509250925092565b60006020828403121561091657610915610d52565b5b600082013567ffffffffffffffff81111561093457610933610d4d565b5b610940848285016107b6565b91505092915050565b60006020828403121561095f5761095e610d52565b5b600082013567ffffffffffffffff81111561097d5761097c610d4d565b5b610989848285016107e4565b91505092915050565b6000806000806000608086880312156109ae576109ad610d52565b5b600086013567ffffffffffffffff8111156109cc576109cb610d4d565b5b6109d888828901610803565b95505060206109e98882890161070c565b94505060406109fa88828901610822565b935050606086013567ffffffffffffffff811115610a1b57610a1a610d4d565b5b610a278882890161074b565b92509250509295509295909350565b610a3f81610c1f565b82525050565b610a4e81610c31565b82525050565b6000610a5f82610c09565b610a698185610c14565b9350610a79818560208601610c92565b80840191505092915050565b6000610a92600683610c14565b9150610a9d82610d68565b600682019050919050565b610ab181610c79565b82525050565b6000610ac38284610a54565b915081905092915050565b6000610ad982610a85565b9150819050919050565b6000606082019050610af86000830186610a36565b610b056020830185610a36565b610b126040830184610aa8565b949350505050565b6000602082019050610b2f6000830184610a45565b92915050565b6000602082019050610b4a6000830184610aa8565b92915050565b60008083356001602003843603038112610b6d57610b6c610d39565b5b80840192508235915067ffffffffffffffff821115610b8f57610b8e610d2f565b5b602083019250600182023603831315610bab57610baa610d43565b5b509250929050565b6000610bbd610bce565b9050610bc98282610cc5565b919050565b6000604051905090565b600067ffffffffffffffff821115610bf357610bf2610cf6565b5b610bfc82610d57565b9050602081019050919050565b600081519050919050565b600081905092915050565b6000610c2a82610c59565b9050919050565b60008115159050919050565b6000819050919050565b6000610c5282610c1f565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610cb0578082015181840152602081019050610c95565b83811115610cbf576000848401525b50505050565b610cce82610d57565b810181811067ffffffffffffffff82111715610ced57610cec610cf6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b610d9a81610c1f565b8114610da557600080fd5b50565b610db181610c31565b8114610dbc57600080fd5b50565b610dc881610c3d565b8114610dd357600080fd5b50565b610ddf81610c47565b8114610dea57600080fd5b50565b610df681610c79565b8114610e0157600080fd5b5056fea2646970667358221220d6765b67214e8cadf15b569ed63c4c4628bd833acf6926d71b9b6a6011fbb78064736f6c63430008070033", +} + +// TestDAppV2ABI is the input ABI used to generate the binding from. +// Deprecated: Use TestDAppV2MetaData.ABI instead. +var TestDAppV2ABI = TestDAppV2MetaData.ABI + +// TestDAppV2Bin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use TestDAppV2MetaData.Bin instead. +var TestDAppV2Bin = TestDAppV2MetaData.Bin + +// DeployTestDAppV2 deploys a new Ethereum contract, binding an instance of TestDAppV2 to it. +func DeployTestDAppV2(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *TestDAppV2, error) { + parsed, err := TestDAppV2MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestDAppV2Bin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &TestDAppV2{TestDAppV2Caller: TestDAppV2Caller{contract: contract}, TestDAppV2Transactor: TestDAppV2Transactor{contract: contract}, TestDAppV2Filterer: TestDAppV2Filterer{contract: contract}}, nil +} + +// TestDAppV2 is an auto generated Go binding around an Ethereum contract. +type TestDAppV2 struct { + TestDAppV2Caller // Read-only binding to the contract + TestDAppV2Transactor // Write-only binding to the contract + TestDAppV2Filterer // Log filterer for contract events +} + +// TestDAppV2Caller is an auto generated read-only Go binding around an Ethereum contract. +type TestDAppV2Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestDAppV2Transactor is an auto generated write-only Go binding around an Ethereum contract. +type TestDAppV2Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestDAppV2Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TestDAppV2Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestDAppV2Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TestDAppV2Session struct { + Contract *TestDAppV2 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestDAppV2CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TestDAppV2CallerSession struct { + Contract *TestDAppV2Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TestDAppV2TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TestDAppV2TransactorSession struct { + Contract *TestDAppV2Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestDAppV2Raw is an auto generated low-level Go binding around an Ethereum contract. +type TestDAppV2Raw struct { + Contract *TestDAppV2 // Generic contract binding to access the raw methods on +} + +// TestDAppV2CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TestDAppV2CallerRaw struct { + Contract *TestDAppV2Caller // Generic read-only contract binding to access the raw methods on +} + +// TestDAppV2TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TestDAppV2TransactorRaw struct { + Contract *TestDAppV2Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewTestDAppV2 creates a new instance of TestDAppV2, bound to a specific deployed contract. +func NewTestDAppV2(address common.Address, backend bind.ContractBackend) (*TestDAppV2, error) { + contract, err := bindTestDAppV2(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &TestDAppV2{TestDAppV2Caller: TestDAppV2Caller{contract: contract}, TestDAppV2Transactor: TestDAppV2Transactor{contract: contract}, TestDAppV2Filterer: TestDAppV2Filterer{contract: contract}}, nil +} + +// NewTestDAppV2Caller creates a new read-only instance of TestDAppV2, bound to a specific deployed contract. +func NewTestDAppV2Caller(address common.Address, caller bind.ContractCaller) (*TestDAppV2Caller, error) { + contract, err := bindTestDAppV2(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TestDAppV2Caller{contract: contract}, nil +} + +// NewTestDAppV2Transactor creates a new write-only instance of TestDAppV2, bound to a specific deployed contract. +func NewTestDAppV2Transactor(address common.Address, transactor bind.ContractTransactor) (*TestDAppV2Transactor, error) { + contract, err := bindTestDAppV2(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TestDAppV2Transactor{contract: contract}, nil +} + +// NewTestDAppV2Filterer creates a new log filterer instance of TestDAppV2, bound to a specific deployed contract. +func NewTestDAppV2Filterer(address common.Address, filterer bind.ContractFilterer) (*TestDAppV2Filterer, error) { + contract, err := bindTestDAppV2(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TestDAppV2Filterer{contract: contract}, nil +} + +// bindTestDAppV2 binds a generic wrapper to an already deployed contract. +func bindTestDAppV2(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TestDAppV2MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestDAppV2 *TestDAppV2Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestDAppV2.Contract.TestDAppV2Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestDAppV2 *TestDAppV2Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestDAppV2.Contract.TestDAppV2Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestDAppV2 *TestDAppV2Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestDAppV2.Contract.TestDAppV2Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_TestDAppV2 *TestDAppV2CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _TestDAppV2.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_TestDAppV2 *TestDAppV2TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestDAppV2.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_TestDAppV2 *TestDAppV2TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _TestDAppV2.Contract.contract.Transact(opts, method, params...) +} + +// AmountWithMessage is a free data retrieval call binding the contract method 0x4297a263. +// +// Solidity: function amountWithMessage(bytes32 ) view returns(uint256) +func (_TestDAppV2 *TestDAppV2Caller) AmountWithMessage(opts *bind.CallOpts, arg0 [32]byte) (*big.Int, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "amountWithMessage", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// AmountWithMessage is a free data retrieval call binding the contract method 0x4297a263. +// +// Solidity: function amountWithMessage(bytes32 ) view returns(uint256) +func (_TestDAppV2 *TestDAppV2Session) AmountWithMessage(arg0 [32]byte) (*big.Int, error) { + return _TestDAppV2.Contract.AmountWithMessage(&_TestDAppV2.CallOpts, arg0) +} + +// AmountWithMessage is a free data retrieval call binding the contract method 0x4297a263. +// +// Solidity: function amountWithMessage(bytes32 ) view returns(uint256) +func (_TestDAppV2 *TestDAppV2CallerSession) AmountWithMessage(arg0 [32]byte) (*big.Int, error) { + return _TestDAppV2.Contract.AmountWithMessage(&_TestDAppV2.CallOpts, arg0) +} + +// CalledWithMessage is a free data retrieval call binding the contract method 0xe2842ed7. +// +// Solidity: function calledWithMessage(bytes32 ) view returns(bool) +func (_TestDAppV2 *TestDAppV2Caller) CalledWithMessage(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "calledWithMessage", arg0) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// CalledWithMessage is a free data retrieval call binding the contract method 0xe2842ed7. +// +// Solidity: function calledWithMessage(bytes32 ) view returns(bool) +func (_TestDAppV2 *TestDAppV2Session) CalledWithMessage(arg0 [32]byte) (bool, error) { + return _TestDAppV2.Contract.CalledWithMessage(&_TestDAppV2.CallOpts, arg0) +} + +// CalledWithMessage is a free data retrieval call binding the contract method 0xe2842ed7. +// +// Solidity: function calledWithMessage(bytes32 ) view returns(bool) +func (_TestDAppV2 *TestDAppV2CallerSession) CalledWithMessage(arg0 [32]byte) (bool, error) { + return _TestDAppV2.Contract.CalledWithMessage(&_TestDAppV2.CallOpts, arg0) +} + +// GetAmountWithMessage is a free data retrieval call binding the contract method 0x9291fe26. +// +// Solidity: function getAmountWithMessage(string message) view returns(uint256) +func (_TestDAppV2 *TestDAppV2Caller) GetAmountWithMessage(opts *bind.CallOpts, message string) (*big.Int, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "getAmountWithMessage", message) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetAmountWithMessage is a free data retrieval call binding the contract method 0x9291fe26. +// +// Solidity: function getAmountWithMessage(string message) view returns(uint256) +func (_TestDAppV2 *TestDAppV2Session) GetAmountWithMessage(message string) (*big.Int, error) { + return _TestDAppV2.Contract.GetAmountWithMessage(&_TestDAppV2.CallOpts, message) +} + +// GetAmountWithMessage is a free data retrieval call binding the contract method 0x9291fe26. +// +// Solidity: function getAmountWithMessage(string message) view returns(uint256) +func (_TestDAppV2 *TestDAppV2CallerSession) GetAmountWithMessage(message string) (*big.Int, error) { + return _TestDAppV2.Contract.GetAmountWithMessage(&_TestDAppV2.CallOpts, message) +} + +// GetCalledWithMessage is a free data retrieval call binding the contract method 0xf592cbfb. +// +// Solidity: function getCalledWithMessage(string message) view returns(bool) +func (_TestDAppV2 *TestDAppV2Caller) GetCalledWithMessage(opts *bind.CallOpts, message string) (bool, error) { + var out []interface{} + err := _TestDAppV2.contract.Call(opts, &out, "getCalledWithMessage", message) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// GetCalledWithMessage is a free data retrieval call binding the contract method 0xf592cbfb. +// +// Solidity: function getCalledWithMessage(string message) view returns(bool) +func (_TestDAppV2 *TestDAppV2Session) GetCalledWithMessage(message string) (bool, error) { + return _TestDAppV2.Contract.GetCalledWithMessage(&_TestDAppV2.CallOpts, message) +} + +// GetCalledWithMessage is a free data retrieval call binding the contract method 0xf592cbfb. +// +// Solidity: function getCalledWithMessage(string message) view returns(bool) +func (_TestDAppV2 *TestDAppV2CallerSession) GetCalledWithMessage(message string) (bool, error) { + return _TestDAppV2.Contract.GetCalledWithMessage(&_TestDAppV2.CallOpts, message) +} + +// Erc20Call is a paid mutator transaction binding the contract method 0xc7a339a9. +// +// Solidity: function erc20Call(address erc20, uint256 amount, string message) returns() +func (_TestDAppV2 *TestDAppV2Transactor) Erc20Call(opts *bind.TransactOpts, erc20 common.Address, amount *big.Int, message string) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "erc20Call", erc20, amount, message) +} + +// Erc20Call is a paid mutator transaction binding the contract method 0xc7a339a9. +// +// Solidity: function erc20Call(address erc20, uint256 amount, string message) returns() +func (_TestDAppV2 *TestDAppV2Session) Erc20Call(erc20 common.Address, amount *big.Int, message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.Erc20Call(&_TestDAppV2.TransactOpts, erc20, amount, message) +} + +// Erc20Call is a paid mutator transaction binding the contract method 0xc7a339a9. +// +// Solidity: function erc20Call(address erc20, uint256 amount, string message) returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) Erc20Call(erc20 common.Address, amount *big.Int, message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.Erc20Call(&_TestDAppV2.TransactOpts, erc20, amount, message) +} + +// GasCall is a paid mutator transaction binding the contract method 0xa799911f. +// +// Solidity: function gasCall(string message) payable returns() +func (_TestDAppV2 *TestDAppV2Transactor) GasCall(opts *bind.TransactOpts, message string) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "gasCall", message) +} + +// GasCall is a paid mutator transaction binding the contract method 0xa799911f. +// +// Solidity: function gasCall(string message) payable returns() +func (_TestDAppV2 *TestDAppV2Session) GasCall(message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.GasCall(&_TestDAppV2.TransactOpts, message) +} + +// GasCall is a paid mutator transaction binding the contract method 0xa799911f. +// +// Solidity: function gasCall(string message) payable returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) GasCall(message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.GasCall(&_TestDAppV2.TransactOpts, message) +} + +// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// +// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2Transactor) OnCrossChainCall(opts *bind.TransactOpts, _context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "onCrossChainCall", _context, _zrc20, amount, message) +} + +// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// +// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2Session) OnCrossChainCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCrossChainCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) +} + +// OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. +// +// Solidity: function onCrossChainCall((bytes,address,uint256) _context, address _zrc20, uint256 amount, bytes message) returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) OnCrossChainCall(_context TestDAppV2zContext, _zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnCrossChainCall(&_TestDAppV2.TransactOpts, _context, _zrc20, amount, message) +} + +// OnRevert is a paid mutator transaction binding the contract method 0x660b9de0. +// +// Solidity: function onRevert((address,uint64,bytes) revertContext) returns() +func (_TestDAppV2 *TestDAppV2Transactor) OnRevert(opts *bind.TransactOpts, revertContext TestDAppV2RevertContext) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "onRevert", revertContext) +} + +// OnRevert is a paid mutator transaction binding the contract method 0x660b9de0. +// +// Solidity: function onRevert((address,uint64,bytes) revertContext) returns() +func (_TestDAppV2 *TestDAppV2Session) OnRevert(revertContext TestDAppV2RevertContext) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnRevert(&_TestDAppV2.TransactOpts, revertContext) +} + +// OnRevert is a paid mutator transaction binding the contract method 0x660b9de0. +// +// Solidity: function onRevert((address,uint64,bytes) revertContext) returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) OnRevert(revertContext TestDAppV2RevertContext) (*types.Transaction, error) { + return _TestDAppV2.Contract.OnRevert(&_TestDAppV2.TransactOpts, revertContext) +} + +// SimpleCall is a paid mutator transaction binding the contract method 0x36e980a0. +// +// Solidity: function simpleCall(string message) returns() +func (_TestDAppV2 *TestDAppV2Transactor) SimpleCall(opts *bind.TransactOpts, message string) (*types.Transaction, error) { + return _TestDAppV2.contract.Transact(opts, "simpleCall", message) +} + +// SimpleCall is a paid mutator transaction binding the contract method 0x36e980a0. +// +// Solidity: function simpleCall(string message) returns() +func (_TestDAppV2 *TestDAppV2Session) SimpleCall(message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.SimpleCall(&_TestDAppV2.TransactOpts, message) +} + +// SimpleCall is a paid mutator transaction binding the contract method 0x36e980a0. +// +// Solidity: function simpleCall(string message) returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) SimpleCall(message string) (*types.Transaction, error) { + return _TestDAppV2.Contract.SimpleCall(&_TestDAppV2.TransactOpts, message) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_TestDAppV2 *TestDAppV2Transactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _TestDAppV2.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_TestDAppV2 *TestDAppV2Session) Receive() (*types.Transaction, error) { + return _TestDAppV2.Contract.Receive(&_TestDAppV2.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_TestDAppV2 *TestDAppV2TransactorSession) Receive() (*types.Transaction, error) { + return _TestDAppV2.Contract.Receive(&_TestDAppV2.TransactOpts) +} diff --git a/pkg/contracts/testdappv2/TestDAppV2.json b/pkg/contracts/testdappv2/TestDAppV2.json new file mode 100644 index 0000000000..bcac60c189 --- /dev/null +++ b/pkg/contracts/testdappv2/TestDAppV2.json @@ -0,0 +1,209 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "amountWithMessage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "calledWithMessage", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "erc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "erc20Call", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "gasCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "getAmountWithMessage", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "getCalledWithMessage", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct TestDAppV2.zContext", + "name": "_context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "_zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCrossChainCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint64", + "name": "amount", + "type": "uint64" + }, + { + "internalType": "bytes", + "name": "revertMessage", + "type": "bytes" + } + ], + "internalType": "struct TestDAppV2.RevertContext", + "name": "revertContext", + "type": "tuple" + } + ], + "name": "onRevert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + } + ], + "name": "simpleCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ], + "bin": "608060405234801561001057600080fd5b50610e3a806100206000396000f3fe60806040526004361061008a5760003560e01c8063a799911f11610059578063a799911f14610162578063c7a339a91461017e578063de43156e146101a7578063e2842ed7146101d0578063f592cbfb1461020d57610091565b806336e980a0146100965780634297a263146100bf578063660b9de0146100fc5780639291fe261461012557610091565b3661009157005b600080fd5b3480156100a257600080fd5b506100bd60048036038101906100b89190610900565b61024a565b005b3480156100cb57600080fd5b506100e660048036038101906100e19190610864565b610274565b6040516100f39190610b35565b60405180910390f35b34801561010857600080fd5b50610123600480360381019061011e9190610949565b61028c565b005b34801561013157600080fd5b5061014c60048036038101906101479190610900565b610347565b6040516101599190610b35565b60405180910390f35b61017c60048036038101906101779190610900565b61038a565b005b34801561018a57600080fd5b506101a560048036038101906101a09190610891565b6103b3565b005b3480156101b357600080fd5b506101ce60048036038101906101c99190610992565b610476565b005b3480156101dc57600080fd5b506101f760048036038101906101f29190610864565b61056f565b6040516102049190610b1a565b60405180910390f35b34801561021957600080fd5b50610234600480360381019061022f9190610900565b61058f565b6040516102419190610b1a565b60405180910390f35b610253816105de565b1561025d57600080fd5b61026681610634565b610271816000610688565b50565b60016020528060005260406000206000915090505481565b6102e781806040019061029f9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b6103448180604001906102fa9190610b50565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506000610688565b50565b6000600160008360405160200161035e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020549050919050565b610393816105de565b1561039d57600080fd5b6103a681610634565b6103b08134610688565b50565b6103bc816105de565b156103c657600080fd5b8273ffffffffffffffffffffffffffffffffffffffff166323b872dd3330856040518463ffffffff1660e01b815260040161040393929190610ae3565b602060405180830381600087803b15801561041d57600080fd5b505af1158015610431573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104559190610837565b61045e57600080fd5b61046781610634565b6104718183610688565b505050565b6104c382828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050506105de565b156104cd57600080fd5b61051a82828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050610634565b61056882828080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505084610688565b5050505050565b60006020528060005260406000206000915054906101000a900460ff1681565b6000806000836040516020016105a59190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900460ff169050919050565b60006040516020016105ef90610ace565b60405160208183030381529060405280519060200120826040516020016106169190610ab7565b60405160208183030381529060405280519060200120149050919050565b60016000808360405160200161064a9190610ab7565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548160ff02191690831515021790555050565b80600160008460405160200161069e9190610ab7565b604051602081830303815290604052805190602001208152602001908152602001600020819055505050565b60006106dd6106d884610bd8565b610bb3565b9050828152602081018484840111156106f9576106f8610d48565b5b610704848285610c83565b509392505050565b60008135905061071b81610d91565b92915050565b60008151905061073081610da8565b92915050565b60008135905061074581610dbf565b92915050565b60008083601f84011261076157610760610d2a565b5b8235905067ffffffffffffffff81111561077e5761077d610d25565b5b60208301915083600182028301111561079a57610799610d3e565b5b9250929050565b6000813590506107b081610dd6565b92915050565b600082601f8301126107cb576107ca610d2a565b5b81356107db8482602086016106ca565b91505092915050565b6000606082840312156107fa576107f9610d34565b5b81905092915050565b60006060828403121561081957610818610d34565b5b81905092915050565b60008135905061083181610ded565b92915050565b60006020828403121561084d5761084c610d52565b5b600061085b84828501610721565b91505092915050565b60006020828403121561087a57610879610d52565b5b600061088884828501610736565b91505092915050565b6000806000606084860312156108aa576108a9610d52565b5b60006108b8868287016107a1565b93505060206108c986828701610822565b925050604084013567ffffffffffffffff8111156108ea576108e9610d4d565b5b6108f6868287016107b6565b9150509250925092565b60006020828403121561091657610915610d52565b5b600082013567ffffffffffffffff81111561093457610933610d4d565b5b610940848285016107b6565b91505092915050565b60006020828403121561095f5761095e610d52565b5b600082013567ffffffffffffffff81111561097d5761097c610d4d565b5b610989848285016107e4565b91505092915050565b6000806000806000608086880312156109ae576109ad610d52565b5b600086013567ffffffffffffffff8111156109cc576109cb610d4d565b5b6109d888828901610803565b95505060206109e98882890161070c565b94505060406109fa88828901610822565b935050606086013567ffffffffffffffff811115610a1b57610a1a610d4d565b5b610a278882890161074b565b92509250509295509295909350565b610a3f81610c1f565b82525050565b610a4e81610c31565b82525050565b6000610a5f82610c09565b610a698185610c14565b9350610a79818560208601610c92565b80840191505092915050565b6000610a92600683610c14565b9150610a9d82610d68565b600682019050919050565b610ab181610c79565b82525050565b6000610ac38284610a54565b915081905092915050565b6000610ad982610a85565b9150819050919050565b6000606082019050610af86000830186610a36565b610b056020830185610a36565b610b126040830184610aa8565b949350505050565b6000602082019050610b2f6000830184610a45565b92915050565b6000602082019050610b4a6000830184610aa8565b92915050565b60008083356001602003843603038112610b6d57610b6c610d39565b5b80840192508235915067ffffffffffffffff821115610b8f57610b8e610d2f565b5b602083019250600182023603831315610bab57610baa610d43565b5b509250929050565b6000610bbd610bce565b9050610bc98282610cc5565b919050565b6000604051905090565b600067ffffffffffffffff821115610bf357610bf2610cf6565b5b610bfc82610d57565b9050602081019050919050565b600081519050919050565b600081905092915050565b6000610c2a82610c59565b9050919050565b60008115159050919050565b6000819050919050565b6000610c5282610c1f565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b82818337600083830152505050565b60005b83811015610cb0578082015181840152602081019050610c95565b83811115610cbf576000848401525b50505050565b610cce82610d57565b810181811067ffffffffffffffff82111715610ced57610cec610cf6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f7265766572740000000000000000000000000000000000000000000000000000600082015250565b610d9a81610c1f565b8114610da557600080fd5b50565b610db181610c31565b8114610dbc57600080fd5b50565b610dc881610c3d565b8114610dd357600080fd5b50565b610ddf81610c47565b8114610dea57600080fd5b50565b610df681610c79565b8114610e0157600080fd5b5056fea2646970667358221220d6765b67214e8cadf15b569ed63c4c4628bd833acf6926d71b9b6a6011fbb78064736f6c63430008070033" +} diff --git a/pkg/contracts/testdappv2/TestDAppV2.sol b/pkg/contracts/testdappv2/TestDAppV2.sol new file mode 100644 index 0000000000..4ab39a05a0 --- /dev/null +++ b/pkg/contracts/testdappv2/TestDAppV2.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.7; + +interface IERC20 { + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); +} + +contract TestDAppV2 { + struct zContext { + bytes origin; + address sender; + uint256 chainID; + } + + /// @notice Struct containing revert context passed to onRevert. + /// @param asset Address of asset, empty if it's gas token. + /// @param amount Amount specified with the transaction. + /// @param revertMessage Arbitrary data sent back in onRevert. + struct RevertContext { + address asset; + uint64 amount; + bytes revertMessage; + } + + // these structures allow to assess contract calls + mapping(bytes32 => bool) public calledWithMessage; + mapping(bytes32 => uint256) public amountWithMessage; + + function setCalledWithMessage(string memory message) internal { + calledWithMessage[keccak256(abi.encodePacked(message))] = true; + } + function setAmountWithMessage(string memory message, uint256 amount) internal { + amountWithMessage[keccak256(abi.encodePacked(message))] = amount; + } + + function getCalledWithMessage(string memory message) public view returns (bool) { + return calledWithMessage[keccak256(abi.encodePacked(message))]; + } + + function getAmountWithMessage(string memory message) public view returns (uint256) { + return amountWithMessage[keccak256(abi.encodePacked(message))]; + } + + // Universal contract interface + function onCrossChainCall( + zContext calldata _context, + address _zrc20, + uint256 amount, + bytes calldata message + ) + external + { + require(!isRevertMessage(string(message))); + + setCalledWithMessage(string(message)); + setAmountWithMessage(string(message), amount); + } + + // called with gas token + function gasCall(string memory message) external payable { + // Revert if the message is "revert" + require(!isRevertMessage(message)); + + setCalledWithMessage(message); + setAmountWithMessage(message, msg.value); + } + + // called with ERC20 token + function erc20Call(IERC20 erc20, uint256 amount, string memory message) external { + require(!isRevertMessage(message)); + require(erc20.transferFrom(msg.sender, address(this), amount)); + + setCalledWithMessage(message); + setAmountWithMessage(message, amount); + } + + // called without token + function simpleCall(string memory message) external { + require(!isRevertMessage(message)); + + setCalledWithMessage(message); + setAmountWithMessage(message, 0); + } + + // used to make functions revert + function isRevertMessage(string memory message) internal pure returns (bool) { + return keccak256(abi.encodePacked(message)) == keccak256(abi.encodePacked("revert")); + } + + // Revertable interface + function onRevert(RevertContext calldata revertContext) external { + setCalledWithMessage(string(revertContext.revertMessage)); + setAmountWithMessage(string(revertContext.revertMessage), 0); + } + + receive() external payable {} +} \ No newline at end of file diff --git a/pkg/contracts/testdappv2/bindings.go b/pkg/contracts/testdappv2/bindings.go new file mode 100644 index 0000000000..73843ba2d2 --- /dev/null +++ b/pkg/contracts/testdappv2/bindings.go @@ -0,0 +1,8 @@ +//go:generate sh -c "solc TestDAppV2.sol --combined-json abi,bin | jq '.contracts.\"TestDAppV2.sol:TestDAppV2\"' > TestDAppV2.json" +//go:generate sh -c "cat TestDAppV2.json | jq .abi > TestDAppV2.abi" +//go:generate sh -c "cat TestDAppV2.json | jq .bin | tr -d '\"' > TestDAppV2.bin" +//go:generate sh -c "abigen --abi TestDAppV2.abi --bin TestDAppV2.bin --pkg testdappv2 --type TestDAppV2 --out TestDAppV2.go" + +package testdappv2 + +var _ TestDAppV2 diff --git a/pkg/crypto/address.go b/pkg/crypto/address.go new file mode 100644 index 0000000000..4d0e9bc5a6 --- /dev/null +++ b/pkg/crypto/address.go @@ -0,0 +1,12 @@ +package crypto + +import ( + "github.com/ethereum/go-ethereum/common" + + "github.com/zeta-chain/zetacore/pkg/constant" +) + +// IsEmptyAddress returns true if the address is empty +func IsEmptyAddress(address common.Address) bool { + return address == (common.Address{}) || address.Hex() == constant.EVMZeroAddress +} diff --git a/pkg/crypto/address_test.go b/pkg/crypto/address_test.go new file mode 100644 index 0000000000..162481730d --- /dev/null +++ b/pkg/crypto/address_test.go @@ -0,0 +1,37 @@ +package crypto + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/constant" + "testing" +) + +func TestIsEmptyAddress(t *testing.T) { + tests := []struct { + name string + address common.Address + want bool + }{ + { + name: "empty address", + address: common.Address{}, + want: true, + }, + { + name: "zero address", + address: common.HexToAddress(constant.EVMZeroAddress), + want: true, + }, + { + name: "non empty address", + address: common.HexToAddress("0x1"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.EqualValues(t, tt.want, IsEmptyAddress(tt.address)) + }) + } +} diff --git a/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto b/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto index c1df04e719..41028156c6 100644 --- a/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto +++ b/proto/zetachain/zetacore/crosschain/cross_chain_tx.proto @@ -93,6 +93,26 @@ message Status { int64 created_timestamp = 5; } +// ProtocolContractVersion represents the version of the protocol contract used +// for cctx workflow +enum ProtocolContractVersion { + option (gogoproto.goproto_enum_stringer) = true; + V1 = 0; + V2 = 1; +} + +// RevertOptions represents the options for reverting a cctx +message RevertOptions { + string revert_address = 1; + bool call_on_revert = 2; + string abort_address = 3; + bytes revert_message = 4; + string revert_gas_limit = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + message CrossChainTx { string creator = 1; string index = 2; @@ -105,4 +125,6 @@ message CrossChainTx { Status cctx_status = 8; InboundParams inbound_params = 9; repeated OutboundParams outbound_params = 10; + ProtocolContractVersion protocol_contract_version = 11; + RevertOptions revert_options = 12 [ (gogoproto.nullable) = false ]; } diff --git a/proto/zetachain/zetacore/crosschain/tx.proto b/proto/zetachain/zetacore/crosschain/tx.proto index e7a82a658f..a34ff32fb9 100644 --- a/proto/zetachain/zetacore/crosschain/tx.proto +++ b/proto/zetachain/zetacore/crosschain/tx.proto @@ -6,6 +6,7 @@ import "zetachain/zetacore/pkg/chains/chains.proto"; import "zetachain/zetacore/pkg/coin/coin.proto"; import "zetachain/zetacore/pkg/proofs/proofs.proto"; import "zetachain/zetacore/crosschain/rate_limiter_flags.proto"; +import "zetachain/zetacore/crosschain/cross_chain_tx.proto"; option go_package = "github.com/zeta-chain/zetacore/x/crosschain/types"; @@ -168,6 +169,12 @@ message MsgVoteInbound { string asset = 14; // event index of the sent asset in the observed tx uint64 event_index = 15; + + // protocol contract version to use for the cctx workflow + ProtocolContractVersion protocol_contract_version = 16; + + // revert options provided by the sender + RevertOptions revert_options = 17 [ (gogoproto.nullable) = false ]; } message MsgVoteInboundResponse {} diff --git a/proto/zetachain/zetacore/pkg/coin/coin.proto b/proto/zetachain/zetacore/pkg/coin/coin.proto index 4b43786228..e80fb4abb0 100644 --- a/proto/zetachain/zetacore/pkg/coin/coin.proto +++ b/proto/zetachain/zetacore/pkg/coin/coin.proto @@ -8,7 +8,8 @@ option go_package = "github.com/zeta-chain/zetacore/pkg/coin"; enum CoinType { option (gogoproto.goproto_enum_stringer) = true; Zeta = 0; - Gas = 1; // Ether, BNB, Matic, Klay, BTC, etc - ERC20 = 2; // ERC20 token - Cmd = 3; // not a real coin, rather a command + Gas = 1; // Ether, BNB, Matic, Klay, BTC, etc + ERC20 = 2; // ERC20 token + Cmd = 3; // no asset, used for admin command + NoAssetCall = 4; // no asset, used for contract call } diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index d12e4e37f9..886e3e5792 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -359,6 +359,7 @@ func MockRevertForHandleEVMDeposit( mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) } diff --git a/testutil/keeper/fungible.go b/testutil/keeper/fungible.go index 9c25c80294..29ddc25790 100644 --- a/testutil/keeper/fungible.go +++ b/testutil/keeper/fungible.go @@ -225,7 +225,7 @@ func (m *FungibleMockEVMKeeper) SetupMockEVMKeeperForSystemContractDeployment() mock.Anything, mock.Anything, ).Return(gasRes, nil) - m.MockEVMSuccessCallTimes(5) + m.MockEVMSuccessCallTimes(7) m.On( "GetAccount", mock.Anything, diff --git a/testutil/keeper/mocks/crosschain/fungible.go b/testutil/keeper/mocks/crosschain/fungible.go index 9eab868481..ac48687449 100644 --- a/testutil/keeper/mocks/crosschain/fungible.go +++ b/testutil/keeper/mocks/crosschain/fungible.go @@ -8,6 +8,8 @@ import ( common "github.com/ethereum/go-ethereum/common" coin "github.com/zeta-chain/zetacore/pkg/coin" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + evmtypes "github.com/evmos/ethermint/x/evm/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" @@ -416,6 +418,24 @@ func (_m *CrosschainFungibleKeeper) GetUniswapV2Router02Address(ctx types.Contex return r0, r1 } +// ProcessV2RevertDeposit provides a mock function with given fields: ctx, amount, chainID, coinType, asset, revertAddress, callOnRevert, revertMessage +func (_m *CrosschainFungibleKeeper) ProcessV2RevertDeposit(ctx types.Context, amount *big.Int, chainID int64, coinType coin.CoinType, asset string, revertAddress common.Address, callOnRevert bool, revertMessage []byte) error { + ret := _m.Called(ctx, amount, chainID, coinType, asset, revertAddress, callOnRevert, revertMessage) + + if len(ret) == 0 { + panic("no return value specified for ProcessV2RevertDeposit") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, *big.Int, int64, coin.CoinType, string, common.Address, bool, []byte) error); ok { + r0 = rf(ctx, amount, chainID, coinType, asset, revertAddress, callOnRevert, revertMessage) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // QueryGasLimit provides a mock function with given fields: ctx, contract func (_m *CrosschainFungibleKeeper) QueryGasLimit(ctx types.Context, contract common.Address) (*big.Int, error) { ret := _m.Called(ctx, contract) @@ -677,9 +697,9 @@ func (_m *CrosschainFungibleKeeper) ZETARevertAndCallContract(ctx types.Context, return r0, r1 } -// ZRC20DepositAndCallContract provides a mock function with given fields: ctx, from, to, amount, senderChainID, data, coinType, asset -func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Context, from []byte, to common.Address, amount *big.Int, senderChainID int64, data []byte, coinType coin.CoinType, asset string) (*evmtypes.MsgEthereumTxResponse, bool, error) { - ret := _m.Called(ctx, from, to, amount, senderChainID, data, coinType, asset) +// ZRC20DepositAndCallContract provides a mock function with given fields: ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion +func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Context, from []byte, to common.Address, amount *big.Int, senderChainID int64, data []byte, coinType coin.CoinType, asset string, protocolContractVersion crosschaintypes.ProtocolContractVersion) (*evmtypes.MsgEthereumTxResponse, bool, error) { + ret := _m.Called(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) if len(ret) == 0 { panic("no return value specified for ZRC20DepositAndCallContract") @@ -688,25 +708,25 @@ func (_m *CrosschainFungibleKeeper) ZRC20DepositAndCallContract(ctx types.Contex var r0 *evmtypes.MsgEthereumTxResponse var r1 bool var r2 error - if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string) (*evmtypes.MsgEthereumTxResponse, bool, error)); ok { - return rf(ctx, from, to, amount, senderChainID, data, coinType, asset) + if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) (*evmtypes.MsgEthereumTxResponse, bool, error)); ok { + return rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) } - if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string) *evmtypes.MsgEthereumTxResponse); ok { - r0 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset) + if rf, ok := ret.Get(0).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) *evmtypes.MsgEthereumTxResponse); ok { + r0 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*evmtypes.MsgEthereumTxResponse) } } - if rf, ok := ret.Get(1).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string) bool); ok { - r1 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset) + if rf, ok := ret.Get(1).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) bool); ok { + r1 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) } else { r1 = ret.Get(1).(bool) } - if rf, ok := ret.Get(2).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string) error); ok { - r2 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset) + if rf, ok := ret.Get(2).(func(types.Context, []byte, common.Address, *big.Int, int64, []byte, coin.CoinType, string, crosschaintypes.ProtocolContractVersion) error); ok { + r2 = rf(ctx, from, to, amount, senderChainID, data, coinType, asset, protocolContractVersion) } else { r2 = ret.Error(2) } diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 2894431724..6742c2d2f1 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -14,7 +14,7 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/stretchr/testify/require" - zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -53,33 +53,6 @@ func RateLimiterFlags() types.RateLimiterFlags { } } -// CustomRateLimiterFlags creates a custom rate limiter flags with the given parameters -func CustomRateLimiterFlags( - enabled bool, - window int64, - rate math.Uint, - conversions []types.Conversion, -) types.RateLimiterFlags { - return types.RateLimiterFlags{ - Enabled: enabled, - Window: window, - Rate: rate, - Conversions: conversions, - } -} - -func AssetRate() types.AssetRate { - r := Rand() - - return types.AssetRate{ - ChainId: r.Int63(), - Asset: EthAddress().Hex(), - Decimals: uint32(r.Uint64()), - CoinType: coin.CoinType_ERC20, - Rate: sdk.NewDec(r.Int63()), - } -} - // CustomAssetRate creates a custom asset rate with the given parameters func CustomAssetRate( chainID int64, @@ -227,13 +200,15 @@ func CrossChainTx(t *testing.T, index string) *types.CrossChainTx { r := newRandFromStringSeed(t, index) return &types.CrossChainTx{ - Creator: AccAddress(), - Index: GetCctxIndexFromString(index), - ZetaFees: math.NewUint(uint64(r.Int63())), - RelayedMessage: StringRandom(r, 32), - CctxStatus: Status(t, index), - InboundParams: InboundParams(r), - OutboundParams: []*types.OutboundParams{OutboundParams(r), OutboundParams(r)}, + Creator: AccAddress(), + Index: GetCctxIndexFromString(index), + ZetaFees: math.NewUint(uint64(r.Int63())), + RelayedMessage: StringRandom(r, 32), + CctxStatus: Status(t, index), + InboundParams: InboundParams(r), + OutboundParams: []*types.OutboundParams{OutboundParams(r), OutboundParams(r)}, + ProtocolContractVersion: types.ProtocolContractVersion_V1, + RevertOptions: types.NewEmptyRevertOptions(), } } @@ -318,7 +293,7 @@ func ZRC20Withdrawal(to []byte, value *big.Int) *zrc20.ZRC20Withdrawal { From: EthAddress(), To: to, Value: value, - Gasfee: big.NewInt(Int64InRange(100000, 10000000)), + GasFee: big.NewInt(Int64InRange(100000, 10000000)), ProtocolFlatFee: big.NewInt(Int64InRange(100000, 10000000)), } } diff --git a/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts index a6a8bd9057..a0ab4b06ca 100644 --- a/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/cross_chain_tx_pb.d.ts @@ -80,6 +80,24 @@ export declare enum TxFinalizationStatus { Executed = 2, } +/** + * ProtocolContractVersion represents the version of the protocol contract used + * for cctx workflow + * + * @generated from enum zetachain.zetacore.crosschain.ProtocolContractVersion + */ +export declare enum ProtocolContractVersion { + /** + * @generated from enum value: V1 = 0; + */ + V1 = 0, + + /** + * @generated from enum value: V2 = 1; + */ + V2 = 1, +} + /** * @generated from message zetachain.zetacore.crosschain.InboundParams */ @@ -337,6 +355,52 @@ export declare class Status extends Message { static equals(a: Status | PlainMessage | undefined, b: Status | PlainMessage | undefined): boolean; } +/** + * RevertOptions represents the options for reverting a cctx + * + * @generated from message zetachain.zetacore.crosschain.RevertOptions + */ +export declare class RevertOptions extends Message { + /** + * @generated from field: string revert_address = 1; + */ + revertAddress: string; + + /** + * @generated from field: bool call_on_revert = 2; + */ + callOnRevert: boolean; + + /** + * @generated from field: string abort_address = 3; + */ + abortAddress: string; + + /** + * @generated from field: bytes revert_message = 4; + */ + revertMessage: Uint8Array; + + /** + * @generated from field: string revert_gas_limit = 5; + */ + revertGasLimit: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.RevertOptions"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): RevertOptions; + + static fromJson(jsonValue: JsonValue, options?: Partial): RevertOptions; + + static fromJsonString(jsonString: string, options?: Partial): RevertOptions; + + static equals(a: RevertOptions | PlainMessage | undefined, b: RevertOptions | PlainMessage | undefined): boolean; +} + /** * @generated from message zetachain.zetacore.crosschain.CrossChainTx */ @@ -378,6 +442,16 @@ export declare class CrossChainTx extends Message { */ outboundParams: OutboundParams[]; + /** + * @generated from field: zetachain.zetacore.crosschain.ProtocolContractVersion protocol_contract_version = 11; + */ + protocolContractVersion: ProtocolContractVersion; + + /** + * @generated from field: zetachain.zetacore.crosschain.RevertOptions revert_options = 12; + */ + revertOptions?: RevertOptions; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts index 4069410e30..657a772384 100644 --- a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts @@ -8,6 +8,7 @@ import { Message, proto3 } from "@bufbuild/protobuf"; import type { CoinType } from "../pkg/coin/coin_pb.js"; import type { Proof } from "../pkg/proofs/proofs_pb.js"; import type { ReceiveStatus } from "../pkg/chains/chains_pb.js"; +import type { ProtocolContractVersion, RevertOptions } from "./cross_chain_tx_pb.js"; import type { RateLimiterFlags } from "./rate_limiter_flags_pb.js"; /** @@ -641,6 +642,20 @@ export declare class MsgVoteInbound extends Message { */ eventIndex: bigint; + /** + * protocol contract version to use for the cctx workflow + * + * @generated from field: zetachain.zetacore.crosschain.ProtocolContractVersion protocol_contract_version = 16; + */ + protocolContractVersion: ProtocolContractVersion; + + /** + * revert options provided by the sender + * + * @generated from field: zetachain.zetacore.crosschain.RevertOptions revert_options = 17; + */ + revertOptions?: RevertOptions; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/zetachain/zetacore/pkg/coin/coin_pb.d.ts b/typescript/zetachain/zetacore/pkg/coin/coin_pb.d.ts index 2ad47ca416..f3b177ebb2 100644 --- a/typescript/zetachain/zetacore/pkg/coin/coin_pb.d.ts +++ b/typescript/zetachain/zetacore/pkg/coin/coin_pb.d.ts @@ -27,10 +27,17 @@ export declare enum CoinType { ERC20 = 2, /** - * not a real coin, rather a command + * no asset, used for admin command * * @generated from enum value: Cmd = 3; */ Cmd = 3, + + /** + * no asset, used for contract call + * + * @generated from enum value: NoAssetCall = 4; + */ + NoAssetCall = 4, } diff --git a/x/crosschain/client/cli/tx_vote_inbound.go b/x/crosschain/client/cli/tx_vote_inbound.go index 3581b01e0c..61d6deab2e 100644 --- a/x/crosschain/client/cli/tx_vote_inbound.go +++ b/x/crosschain/client/cli/tx_vote_inbound.go @@ -17,9 +17,9 @@ import ( func CmdVoteInbound() *cobra.Command { cmd := &cobra.Command{ Use: "vote-inbound [sender] [senderChainID] [txOrigin] [receiver] [receiverChainID] [amount] [message" + - "] [inboundHash] [inBlockHeight] [coinType] [asset] [eventIndex]", + "] [inboundHash] [inBlockHeight] [coinType] [asset] [eventIndex] [protocolContractVersion]", Short: "Broadcast message to vote an inbound", - Args: cobra.ExactArgs(12), + Args: cobra.ExactArgs(13), RunE: func(cmd *cobra.Command, args []string) error { argsSender := args[0] argsSenderChain, err := strconv.ParseInt(args[1], 10, 64) @@ -62,6 +62,11 @@ func CmdVoteInbound() *cobra.Command { return err } + protocolContractVersion, err := parseProtocolContractVersion(args[12]) + if err != nil { + return err + } + msg := types.NewMsgVoteInbound( clientCtx.GetFromAddress().String(), argsSender, @@ -77,6 +82,7 @@ func CmdVoteInbound() *cobra.Command { argsCoinType, argsAsset, uint(argsEventIndex), + protocolContractVersion, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) @@ -87,3 +93,16 @@ func CmdVoteInbound() *cobra.Command { return cmd } + +func parseProtocolContractVersion(version string) (types.ProtocolContractVersion, error) { + switch version { + case "V1": + return types.ProtocolContractVersion_V1, nil + case "V2": + return types.ProtocolContractVersion_V2, nil + default: + return types.ProtocolContractVersion_V1, fmt.Errorf( + "invalid protocol contract version, specify either V1 or V2", + ) + } +} diff --git a/x/crosschain/client/querytests/cctx.go b/x/crosschain/client/querytests/cctx.go deleted file mode 100644 index 0fe177e6ca..0000000000 --- a/x/crosschain/client/querytests/cctx.go +++ /dev/null @@ -1,122 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestListCCTX() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.CrossChainTxs - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListSend(), args) - s.Require().NoError(err) - var resp types.QueryAllCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Require().Equal(objs[j], resp.CrossChainTx[j-i]) - } - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListSend(), args) - s.Require().NoError(err) - var resp types.QueryAllCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.CrossChainTx[j-i]) - } - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListSend(), args) - s.Require().NoError(err) - var resp types.QueryAllCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // #nosec G115 always in range - s.Require().Equal(len(objs), int(resp.Pagination.Total)) - s.Require().Equal(objs, resp.CrossChainTx) - }) -} - -func (s *CliTestSuite) TestShowSend() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.CrossChainTxs - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - id string - args []string - err error - obj *types.CrossChainTx - }{ - { - desc: "found", - id: objs[0].Index, - args: common, - obj: objs[0], - }, - { - desc: "not found", - id: "not_found", - args: common, - err: status.Error(codes.InvalidArgument, "not found"), - }, - } { - tc := tc - s.Run(tc.desc, func() { - args := []string{tc.id} - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowSend(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.CrossChainTx) - s.Require().Equal(tc.obj, resp.CrossChainTx) - } - }) - } -} diff --git a/x/crosschain/client/querytests/cli_test.go b/x/crosschain/client/querytests/cli_test.go deleted file mode 100644 index 777fc81528..0000000000 --- a/x/crosschain/client/querytests/cli_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package querytests - -import ( - "testing" - - tmdb "github.com/cometbft/cometbft-db" - "github.com/cosmos/cosmos-sdk/baseapp" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/stretchr/testify/suite" - - "github.com/zeta-chain/zetacore/app" - "github.com/zeta-chain/zetacore/testutil/network" -) - -func TestCLIQuerySuite(t *testing.T) { - cfg := network.DefaultConfig(NewTestNetworkFixture) - suite.Run(t, NewCLITestSuite(cfg)) -} - -func NewTestNetworkFixture() network.TestFixture { - encoding := app.MakeEncodingConfig() - appCtr := func(val network.ValidatorI) servertypes.Application { - return app.New( - val.GetCtx().Logger, tmdb.NewMemDB(), nil, true, map[int64]bool{}, val.GetCtx().Config.RootDir, 0, - encoding, - simtestutil.EmptyAppOptions{}, - baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), - baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), - baseapp.SetChainID("athens_8888-2"), - ) - } - - return network.TestFixture{ - AppConstructor: appCtr, - GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Codec), - EncodingConfig: testutil.TestEncodingConfig{ - InterfaceRegistry: encoding.InterfaceRegistry, - Codec: encoding.Codec, - TxConfig: encoding.TxConfig, - Amino: encoding.Amino, - }, - } -} diff --git a/x/crosschain/client/querytests/gas_price.go b/x/crosschain/client/querytests/gas_price.go deleted file mode 100644 index 7a0c6422c8..0000000000 --- a/x/crosschain/client/querytests/gas_price.go +++ /dev/null @@ -1,122 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestShowGasPrice() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.GasPriceList - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - id string - args []string - err error - obj *types.GasPrice - }{ - { - desc: "found", - id: objs[0].Index, - args: common, - obj: objs[0], - }, - { - desc: "not found", - id: "not_found", - args: common, - err: status.Error(codes.InvalidArgument, "not found"), - }, - } { - tc := tc - s.Run(tc.desc, func() { - args := []string{tc.id} - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowGasPrice(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetGasPriceResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.GasPrice) - s.Require().Equal(tc.obj, resp.GasPrice) - } - }) - } -} - -func (s *CliTestSuite) TestListGasPrice() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.GasPriceList - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) - s.Require().NoError(err) - var resp types.QueryAllGasPriceResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.GasPrice[j-i]) - } - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) - s.Require().NoError(err) - var resp types.QueryAllGasPriceResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.GasPrice[j-i]) - } - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - // #nosec G115 always in range - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListGasPrice(), args) - s.Require().NoError(err) - var resp types.QueryAllGasPriceResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // #nosec G115 always in range - s.Require().Equal(len(objs), int(resp.Pagination.Total)) - s.Require().Equal(objs, resp.GasPrice) - }) -} diff --git a/x/crosschain/client/querytests/inbound_hash.go b/x/crosschain/client/querytests/inbound_hash.go deleted file mode 100644 index 9f6d48ef53..0000000000 --- a/x/crosschain/client/querytests/inbound_hash.go +++ /dev/null @@ -1,137 +0,0 @@ -package querytests - -import ( - "fmt" - "strconv" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/testutil/nullify" - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestShowInboundHashToCctx() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.InboundHashToCctxList - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - idInboundHash string - - args []string - err error - obj types.InboundHashToCctx - }{ - { - desc: "found", - idInboundHash: objs[0].InboundHash, - - args: common, - obj: objs[0], - }, - { - desc: "not found", - idInboundHash: strconv.Itoa(100000), - - args: common, - err: status.Error(codes.NotFound, "not found"), - }, - } { - s.Run(tc.desc, func() { - args := []string{ - tc.idInboundHash, - } - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowInboundHashToCctx(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetInboundHashToCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.InboundHashToCctx) - tc := tc - s.Require().Equal(nullify.Fill(&tc.obj), - nullify.Fill(&resp.InboundHashToCctx), - ) - } - }) - } -} - -func (s *CliTestSuite) TestListInboundHashToCctx() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.InboundHashToCctxList - cctxCount := len(s.crosschainState.CrossChainTxs) - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundHashToCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.InboundHashToCctx), step) - s.Require().Subset(nullify.Fill(objs), - nullify.Fill(resp.InboundHashToCctx), - ) - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundHashToCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.InboundHashToCctx), step) - s.Require().Subset(nullify.Fill(objs), - nullify.Fill(resp.InboundHashToCctx), - ) - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - // #nosec G115 always in range - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundHashToCctx(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundHashToCctxResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // saving CCTX also adds a new mapping - // #nosec G115 always in range - s.Require().Equal(len(objs)+cctxCount, int(resp.Pagination.Total)) - s.Require().ElementsMatch(nullify.Fill(objs), - nullify.Fill(resp.InboundHashToCctx), - ) - }) -} diff --git a/x/crosschain/client/querytests/inbound_tracker.go b/x/crosschain/client/querytests/inbound_tracker.go deleted file mode 100644 index 162b552865..0000000000 --- a/x/crosschain/client/querytests/inbound_tracker.go +++ /dev/null @@ -1,104 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - - "github.com/zeta-chain/zetacore/testutil/nullify" - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestListInboundTrackers() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.InboundTrackerList - s.Run("List all trackers", func() { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackers(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundTrackersResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().Equal(len(objs), len(resp.InboundTracker)) - s.Require().ElementsMatch(nullify.Fill(objs), nullify.Fill(resp.InboundTracker)) - }) -} - -func (s *CliTestSuite) TestListInboundTrackersByChain() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.InboundTrackerList - request := func(next []byte, offset, limit uint64, total bool, chainID int) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - args = append(args, fmt.Sprintf("%d", chainID)) - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always positive - args := request(nil, uint64(i), uint64(step), false, 5) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundTrackerByChainResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.InboundTracker), step) - s.Require().Subset(nullify.Fill(objs), - nullify.Fill(resp.InboundTracker), - ) - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always positive - args := request(next, 0, uint64(step), false, 5) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundTrackerByChainResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.InboundTracker), step) - s.Require().Subset( - nullify.Fill(objs), - nullify.Fill(resp.InboundTracker), - ) - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - args := request(nil, 0, uint64(len(objs)), true, 5) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundTrackerByChainResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - s.Require().Equal(uint64(len(objs)), resp.Pagination.Total) - s.Require().ElementsMatch(nullify.Fill(objs), - nullify.Fill(resp.InboundTracker), - ) - }) - s.Run("Incorrect Chain ID ", func() { - args := request(nil, 0, uint64(len(objs)), true, 15) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListInboundTrackerByChain(), args) - s.Require().NoError(err) - var resp types.QueryAllInboundTrackerByChainResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - s.Require().Equal(uint64(0), resp.Pagination.Total) - }) -} diff --git a/x/crosschain/client/querytests/last_block_height.go b/x/crosschain/client/querytests/last_block_height.go deleted file mode 100644 index b7bf12f015..0000000000 --- a/x/crosschain/client/querytests/last_block_height.go +++ /dev/null @@ -1,122 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestShowLastBlockHeight() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.LastBlockHeightList - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - id string - args []string - err error - obj *types.LastBlockHeight - }{ - { - desc: "found", - id: objs[0].Index, - args: common, - obj: objs[0], - }, - { - desc: "not found", - id: "not_found", - args: common, - err: status.Error(codes.InvalidArgument, "not found"), - }, - } { - tc := tc - s.Run(tc.desc, func() { - args := []string{tc.id} - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowLastBlockHeight(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetLastBlockHeightResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.LastBlockHeight) - s.Require().Equal(tc.obj, resp.LastBlockHeight) - } - }) - } -} - -func (s *CliTestSuite) TestListLastBlockHeight() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.LastBlockHeightList - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) - s.Require().NoError(err) - var resp types.QueryAllLastBlockHeightResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.LastBlockHeight[j-i]) - } - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) - s.Require().NoError(err) - var resp types.QueryAllLastBlockHeightResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.LastBlockHeight[j-i]) - } - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - // #nosec G115 always in range - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListLastBlockHeight(), args) - s.Require().NoError(err) - var resp types.QueryAllLastBlockHeightResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // #nosec G115 always in range - s.Require().Equal(len(objs), int(resp.Pagination.Total)) - s.Require().Equal(objs, resp.LastBlockHeight) - }) -} diff --git a/x/crosschain/client/querytests/outbound_tracker.go b/x/crosschain/client/querytests/outbound_tracker.go deleted file mode 100644 index 6a6ded0b6d..0000000000 --- a/x/crosschain/client/querytests/outbound_tracker.go +++ /dev/null @@ -1,80 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - - "github.com/zeta-chain/zetacore/testutil/nullify" - "github.com/zeta-chain/zetacore/x/crosschain/client/cli" - "github.com/zeta-chain/zetacore/x/crosschain/types" -) - -func (s *CliTestSuite) TestListOutboundTracker() { - ctx := s.network.Validators[0].ClientCtx - objs := s.crosschainState.OutboundTrackerList - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) - s.Require().NoError(err) - var resp types.QueryAllOutboundTrackerResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.OutboundTracker), step) - s.Require().Subset(nullify.Fill(objs), - nullify.Fill(resp.OutboundTracker), - ) - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) - s.Require().NoError(err) - var resp types.QueryAllOutboundTrackerResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().LessOrEqual(len(resp.OutboundTracker), step) - s.Require().Subset( - nullify.Fill(objs), - nullify.Fill(resp.OutboundTracker), - ) - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - // #nosec G115 always in range - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListOutboundTracker(), args) - s.Require().NoError(err) - var resp types.QueryAllOutboundTrackerResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // #nosec G115 always in range - s.Require().Equal(len(objs), int(resp.Pagination.Total)) - s.Require().ElementsMatch(nullify.Fill(objs), - nullify.Fill(resp.OutboundTracker), - ) - }) -} diff --git a/x/crosschain/client/querytests/suite.go b/x/crosschain/client/querytests/suite.go deleted file mode 100644 index d9354b6d18..0000000000 --- a/x/crosschain/client/querytests/suite.go +++ /dev/null @@ -1,60 +0,0 @@ -package querytests - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - ethcfg "github.com/evmos/ethermint/cmd/config" - "github.com/stretchr/testify/suite" - - "github.com/zeta-chain/zetacore/app" - cmdcfg "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/testutil/network" - "github.com/zeta-chain/zetacore/x/crosschain/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -type CliTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network - crosschainState *types.GenesisState - observerState *observerTypes.GenesisState -} - -func NewCLITestSuite(cfg network.Config) *CliTestSuite { - return &CliTestSuite{cfg: cfg} -} - -func (s *CliTestSuite) Setconfig() { - config := sdk.GetConfig() - cmdcfg.SetBech32Prefixes(config) - ethcfg.SetBip44CoinType(config) - // Make sure the address is compatible with ethereum - config.SetAddressVerifier(app.VerifyAddressFormat) - config.Seal() -} - -func (s *CliTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - s.Setconfig() - minOBsDel, ok := sdk.NewIntFromString("100000000000000000000") - s.Require().True(ok) - s.cfg.StakingTokens = minOBsDel.Mul(sdk.NewInt(int64(10))) - s.cfg.BondedTokens = minOBsDel - observerList := []string{"zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", - } - network.SetupZetaGenesisState(s.T(), s.cfg.GenesisState, s.cfg.Codec, observerList, false) - s.crosschainState = network.AddCrosschainData(s.T(), 2, s.cfg.GenesisState, s.cfg.Codec) - s.observerState = network.AddObserverData(s.T(), 2, s.cfg.GenesisState, s.cfg.Codec, nil) - net, err := network.New(s.T(), app.NodeDir, s.cfg) - s.Assert().NoError(err) - s.network = net - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) -} - -func (s *CliTestSuite) TearDownSuite() { - s.T().Log("tearing down genesis test suite") - s.network.Cleanup() -} diff --git a/x/crosschain/keeper/cctx.go b/x/crosschain/keeper/cctx.go index 306df6979b..f9cf7ee570 100644 --- a/x/crosschain/keeper/cctx.go +++ b/x/crosschain/keeper/cctx.go @@ -54,7 +54,7 @@ func (k Keeper) SetCctxAndNonceToCctxAndInboundHashToCctx(ctx sdk.Context, cctx } } -// SetCrossChainTx set a specific send in the store from its index +// SetCrossChainTx set a specific cctx in the store from its index func (k Keeper) SetCrossChainTx(ctx sdk.Context, cctx types.CrossChainTx) { // only set the update timestamp if the block height is >0 to allow // for a genesis import @@ -67,7 +67,7 @@ func (k Keeper) SetCrossChainTx(ctx sdk.Context, cctx types.CrossChainTx) { store.Set(types.KeyPrefix(cctx.Index), b) } -// GetCrossChainTx returns a send from its index +// GetCrossChainTx returns a cctx from its index func (k Keeper) GetCrossChainTx(ctx sdk.Context, index string) (val types.CrossChainTx, found bool) { p := types.KeyPrefix(fmt.Sprintf("%s", types.CCTXKey)) store := prefix.NewStore(ctx.KVStore(k.storeKey), p) @@ -81,6 +81,7 @@ func (k Keeper) GetCrossChainTx(ctx sdk.Context, index string) (val types.CrossC return val, true } +// GetAllCrossChainTx returns all cctxs func (k Keeper) GetAllCrossChainTx(ctx sdk.Context) (list []types.CrossChainTx) { p := types.KeyPrefix(fmt.Sprintf("%s", types.CCTXKey)) store := prefix.NewStore(ctx.KVStore(k.storeKey), p) @@ -98,7 +99,7 @@ func (k Keeper) GetAllCrossChainTx(ctx sdk.Context) (list []types.CrossChainTx) return list } -// RemoveCrossChainTx removes a send from the store +// RemoveCrossChainTx removes a cctx from the store func (k Keeper) RemoveCrossChainTx(ctx sdk.Context, index string) { p := types.KeyPrefix(fmt.Sprintf("%s", types.CCTXKey)) store := prefix.NewStore(ctx.KVStore(k.storeKey), p) diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go index 1391bbaf9d..d4715271da 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound.go @@ -10,6 +10,7 @@ import ( tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -44,7 +45,7 @@ func (k Keeper) ValidateOutboundZEVM( if depositErr != nil && isContractReverted { tmpCtxRevert, commitRevert := ctx.CacheContext() // contract call reverted; should refund via a revert tx - err := k.validateFailedOutbound( + err := k.processFailedOutboundOnExternalChain( tmpCtxRevert, cctx, types.CctxStatus_PendingOutbound, @@ -52,14 +53,14 @@ func (k Keeper) ValidateOutboundZEVM( cctx.InboundParams.Amount, ) if err != nil { - cctx.SetAbort(err.Error()) + cctx.SetAbort(fmt.Sprintf("%s : %s", depositErr, err.Error())) return types.CctxStatus_Aborted } commitRevert() return types.CctxStatus_PendingRevert } - k.validateSuccessfulOutbound(ctx, cctx, "", false) + k.processSuccessfulOutbound(ctx, cctx, "", false) return types.CctxStatus_OutboundMined } @@ -75,12 +76,9 @@ func (k Keeper) ValidateOutboundObservers( err := func() error { switch ballotStatus { case observertypes.BallotStatus_BallotFinalized_SuccessObservation: - k.validateSuccessfulOutbound(tmpCtx, cctx, valueReceived, true) + k.processSuccessfulOutbound(tmpCtx, cctx, valueReceived, true) case observertypes.BallotStatus_BallotFinalized_FailureObservation: - err := k.validateFailedOutboundObservers(tmpCtx, cctx, valueReceived) - if err != nil { - return err - } + return k.processFailedOutboundObservers(tmpCtx, cctx, valueReceived) } return nil }() @@ -95,7 +93,7 @@ func (k Keeper) ValidateOutboundObservers( return nil } -// validateFailedOutboundObservers processes a failed outbound transaction for observers. It does the following things in one function: +// processFailedOutboundObservers processes a failed outbound transaction for observers. It does the following things in one function: // // 1. For Admin Tx or a withdrawal from Zeta chain, it aborts the CCTX // @@ -109,24 +107,28 @@ func (k Keeper) ValidateOutboundObservers( // // This function sets CCTX status , in cases where the outbound tx is successful, but tx itself fails // This is done because SaveSuccessfulOutbound does not set the cctx status -// For cases where the outbound tx is unsuccessful, the cctx status is automatically set to Aborted in the validateFailedOutboundObservers function, so we can just return and error to trigger that -func (k Keeper) validateFailedOutboundObservers(ctx sdk.Context, cctx *types.CrossChainTx, valueReceived string) error { +// For cases where the outbound tx is unsuccessful, the cctx status is automatically set to Aborted in the processFailedOutboundObservers function, so we can just return and error to trigger that +func (k Keeper) processFailedOutboundObservers(ctx sdk.Context, cctx *types.CrossChainTx, valueReceived string) error { oldStatus := cctx.CctxStatus.Status // The following logic is used to handler the mentioned conditions separately. The reason being // All admin tx is created using a policy message, there is no associated inbound tx, therefore we do not need any revert logic // For transactions which originated from ZEVM, we can process the outbound in the same block as there is no TSS signing required for the revert // For all other transactions we need to create a revert tx and set the status to pending revert + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 { + return k.processFailedOutboundV2(ctx, cctx) + } + if cctx.InboundParams.CoinType == coin.CoinType_Cmd { // if the cctx is of coin type cmd or the sender chain is zeta chain, then we do not revert, the cctx is aborted cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed") + cctx.SetAbort("Outbound failed, cmd cctx reverted") } else if chains.IsZetaChain(cctx.InboundParams.SenderChainId, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) { switch cctx.InboundParams.CoinType { // Try revert if the coin-type is ZETA case coin.CoinType_Zeta: { - err := k.validateFailedOutboundObserversForZEVM(ctx, cctx) + err := k.processFailedZETAOutboundOnZEVM(ctx, cctx) if err != nil { return cosmoserrors.Wrap(err, "validateFailedOutboundObserversForZEVMTx") } @@ -135,13 +137,13 @@ func (k Keeper) validateFailedOutboundObservers(ctx sdk.Context, cctx *types.Cro default: { cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed - cctx.SetAbort("Outbound failed") + cctx.SetAbort("Outbound failed for non-ZETA cctx") } } } else { - err := k.validateFailedOutbound(ctx, cctx, oldStatus, "Outbound failed, start revert", cctx.GetCurrentOutboundParam().Amount) + err := k.processFailedOutboundOnExternalChain(ctx, cctx, oldStatus, "Outbound failed, start revert", cctx.GetCurrentOutboundParam().Amount) if err != nil { - return cosmoserrors.Wrap(err, "validateFailedOutbound") + return cosmoserrors.Wrap(err, "processFailedOutboundOnExternalChain") } } newStatus := cctx.CctxStatus.Status.String() @@ -149,8 +151,8 @@ func (k Keeper) validateFailedOutboundObservers(ctx sdk.Context, cctx *types.Cro return nil } -// validateFailedOutbound processes the failed outbound transaction -func (k Keeper) validateFailedOutbound( +// processFailedOutboundOnExternalChain processes the failed outbound transaction where the receiver is an external chain. +func (k Keeper) processFailedOutboundOnExternalChain( ctx sdk.Context, cctx *types.CrossChainTx, oldStatus types.CctxStatus, @@ -201,7 +203,7 @@ func (k Keeper) validateFailedOutbound( return nil } -// validateSuccessfulOutbound processes a successful outbound transaction. It does the following things in one function: +// processSuccessfulOutbound processes a successful outbound transaction. It does the following things in one function: // // 1. Change the status of the CCTX from // - PendingRevert to Reverted @@ -213,8 +215,8 @@ func (k Keeper) validateFailedOutbound( // // This function sets CCTX status, in cases where the outbound tx is successful, but tx itself fails // This is done because SaveSuccessfulOutbound does not set the cctx status -// For cases where the outbound tx is unsuccessful, the cctx status is automatically set to Aborted in the validateFailedOutboundObservers function, so we can just return and error to trigger that -func (k Keeper) validateSuccessfulOutbound( +// For cases where the outbound tx is unsuccessful, the cctx status is automatically set to Aborted in the processFailedOutboundObservers function, so we can just return and error to trigger that +func (k Keeper) processSuccessfulOutbound( ctx sdk.Context, cctx *types.CrossChainTx, valueReceived string, @@ -236,8 +238,8 @@ func (k Keeper) validateSuccessfulOutbound( } } -// validateFailedOutboundObserversForZEVM processes the failed outbound transaction for ZEVM -func (k Keeper) validateFailedOutboundObserversForZEVM(ctx sdk.Context, cctx *types.CrossChainTx) error { +// processFailedZETAOutboundOnZEVM processes a failed ZETA outbound on ZEVM +func (k Keeper) processFailedZETAOutboundOnZEVM(ctx sdk.Context, cctx *types.CrossChainTx) error { indexBytes, err := cctx.GetCCTXIndexBytes() if err != nil { // Return err to save the failed outbound and set to aborted @@ -247,8 +249,8 @@ func (k Keeper) validateFailedOutboundObserversForZEVM(ctx sdk.Context, cctx *ty cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed // create new OutboundParams for the revert. We use the fixed gas limit for revert when calling zEVM - err = cctx.AddRevertOutbound(fungiblekeeper.ZEVMGasLimitDepositAndCall.Uint64()) - if err != nil { + + if err := cctx.AddRevertOutbound(fungiblekeeper.ZEVMGasLimitDepositAndCall.Uint64()); err != nil { // Return err to save the failed outbound ad set to aborted return fmt.Errorf("failed AddRevertOutbound: %s", err.Error()) } @@ -300,3 +302,71 @@ func (k Keeper) validateFailedOutboundObserversForZEVM(ctx sdk.Context, cctx *ty cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed return nil } + +// processFailedOutboundV2 processes a failed outbound transaction for protocol version 2 +// for revert, in V2 we have some assumption simplifying the logic +// - sender chain is always ZetaChain +// - all coin type use the same workflow +// TODO: consolidate logic with above function +// https://github.com/zeta-chain/node/issues/2627 +func (k Keeper) processFailedOutboundV2(ctx sdk.Context, cctx *types.CrossChainTx) error { + // check the sender is ZetaChain + zetaChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return errors.Wrap(err, "failed to get ZetaChain chainID") + } + if cctx.InboundParams.SenderChainId != zetaChain.ChainId { + return fmt.Errorf( + "sender chain for withdraw cctx is not ZetaChain expected %d got %d", + zetaChain.ChainId, + cctx.InboundParams.SenderChainId, + ) + } + + switch cctx.CctxStatus.Status { + case types.CctxStatus_PendingOutbound: + + // get the chain ID of the connected chain + chainID := cctx.GetCurrentOutboundParam().ReceiverChainId + + // add revert outbound + if err := cctx.AddRevertOutbound(fungiblekeeper.ZEVMGasLimitDepositAndCall.Uint64()); err != nil { + // Return err to save the failed outbound ad set to aborted + return errors.Wrap(err, "failed AddRevertOutbound") + } + + // update status + cctx.SetPendingRevert("Outbound failed, trying revert") + + // process the revert on ZEVM + if err := k.fungibleKeeper.ProcessV2RevertDeposit( + ctx, + cctx.GetCurrentOutboundParam().Amount.BigInt(), + chainID, + cctx.InboundParams.CoinType, + cctx.InboundParams.Asset, + ethcommon.HexToAddress(cctx.GetCurrentOutboundParam().Receiver), + cctx.RevertOptions.CallOnRevert, + cctx.RevertOptions.RevertMessage, + ); err != nil { + return errors.Wrap(err, "failed ProcessV2RevertDeposit") + } + + // tx is reverted + cctx.SetReverted("Outbound failed, revert executed") + + // add event for tendermint transaction hash format + if len(ctx.TxBytes()) > 0 { + hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) + ethTxHash := ethcommon.BytesToHash(hash) + cctx.GetCurrentOutboundParam().Hash = ethTxHash.String() + // #nosec G115 always positive + cctx.GetCurrentOutboundParam().ObservedExternalHeight = uint64(ctx.BlockHeight()) + } + cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed + case types.CctxStatus_PendingRevert: + cctx.GetCurrentOutboundParam().TxFinalizationStatus = types.TxFinalizationStatus_Executed + cctx.SetAbort("Outbound failed: revert failed; abort TX") + } + return nil +} diff --git a/x/crosschain/keeper/cctx_test.go b/x/crosschain/keeper/cctx_test.go index 647016849e..c523814c69 100644 --- a/x/crosschain/keeper/cctx_test.go +++ b/x/crosschain/keeper/cctx_test.go @@ -37,6 +37,7 @@ func createNCctxWithStatus( items[i].ZetaFees = math.OneUint() items[i].InboundParams = &types.InboundParams{ObservedHash: fmt.Sprintf("%d", i), Amount: math.OneUint()} items[i].OutboundParams = []*types.OutboundParams{{Amount: math.ZeroUint()}} + items[i].RevertOptions = types.NewEmptyRevertOptions() keeper.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, items[i]) } @@ -79,6 +80,8 @@ func createNCctx(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.CrossCha items[i].ZetaFees = math.OneUint() items[i].Index = fmt.Sprintf("%d", i) + items[i].RevertOptions = types.NewEmptyRevertOptions() + keeper.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, items[i]) } return items diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 6be78bdfaa..3303a99c87 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -67,6 +67,12 @@ func (k Keeper) SetObserverOutboundInfo(ctx sdk.Context, receiveChainID int64, c // GetRevertGasLimit returns the gas limit for the revert transaction in a CCTX // It returns 0 if there is no error but the gas limit can't be determined from the CCTX data func (k Keeper) GetRevertGasLimit(ctx sdk.Context, cctx types.CrossChainTx) (uint64, error) { + // with V2 protocol, reverts on connected chains can eventually call a onRevert function which can require a higher gas limit + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 && cctx.RevertOptions.CallOnRevert && + !cctx.RevertOptions.RevertGasLimit.IsZero() { + return cctx.RevertOptions.RevertGasLimit.Uint64(), nil + } + if cctx.InboundParams == nil { return 0, nil } diff --git a/x/crosschain/keeper/evm_deposit.go b/x/crosschain/keeper/evm_deposit.go index b1e55e3f6f..a85b98ed86 100644 --- a/x/crosschain/keeper/evm_deposit.go +++ b/x/crosschain/keeper/evm_deposit.go @@ -69,13 +69,29 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo return false, err } } else { - // cointype is Gas or ERC20; then it could be a ZRC20 deposit/depositAndCall cctx. - parsedAddress, data, err := chains.ParseAddressAndData(cctx.RelayedMessage) - if err != nil { - return false, errors.Wrap(types.ErrUnableToParseAddress, err.Error()) - } - if parsedAddress != (ethcommon.Address{}) { - to = parsedAddress + var ( + message []byte + err error + ) + + // in protocol version 1, the destination of the deposit is the first 20 bytes of the message when the message is not empty + // in protocol version 2, the destination of the deposit is always the to address, the message is the data to be sent to the contract + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V1 { + var parsedAddress ethcommon.Address + parsedAddress, message, err = chains.ParseAddressAndData(cctx.RelayedMessage) + if err != nil { + return false, errors.Wrap(types.ErrUnableToParseAddress, err.Error()) + } + if parsedAddress != (ethcommon.Address{}) { + to = parsedAddress + } + } else if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 { + if len(cctx.RelayedMessage) > 0 { + message, err = hex.DecodeString(cctx.RelayedMessage) + if err != nil { + return false, errors.Wrap(types.ErrUnableToDecodeMessageString, err.Error()) + } + } } from, err := chains.DecodeAddressFromChainID(inboundSenderChainID, inboundSender, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) @@ -89,9 +105,10 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo to, inboundAmount, inboundSenderChainID, - data, + message, inboundCoinType, cctx.InboundParams.Asset, + cctx.ProtocolContractVersion, ) if fungibletypes.IsContractReverted(evmTxResponse, err) || errShouldRevertCctx(err) { return true, err @@ -120,7 +137,7 @@ func (k Keeper) HandleEVMDeposit(ctx sdk.Context, cctx *types.CrossChainTx) (boo sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute("action", "DepositZRC20AndCallContract"), sdk.NewAttribute("contract", to.String()), - sdk.NewAttribute("data", hex.EncodeToString(data)), + sdk.NewAttribute("data", hex.EncodeToString(message)), sdk.NewAttribute("cctxIndex", cctx.Index), ), ) diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index 7541c21aa6..45e8a5547e 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -67,7 +67,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { fungibleMock.On("ZETADepositAndCallContract", ctx, ethcommon.HexToAddress(sender.String()), receiver, senderChainId, amount, mock.Anything, mock.Anything). Return(nil, errDeposit) - // call HandleEVMDeposit + // call HandleEVMDeposit cctx.InboundParams.Sender = sender.String() cctx.GetCurrentOutboundParam().Receiver = receiver.String() @@ -106,6 +106,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) // call HandleEVMDeposit @@ -151,6 +152,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{ Logs: []*evmtypes.Log{ { @@ -213,6 +215,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{ Logs: []*evmtypes.Log{ { @@ -302,6 +305,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, errDeposit) // call HandleEVMDeposit @@ -346,6 +350,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{VmError: "reverted"}, false, errDeposit) // call HandleEVMDeposit @@ -389,6 +394,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrForeignCoinCapReached) // call HandleEVMDeposit @@ -432,6 +438,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrPausedZRC20) // call HandleEVMDeposit @@ -475,6 +482,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { mock.Anything, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, fungibletypes.ErrCallNonContract) // call HandleEVMDeposit @@ -544,6 +552,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { data, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) cctx.GetCurrentOutboundParam().Receiver = sample.EthAddress().String() @@ -586,6 +595,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { data, coin.CoinType_ERC20, mock.Anything, + mock.Anything, ).Return(&evmtypes.MsgEthereumTxResponse{}, false, nil) cctx := sample.CrossChainTx(t, "foo") diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index 5df569a6d9..e19d664310 100644 --- a/x/crosschain/keeper/evm_hooks.go +++ b/x/crosschain/keeper/evm_hooks.go @@ -3,7 +3,6 @@ package keeper import ( "encoding/base64" "encoding/hex" - "errors" "fmt" "math/big" @@ -15,13 +14,15 @@ import ( "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" evmtypes "github.com/evmos/ethermint/x/evm/types" - connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" - zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/pkg/errors" + connectorzevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/crypto" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -64,10 +65,12 @@ func (k Keeper) PostTxProcessing( // - clear the logs // TODO: implement unit tests // https://github.com/zeta-chain/node/issues/1759 +// TODO: refactor and simplify +// https://github.com/zeta-chain/node/issues/2627 func (k Keeper) ProcessLogs( ctx sdk.Context, logs []*ethtypes.Log, - emittingContract ethcommon.Address, + emittingAddress ethcommon.Address, txOrigin string, ) error { system, found := k.fungibleKeeper.GetSystemContract(ctx) @@ -78,47 +81,71 @@ func (k Keeper) ProcessLogs( if connectorZEVMAddr == (ethcommon.Address{}) { return fmt.Errorf("connectorZEVM address is empty") } + gatewayAddr := ethcommon.HexToAddress(system.Gateway) + // read the logs and process inbounds from emitted events + // run the processing for the v1 and the v2 protocol contracts for _, log := range logs { - eventZrc20Withdrawal, errZrc20 := ParseZRC20WithdrawalEvent(*log) - eventZetaSent, errZetaSent := ParseZetaSentEvent(*log, connectorZEVMAddr) - if errZrc20 != nil && errZetaSent != nil { - // This log does not contain any of the two events - continue + if !crypto.IsEmptyAddress(gatewayAddr) { + if err := k.ProcessZEVMInboundV2(ctx, log, gatewayAddr, emittingAddress, txOrigin); err != nil { + return errors.Wrap(err, "failed to process ZEVM inbound V2") + } } - if eventZrc20Withdrawal != nil && eventZetaSent != nil { - // This log contains both events, this is not possible - ctx.Logger(). - Error(fmt.Sprintf("ProcessLogs: log contains both ZRC20Withdrawal and ZetaSent events, %s , %s", log.Topics, log.Data)) - continue + if err := k.ProcessZEVMInboundV1(ctx, log, connectorZEVMAddr, emittingAddress, txOrigin); err != nil { + return errors.Wrap(err, "failed to process ZEVM inbound V1") } + } - // if eventZrc20Withdrawal is not nil we will try to validate it and see if it can be processed - if eventZrc20Withdrawal != nil { - // Check if the contract is a registered ZRC20 contract. If its not a registered ZRC20 contract, we can discard this event as it is not relevant - coin, foundCoin := k.fungibleKeeper.GetForeignCoins(ctx, eventZrc20Withdrawal.Raw.Address.Hex()) - if !foundCoin { - ctx.Logger(). - Info(fmt.Sprintf("cannot find foreign coin with contract address %s", eventZrc20Withdrawal.Raw.Address.Hex())) - continue - } + return nil +} - // If Validation fails, we will not process the event and return and error. This condition means that the event was correct, and emitted from a registered ZRC20 contract - // But the information entered by the user is incorrect. In this case we can return an error and roll back the transaction - if err := k.ValidateZrc20WithdrawEvent(ctx, eventZrc20Withdrawal, coin.ForeignChainId); err != nil { - return err - } - // If the event is valid, we will process it and create a new CCTX - // If the process fails, we will return an error and roll back the transaction - if err := k.ProcessZRC20WithdrawalEvent(ctx, eventZrc20Withdrawal, emittingContract, txOrigin); err != nil { - return err - } +// ProcessZEVMInboundV1 processes the logs emitted by the zEVM contract for V1 protocol contracts +// it parses logs from Connector and ZRC20 contracts and processes them accordingly +func (k Keeper) ProcessZEVMInboundV1( + ctx sdk.Context, + log *ethtypes.Log, + connectorZEVMAddr, + emittingAddress ethcommon.Address, + txOrigin string, +) error { + eventZRC20Withdrawal, errZrc20 := ParseZRC20WithdrawalEvent(*log) + eventZETASent, errZetaSent := ParseZetaSentEvent(*log, connectorZEVMAddr) + if errZrc20 != nil && errZetaSent != nil { + // This log does not contain any of the two events + return nil + } + if eventZRC20Withdrawal != nil && eventZETASent != nil { + // This log contains both events, this is not possible + ctx.Logger(). + Error(fmt.Sprintf("ProcessLogs: log contains both ZRC20Withdrawal and ZetaSent events, %s , %s", log.Topics, log.Data)) + return nil + } + + // if eventZrc20Withdrawal is not nil we will try to validate it and see if it can be processed + if eventZRC20Withdrawal != nil { + // Check if the contract is a registered ZRC20 contract. If its not a registered ZRC20 contract, we can discard this event as it is not relevant + coin, foundCoin := k.fungibleKeeper.GetForeignCoins(ctx, eventZRC20Withdrawal.Raw.Address.Hex()) + if !foundCoin { + ctx.Logger(). + Info(fmt.Sprintf("cannot find foreign coin with contract address %s", eventZRC20Withdrawal.Raw.Address.Hex())) + return nil } - // if eventZetaSent is not nil we will try to validate it and see if it can be processed - if eventZetaSent != nil { - if err := k.ProcessZetaSentEvent(ctx, eventZetaSent, emittingContract, txOrigin); err != nil { - return err - } + + // If Validation fails, we will not process the event and return and error. This condition means that the event was correct, and emitted from a registered ZRC20 contract + // But the information entered by the user is incorrect. In this case we can return an error and roll back the transaction + if err := k.ValidateZrc20WithdrawEvent(ctx, eventZRC20Withdrawal, coin.ForeignChainId); err != nil { + return err + } + // If the event is valid, we will process it and create a new CCTX + // If the process fails, we will return an error and roll back the transaction + if err := k.ProcessZRC20WithdrawalEvent(ctx, eventZRC20Withdrawal, emittingAddress, txOrigin); err != nil { + return err + } + } + // if eventZetaSent is not nil we will try to validate it and see if it can be processed + if eventZETASent != nil { + if err := k.ProcessZetaSentEvent(ctx, eventZETASent, emittingAddress, txOrigin); err != nil { + return err } } return nil @@ -178,6 +205,7 @@ func (k Keeper) ProcessZRC20WithdrawalEvent( foreignCoin.CoinType, foreignCoin.Asset, event.Raw.Index, + types.ProtocolContractVersion_V1, ) cctx, err := k.ValidateInbound(ctx, msg, false) @@ -241,6 +269,7 @@ func (k Keeper) ProcessZetaSentEvent( amount := math.NewUintFromBigInt(event.ZetaValueAndGas) messageString := base64.StdEncoding.EncodeToString(event.Message) + // Bump gasLimit by event index (which is very unlikely to be larger than 1000) to always have different ZetaSent events msgs. msg := types.NewMsgVoteInbound( "", @@ -256,6 +285,7 @@ func (k Keeper) ProcessZetaSentEvent( coin.CoinType_Zeta, "", event.Raw.Index, + types.ProtocolContractVersion_V1, ) cctx, err := k.ValidateInbound(ctx, msg, true) @@ -275,36 +305,41 @@ func (k Keeper) ProcessZetaSentEvent( // It verifies event information for BTC chains and returns an error if the event is invalid func (k Keeper) ValidateZrc20WithdrawEvent(ctx sdk.Context, event *zrc20.ZRC20Withdrawal, chainID int64) error { // The event was parsed; that means the user has deposited tokens to the contract. + return k.validateZRC20Withdrawal(ctx, chainID, event.Value, event.To) +} +// validateZRC20Withdrawal validates the data of a ZRC20 Withdrawal event (version 1 or 2) +// it checks if the withdrawal amount is valid and the destination address is supported depending on the chain +func (k Keeper) validateZRC20Withdrawal(ctx sdk.Context, chainID int64, value *big.Int, to []byte) error { additionalChains := k.GetAuthorityKeeper().GetAdditionalChainList(ctx) if chains.IsBitcoinChain(chainID, additionalChains) { - if event.Value.Cmp(big.NewInt(constant.BTCWithdrawalDustAmount)) < 0 { + if value.Cmp(big.NewInt(constant.BTCWithdrawalDustAmount)) < 0 { return errorsmod.Wrapf( types.ErrInvalidWithdrawalAmount, "withdraw amount %s is less than dust amount %d", - event.Value.String(), + value.String(), constant.BTCWithdrawalDustAmount, ) } - addr, err := chains.DecodeBtcAddress(string(event.To), chainID) + addr, err := chains.DecodeBtcAddress(string(to), chainID) if err != nil { - return errorsmod.Wrapf(types.ErrInvalidAddress, "invalid address %s", string(event.To)) + return errorsmod.Wrapf(types.ErrInvalidAddress, "invalid address %s", string(to)) } if !chains.IsBtcAddressSupported(addr) { - return errorsmod.Wrapf(types.ErrInvalidAddress, "unsupported address %s", string(event.To)) + return errorsmod.Wrapf(types.ErrInvalidAddress, "unsupported address %s", string(to)) } } else if chains.IsSolanaChain(chainID, additionalChains) { - if event.Value.Cmp(big.NewInt(constant.SolanaWalletRentExempt)) < 0 { + if value.Cmp(big.NewInt(constant.SolanaWalletRentExempt)) < 0 { return errorsmod.Wrapf( types.ErrInvalidWithdrawalAmount, "withdraw amount %s is less than rent exempt %d", - event.Value.String(), + value.String(), constant.SolanaWalletRentExempt, ) } - _, err := chains.DecodeSolanaWalletAddress(string(event.To)) + _, err := chains.DecodeSolanaWalletAddress(string(to)) if err != nil { - return errorsmod.Wrapf(types.ErrInvalidAddress, "invalid address %s", string(event.To)) + return errorsmod.Wrapf(types.ErrInvalidAddress, "invalid address %s", string(to)) } } diff --git a/x/crosschain/keeper/gas_payment.go b/x/crosschain/keeper/gas_payment.go index a5a9b7d0fd..2a8331e059 100644 --- a/x/crosschain/keeper/gas_payment.go +++ b/x/crosschain/keeper/gas_payment.go @@ -169,11 +169,19 @@ func (k Keeper) PayGasInERC20AndUpdateCctx( if _, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, chainID); !found { return observertypes.ErrSupportedChains } + // get gas params gas, err := k.ChainGasParams(ctx, chainID) if err != nil { return cosmoserrors.Wrap(types.ErrCannotFindGasParams, err.Error()) } + + // with V2 protocol, reverts on connected chains can eventually call a onRevert function which can require a higher gas limit + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 && cctx.RevertOptions.CallOnRevert && + !cctx.RevertOptions.RevertGasLimit.IsZero() { + gas.GasLimit = cctx.RevertOptions.RevertGasLimit + } + outTxGasFee := gas.GasLimit.Mul(gas.GasPrice).Add(gas.ProtocolFlatFee) // get address of the zrc20 fc, found := k.fungibleKeeper.GetForeignCoinFromAsset(ctx, cctx.InboundParams.Asset, chainID) diff --git a/x/crosschain/keeper/grpc_query_inbound_hash_to_cctx_test.go b/x/crosschain/keeper/grpc_query_inbound_hash_to_cctx_test.go index f1b2862520..c99bd9d9fc 100644 --- a/x/crosschain/keeper/grpc_query_inbound_hash_to_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_inbound_hash_to_cctx_test.go @@ -135,6 +135,7 @@ func createInTxHashToCctxWithCctxs(keeper *crosschainkeeper.Keeper, ctx sdk.Cont cctxs[i].ZetaFees = math.OneUint() cctxs[i].InboundParams = &types.InboundParams{ObservedHash: fmt.Sprintf("%d", i), Amount: math.OneUint()} cctxs[i].CctxStatus = &types.Status{Status: types.CctxStatus_PendingInbound} + cctxs[i].RevertOptions = types.NewEmptyRevertOptions() keeper.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctxs[i]) } diff --git a/x/crosschain/keeper/initiate_outbound_test.go b/x/crosschain/keeper/initiate_outbound_test.go index f4f24a3760..05da1daa0e 100644 --- a/x/crosschain/keeper/initiate_outbound_test.go +++ b/x/crosschain/keeper/initiate_outbound_test.go @@ -109,10 +109,10 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( + require.Contains( t, - "chain not supported", cctx.CctxStatus.StatusMessage, + "chain not supported", ) }, ) @@ -149,10 +149,10 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( + require.Contains( t, - "GetRevertGasLimit: foreign coin not found for sender chain", cctx.CctxStatus.StatusMessage, + "GetRevertGasLimit: foreign coin not found for sender chain", ) }) @@ -192,10 +192,10 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( + require.Contains( t, - "chain not supported", cctx.CctxStatus.StatusMessage, + "chain not supported", ) }, ) @@ -237,10 +237,10 @@ func TestKeeper_InitiateOutboundZEVMDeposit(t *testing.T) { require.NoError(t, err) require.Equal(t, types.CctxStatus_Aborted, cctx.CctxStatus.Status) require.Equal(t, types.CctxStatus_Aborted, newStatus) - require.Equal( + require.Contains( t, - "chain not supported", cctx.CctxStatus.StatusMessage, + "chain not supported", ) }, ) diff --git a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go index 91ef323ad9..5e380acc81 100644 --- a/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go +++ b/x/crosschain/keeper/msg_server_refund_aborted_tx_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "github.com/zeta-chain/zetacore/pkg/constant" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -449,7 +450,7 @@ func TestMsgServer_RefundAbortedCCTX(t *testing.T) { msg := crosschaintypes.MsgRefundAbortedCCTX{ Creator: admin, CctxIndex: cctx.Index, - RefundAddress: "0x0000000000000000000000000000000000000000", + RefundAddress: constant.EVMZeroAddress, } keepertest.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) _, err := msgServer.RefundAbortedCCTX(ctx, &msg) diff --git a/x/crosschain/keeper/utils_test.go b/x/crosschain/keeper/utils_test.go index ce5aa82447..2c4126b635 100644 --- a/x/crosschain/keeper/utils_test.go +++ b/x/crosschain/keeper/utils_test.go @@ -2,6 +2,9 @@ package keeper_test import ( + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + "github.com/zeta-chain/zetacore/pkg/contracts/erc1967proxy" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" "math/big" "testing" @@ -10,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" evmkeeper "github.com/evmos/ethermint/x/evm/keeper" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" "github.com/zeta-chain/zetacore/cmd/zetacored/config" "github.com/zeta-chain/zetacore/pkg/chains" @@ -67,6 +70,44 @@ func assertContractDeployment(t *testing.T, k *evmkeeper.Keeper, ctx sdk.Context require.NotEmpty(t, code) } +// deploy upgradable gateway contract and return its address +func deployGatewayContract( + t *testing.T, + ctx sdk.Context, + k *fungiblekeeper.Keeper, + evmk *evmkeeper.Keeper, + wzeta, admin common.Address, +) common.Address { + // Deploy the gateway contract + implAddr, err := k.DeployContract(ctx, gatewayzevm.GatewayZEVMMetaData) + require.NoError(t, err) + require.NotEmpty(t, implAddr) + assertContractDeployment(t, evmk, ctx, implAddr) + + // Deploy the proxy contract + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + require.NoError(t, err) + + // Encode the initializer data + initializerData, err := gatewayABI.Pack("initialize", wzeta, admin) + require.NoError(t, err) + + gatewayContract, err := k.DeployContract(ctx, erc1967proxy.ERC1967ProxyMetaData, implAddr, initializerData) + require.NoError(t, err) + require.NotEmpty(t, gatewayContract) + assertContractDeployment(t, evmk, ctx, gatewayContract) + + // store the gateway in the system contract object + sys, found := k.GetSystemContract(ctx) + if !found { + sys = fungibletypes.SystemContract{} + } + sys.Gateway = gatewayContract.Hex() + k.SetSystemContract(ctx, sys) + + return gatewayContract +} + // deploySystemContracts deploys the system contracts and returns their addresses. func deploySystemContracts( t *testing.T, @@ -101,6 +142,10 @@ func deploySystemContracts( require.NotEmpty(t, systemContract) assertContractDeployment(t, evmk, ctx, systemContract) + // deploy the gateway contract + contract := deployGatewayContract(t, ctx, k, evmk, wzeta, sample.EthAddress()) + require.NotEmpty(t, contract) + return } diff --git a/x/crosschain/keeper/v2_zevm_inbound.go b/x/crosschain/keeper/v2_zevm_inbound.go new file mode 100644 index 0000000000..158a8f0f40 --- /dev/null +++ b/x/crosschain/keeper/v2_zevm_inbound.go @@ -0,0 +1,278 @@ +package keeper + +import ( + "encoding/hex" + "fmt" + "math/big" + + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/x/crosschain/types" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" +) + +// ProcessZEVMInboundV2 processes the logs emitted by the zEVM contract for V2 protocol contracts +// it parses logs from GatewayZEVM contract and updates the crosschain module state +func (k Keeper) ProcessZEVMInboundV2( + ctx sdk.Context, + log *ethtypes.Log, + gatewayAddr, + from ethcommon.Address, + txOrigin string, +) error { + // try to parse a withdrawal event from the log + withdrawalEvent, gatewayEvent, err := k.parseGatewayEvent(*log, gatewayAddr) + if err == nil && (withdrawalEvent != nil || gatewayEvent != nil) { + var inbound *types.MsgVoteInbound + + // parse data from event and validate + var zrc20 ethcommon.Address + var value *big.Int + var receiver []byte + var contractAddress ethcommon.Address + if withdrawalEvent != nil { + zrc20 = withdrawalEvent.Zrc20 + value = withdrawalEvent.Value + receiver = withdrawalEvent.Receiver + contractAddress = withdrawalEvent.Raw.Address + } else { + zrc20 = gatewayEvent.Zrc20 + value = big.NewInt(0) + receiver = gatewayEvent.Receiver + contractAddress = gatewayEvent.Raw.Address + } + + k.Logger(ctx).Error(fmt.Sprintf("processing inbound. zrc20: %s", zrc20.Hex())) + + foreignCoin, found := k.fungibleKeeper.GetForeignCoins(ctx, zrc20.Hex()) + if !found { + ctx.Logger(). + Info(fmt.Sprintf("cannot find foreign coin with contract address %s", contractAddress.Hex())) + return nil + } + + // validate data of the withdrawal event + if err := k.validateZRC20Withdrawal(ctx, foreignCoin.ForeignChainId, value, receiver); err != nil { + return err + } + + // create inbound object depending on the event type + if withdrawalEvent != nil { + inbound, err = k.newWithdrawalInbound(ctx, from, txOrigin, foreignCoin, withdrawalEvent) + if err != nil { + return err + } + } else { + inbound, err = k.newCallInbound(ctx, from, txOrigin, foreignCoin, gatewayEvent) + if err != nil { + return err + } + } + + if inbound == nil { + return errors.New("ParseGatewayEvent: invalid log - no event found") + } + + // validate inbound for processing + cctx, err := k.ValidateInbound(ctx, inbound, false) + if err != nil { + return err + } + if cctx.CctxStatus.Status == types.CctxStatus_Aborted { + return errors.New("cctx aborted") + } + + EmitZRCWithdrawCreated(ctx, *cctx) + } + + return nil +} + +// parseGatewayEvent parses the event from the gateway contract +func (k Keeper) parseGatewayEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, +) (*gatewayzevm.GatewayZEVMWithdrawn, *gatewayzevm.GatewayZEVMCalled, error) { + if len(log.Topics) == 0 { + return nil, nil, errors.New("ParseGatewayCallEvent: invalid log - no topics") + } + filterer, err := gatewayzevm.NewGatewayZEVMFilterer(log.Address, bind.ContractFilterer(nil)) + if err != nil { + return nil, nil, err + } + withdrawalEvent, err := k.parseGatewayWithdrawalEvent(log, gatewayAddr, filterer) + if err == nil { + return withdrawalEvent, nil, nil + } + callEvent, err := k.parseGatewayCallEvent(log, gatewayAddr, filterer) + if err == nil { + return nil, callEvent, nil + } + return nil, nil, errors.New("ParseGatewayEvent: invalid log - no event found") +} + +// parseGatewayWithdrawalEvent parses the GatewayZEVMWithdrawal event from the log +func (k Keeper) parseGatewayWithdrawalEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, + filterer *gatewayzevm.GatewayZEVMFilterer, +) (*gatewayzevm.GatewayZEVMWithdrawn, error) { + event, err := filterer.ParseWithdrawn(log) + if err != nil { + return nil, err + } + if event.Raw.Address != gatewayAddr { + return nil, errors.New("ParseGatewayWithdrawalEvent: invalid log - wrong contract address") + } + return event, nil +} + +// parseGatewayCallEvent parses the GatewayZEVMCall event from the log +func (k Keeper) parseGatewayCallEvent( + log ethtypes.Log, + gatewayAddr ethcommon.Address, + filterer *gatewayzevm.GatewayZEVMFilterer, +) (*gatewayzevm.GatewayZEVMCalled, error) { + event, err := filterer.ParseCalled(log) + if err != nil { + return nil, err + } + if event.Raw.Address != gatewayAddr { + return nil, errors.New("ParseGatewayCallEvent: invalid log - wrong contract address") + } + return event, nil +} + +// newWithdrawalInbound creates a new inbound object for a withdrawal +// currently inbound data is represented with a MsgVoteInbound message +// TODO: replace with a more appropriate object +// https://github.com/zeta-chain/node/issues/2658 +func (k Keeper) newWithdrawalInbound( + ctx sdk.Context, + from ethcommon.Address, + txOrigin string, + foreignCoin fungibletypes.ForeignCoins, + event *gatewayzevm.GatewayZEVMWithdrawn, +) (*types.MsgVoteInbound, error) { + receiverChain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, foreignCoin.ForeignChainId) + if !found { + return nil, errorsmod.Wrapf( + observertypes.ErrSupportedChains, + "chain with chainID %d not supported", + foreignCoin.ForeignChainId, + ) + } + + senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) + } + + toAddr, err := receiverChain.EncodeAddress(event.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) + } + + gasLimit := event.GasLimit.Uint64() + if gasLimit == 0 { + gasLimitQueried, err := k.fungibleKeeper.QueryGasLimit( + ctx, + ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress), + ) + if err != nil { + return nil, errors.Wrap(err, "cannot query gas limit") + } + gasLimit = gasLimitQueried.Uint64() + } + + return types.NewMsgVoteInbound( + "", + from.Hex(), + senderChain.ChainId, + txOrigin, + toAddr, + foreignCoin.ForeignChainId, + math.NewUintFromBigInt(event.Value), + hex.EncodeToString(event.Message), + event.Raw.TxHash.String(), + event.Raw.BlockNumber, + gasLimit, + foreignCoin.CoinType, + foreignCoin.Asset, + event.Raw.Index, + types.ProtocolContractVersion_V2, + types.WithZEVMRevertOptions(event.RevertOptions), + ), nil +} + +// newCallInbound creates a new inbound object for a call +// currently inbound data is represented with a MsgVoteInbound message +// TODO: replace with a more appropriate object +// https://github.com/zeta-chain/node/issues/2658 +func (k Keeper) newCallInbound( + ctx sdk.Context, + from ethcommon.Address, + txOrigin string, + foreignCoin fungibletypes.ForeignCoins, + event *gatewayzevm.GatewayZEVMCalled, +) (*types.MsgVoteInbound, error) { + receiverChain, found := k.zetaObserverKeeper.GetSupportedChainFromChainID(ctx, foreignCoin.ForeignChainId) + if !found { + return nil, errorsmod.Wrapf( + observertypes.ErrSupportedChains, + "chain with chainID %d not supported", + foreignCoin.ForeignChainId, + ) + } + + senderChain, err := chains.ZetaChainFromCosmosChainID(ctx.ChainID()) + if err != nil { + return nil, errors.Wrapf(err, "ProcessZEVMInboundV2: failed to convert chainID %s", ctx.ChainID()) + } + + toAddr, err := receiverChain.EncodeAddress(event.Receiver) + if err != nil { + return nil, errors.Wrapf(err, "cannot encode address %v", event.Receiver) + } + + gasLimit := event.GasLimit.Uint64() + if gasLimit == 0 { + gasLimitQueried, err := k.fungibleKeeper.QueryGasLimit( + ctx, + ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress), + ) + if err != nil { + return nil, errors.Wrap(err, "cannot query gas limit") + } + gasLimit = gasLimitQueried.Uint64() + } + + return types.NewMsgVoteInbound( + "", + from.Hex(), + senderChain.ChainId, + txOrigin, + toAddr, + foreignCoin.ForeignChainId, + math.ZeroUint(), + hex.EncodeToString(event.Message), + event.Raw.TxHash.String(), + event.Raw.BlockNumber, + gasLimit, + coin.CoinType_NoAssetCall, + "", + event.Raw.Index, + types.ProtocolContractVersion_V2, + types.WithZEVMRevertOptions(event.RevertOptions), + ), nil +} diff --git a/x/crosschain/types/cctx.go b/x/crosschain/types/cctx.go index debd1656d6..d7a77e259c 100644 --- a/x/crosschain/types/cctx.go +++ b/x/crosschain/types/cctx.go @@ -8,10 +8,33 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +// GetEVMRevertAddress returns the EVM revert address +// If a revert address is specified in the revert options, it returns the address +// Otherwise returns sender address +func (m CrossChainTx) GetEVMRevertAddress() ethcommon.Address { + addr, valid := m.RevertOptions.GetEVMRevertAddress() + if valid { + return addr + } + return ethcommon.HexToAddress(m.InboundParams.Sender) +} + +// GetEVMAbortAddress returns the EVM abort address +// If an abort address is specified in the revert options, it returns the address +// Otherwise returns sender address +func (m CrossChainTx) GetEVMAbortAddress() ethcommon.Address { + addr, valid := m.RevertOptions.GetEVMAbortAddress() + if valid { + return addr + } + return ethcommon.HexToAddress(m.InboundParams.Sender) +} + // GetCurrentOutboundParam returns the current outbound params. // There can only be one active outbound. // OutboundParams[0] is the original outbound, if it reverts, then @@ -82,8 +105,19 @@ func (m *CrossChainTx) AddRevertOutbound(gasLimit uint64) error { return fmt.Errorf("cannot revert before trying to process an outbound tx") } + // in protocol contract V2, developers can specify a specific address to receive the revert + // if not specified, the sender address is used + // note: this option is current only support for EVM type chains + revertReceiver := m.InboundParams.Sender + if m.ProtocolContractVersion == ProtocolContractVersion_V2 { + revertAddress, valid := m.RevertOptions.GetEVMRevertAddress() + if valid { + revertReceiver = revertAddress.Hex() + } + } + revertTxParams := &OutboundParams{ - Receiver: m.InboundParams.Sender, + Receiver: revertReceiver, ReceiverChainId: m.InboundParams.SenderChainId, Amount: m.GetCurrentOutboundParam().Amount, GasLimit: gasLimit, @@ -168,6 +202,7 @@ func (m CrossChainTx) SetOutboundBallotIndex(index string) { m.GetCurrentOutboundParam().BallotIndex = index } +// GetCctxIndexFromBytes returns the CCTX index from a byte array. func GetCctxIndexFromBytes(sendHash [32]byte) string { return fmt.Sprintf("0x%s", hex.EncodeToString(sendHash[:])) } @@ -215,13 +250,15 @@ func NewCCTX(ctx sdk.Context, msg MsgVoteInbound, tssPubkey string) (CrossChainT IsAbortRefunded: false, } cctx := CrossChainTx{ - Creator: msg.Creator, - Index: index, - ZetaFees: sdkmath.ZeroUint(), - RelayedMessage: msg.Message, - CctxStatus: status, - InboundParams: inboundParams, - OutboundParams: []*OutboundParams{outboundParams}, + Creator: msg.Creator, + Index: index, + ZetaFees: sdkmath.ZeroUint(), + RelayedMessage: msg.Message, + CctxStatus: status, + InboundParams: inboundParams, + OutboundParams: []*OutboundParams{outboundParams}, + ProtocolContractVersion: msg.ProtocolContractVersion, + RevertOptions: msg.RevertOptions, } // TODO: remove this validate call diff --git a/x/crosschain/types/cctx_test.go b/x/crosschain/types/cctx_test.go index 6a8a460335..1b706d0feb 100644 --- a/x/crosschain/types/cctx_test.go +++ b/x/crosschain/types/cctx_test.go @@ -16,12 +16,46 @@ import ( observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) +func TestCrossChainTx_GetEVMRevertAddress(t *testing.T) { + t.Run("use revert address if revert options", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "sample") + addr := sample.EthAddress() + cctx.RevertOptions.RevertAddress = addr.Hex() + require.EqualValues(t, addr, cctx.GetEVMRevertAddress()) + }) + + t.Run("use sender address if no revert options", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "sample") + addr := sample.EthAddress() + cctx.InboundParams.Sender = addr.Hex() + require.EqualValues(t, addr, cctx.GetEVMRevertAddress()) + }) + +} + +func TestCrossChainTx_GetEVMAbortAddress(t *testing.T) { + t.Run("use revert address if abort options", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "sample") + addr := sample.EthAddress() + cctx.RevertOptions.AbortAddress = addr.Hex() + require.EqualValues(t, addr, cctx.GetEVMAbortAddress()) + }) + + t.Run("use sender address if no abort options", func(t *testing.T) { + cctx := sample.CrossChainTx(t, "sample") + addr := sample.EthAddress() + cctx.InboundParams.Sender = addr.Hex() + require.EqualValues(t, addr, cctx.GetEVMAbortAddress()) + }) +} + func TestCrossChainTx_SetOutboundBallot(t *testing.T) { cctx := sample.CrossChainTx(t, "test") ballotIndex := sample.ZetaIndex(t) cctx.SetOutboundBallotIndex(ballotIndex) require.Equal(t, ballotIndex, cctx.GetCurrentOutboundParam().BallotIndex) } + func TestCrossChainTx_GetCCTXIndexBytes(t *testing.T) { cctx := sample.CrossChainTx(t, "sample") indexBytes, err := cctx.GetCCTXIndexBytes() @@ -29,7 +63,7 @@ func TestCrossChainTx_GetCCTXIndexBytes(t *testing.T) { require.Equal(t, cctx.Index, types.GetCctxIndexFromBytes(indexBytes)) } -func Test_InitializeCCTX(t *testing.T) { +func Test_NewCCTX(t *testing.T) { t.Run("should return a cctx with correct values", func(t *testing.T) { _, ctx, _, _ := keepertest.CrosschainKeeper(t) senderChain := chains.Goerli @@ -47,20 +81,21 @@ func Test_InitializeCCTX(t *testing.T) { cointType := coin.CoinType_ERC20 tss := sample.Tss() msg := types.MsgVoteInbound{ - Creator: creator, - Sender: sender.String(), - SenderChainId: senderChain.ChainId, - Receiver: receiver.String(), - ReceiverChain: receiverChain.ChainId, - Amount: amount, - Message: message, - InboundHash: inboundHash.String(), - InboundBlockHeight: inboundBlockHeight, - GasLimit: gasLimit, - CoinType: cointType, - TxOrigin: sender.String(), - Asset: asset, - EventIndex: eventIndex, + Creator: creator, + Sender: sender.String(), + SenderChainId: senderChain.ChainId, + Receiver: receiver.String(), + ReceiverChain: receiverChain.ChainId, + Amount: amount, + Message: message, + InboundHash: inboundHash.String(), + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: cointType, + TxOrigin: sender.String(), + Asset: asset, + EventIndex: eventIndex, + ProtocolContractVersion: types.ProtocolContractVersion_V2, } cctx, err := types.NewCCTX(ctx, msg, tss.TssPubkey) require.NoError(t, err) @@ -79,7 +114,9 @@ func Test_InitializeCCTX(t *testing.T) { require.Equal(t, sdkmath.ZeroUint(), cctx.GetCurrentOutboundParam().Amount) require.Equal(t, types.CctxStatus_PendingInbound, cctx.CctxStatus.Status) require.Equal(t, false, cctx.CctxStatus.IsAbortRefunded) + require.Equal(t, types.ProtocolContractVersion_V2, cctx.ProtocolContractVersion) }) + t.Run("should return an error if the cctx is invalid", func(t *testing.T) { _, ctx, _, _ := keepertest.CrosschainKeeper(t) senderChain := chains.Goerli @@ -115,6 +152,11 @@ func Test_InitializeCCTX(t *testing.T) { _, err := types.NewCCTX(ctx, msg, tss.TssPubkey) require.ErrorContains(t, err, "sender cannot be empty") }) + + t.Run("zero value for protocol contract version gives V1", func(t *testing.T) { + cctx := types.CrossChainTx{} + require.Equal(t, types.ProtocolContractVersion_V1, cctx.ProtocolContractVersion) + }) } func TestCrossChainTx_Validate(t *testing.T) { diff --git a/x/crosschain/types/cross_chain_tx.pb.go b/x/crosschain/types/cross_chain_tx.pb.go index 0f33b7e929..526efa053e 100644 --- a/x/crosschain/types/cross_chain_tx.pb.go +++ b/x/crosschain/types/cross_chain_tx.pb.go @@ -90,6 +90,33 @@ func (TxFinalizationStatus) EnumDescriptor() ([]byte, []int) { return fileDescriptor_d4c1966807fb5cb2, []int{1} } +// ProtocolContractVersion represents the version of the protocol contract used +// for cctx workflow +type ProtocolContractVersion int32 + +const ( + ProtocolContractVersion_V1 ProtocolContractVersion = 0 + ProtocolContractVersion_V2 ProtocolContractVersion = 1 +) + +var ProtocolContractVersion_name = map[int32]string{ + 0: "V1", + 1: "V2", +} + +var ProtocolContractVersion_value = map[string]int32{ + "V1": 0, + "V2": 1, +} + +func (x ProtocolContractVersion) String() string { + return proto.EnumName(ProtocolContractVersion_name, int32(x)) +} + +func (ProtocolContractVersion) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_d4c1966807fb5cb2, []int{2} +} + type InboundParams struct { Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // the Connector.send() @@ -476,21 +503,93 @@ func (m *Status) GetCreatedTimestamp() int64 { return 0 } +// RevertOptions represents the options for reverting a cctx +type RevertOptions struct { + RevertAddress string `protobuf:"bytes,1,opt,name=revert_address,json=revertAddress,proto3" json:"revert_address,omitempty"` + CallOnRevert bool `protobuf:"varint,2,opt,name=call_on_revert,json=callOnRevert,proto3" json:"call_on_revert,omitempty"` + AbortAddress string `protobuf:"bytes,3,opt,name=abort_address,json=abortAddress,proto3" json:"abort_address,omitempty"` + RevertMessage []byte `protobuf:"bytes,4,opt,name=revert_message,json=revertMessage,proto3" json:"revert_message,omitempty"` + RevertGasLimit github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,5,opt,name=revert_gas_limit,json=revertGasLimit,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"revert_gas_limit"` +} + +func (m *RevertOptions) Reset() { *m = RevertOptions{} } +func (m *RevertOptions) String() string { return proto.CompactTextString(m) } +func (*RevertOptions) ProtoMessage() {} +func (*RevertOptions) Descriptor() ([]byte, []int) { + return fileDescriptor_d4c1966807fb5cb2, []int{4} +} +func (m *RevertOptions) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RevertOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RevertOptions.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RevertOptions) XXX_Merge(src proto.Message) { + xxx_messageInfo_RevertOptions.Merge(m, src) +} +func (m *RevertOptions) XXX_Size() int { + return m.Size() +} +func (m *RevertOptions) XXX_DiscardUnknown() { + xxx_messageInfo_RevertOptions.DiscardUnknown(m) +} + +var xxx_messageInfo_RevertOptions proto.InternalMessageInfo + +func (m *RevertOptions) GetRevertAddress() string { + if m != nil { + return m.RevertAddress + } + return "" +} + +func (m *RevertOptions) GetCallOnRevert() bool { + if m != nil { + return m.CallOnRevert + } + return false +} + +func (m *RevertOptions) GetAbortAddress() string { + if m != nil { + return m.AbortAddress + } + return "" +} + +func (m *RevertOptions) GetRevertMessage() []byte { + if m != nil { + return m.RevertMessage + } + return nil +} + type CrossChainTx struct { - Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` - Index string `protobuf:"bytes,2,opt,name=index,proto3" json:"index,omitempty"` - ZetaFees github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,5,opt,name=zeta_fees,json=zetaFees,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"zeta_fees" yaml:"zeta_fees"` - RelayedMessage string `protobuf:"bytes,6,opt,name=relayed_message,json=relayedMessage,proto3" json:"relayed_message,omitempty"` - CctxStatus *Status `protobuf:"bytes,8,opt,name=cctx_status,json=cctxStatus,proto3" json:"cctx_status,omitempty"` - InboundParams *InboundParams `protobuf:"bytes,9,opt,name=inbound_params,json=inboundParams,proto3" json:"inbound_params,omitempty"` - OutboundParams []*OutboundParams `protobuf:"bytes,10,rep,name=outbound_params,json=outboundParams,proto3" json:"outbound_params,omitempty"` + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + Index string `protobuf:"bytes,2,opt,name=index,proto3" json:"index,omitempty"` + ZetaFees github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,5,opt,name=zeta_fees,json=zetaFees,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"zeta_fees" yaml:"zeta_fees"` + RelayedMessage string `protobuf:"bytes,6,opt,name=relayed_message,json=relayedMessage,proto3" json:"relayed_message,omitempty"` + CctxStatus *Status `protobuf:"bytes,8,opt,name=cctx_status,json=cctxStatus,proto3" json:"cctx_status,omitempty"` + InboundParams *InboundParams `protobuf:"bytes,9,opt,name=inbound_params,json=inboundParams,proto3" json:"inbound_params,omitempty"` + OutboundParams []*OutboundParams `protobuf:"bytes,10,rep,name=outbound_params,json=outboundParams,proto3" json:"outbound_params,omitempty"` + ProtocolContractVersion ProtocolContractVersion `protobuf:"varint,11,opt,name=protocol_contract_version,json=protocolContractVersion,proto3,enum=zetachain.zetacore.crosschain.ProtocolContractVersion" json:"protocol_contract_version,omitempty"` + RevertOptions RevertOptions `protobuf:"bytes,12,opt,name=revert_options,json=revertOptions,proto3" json:"revert_options"` } func (m *CrossChainTx) Reset() { *m = CrossChainTx{} } func (m *CrossChainTx) String() string { return proto.CompactTextString(m) } func (*CrossChainTx) ProtoMessage() {} func (*CrossChainTx) Descriptor() ([]byte, []int) { - return fileDescriptor_d4c1966807fb5cb2, []int{4} + return fileDescriptor_d4c1966807fb5cb2, []int{5} } func (m *CrossChainTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -561,13 +660,29 @@ func (m *CrossChainTx) GetOutboundParams() []*OutboundParams { return nil } +func (m *CrossChainTx) GetProtocolContractVersion() ProtocolContractVersion { + if m != nil { + return m.ProtocolContractVersion + } + return ProtocolContractVersion_V1 +} + +func (m *CrossChainTx) GetRevertOptions() RevertOptions { + if m != nil { + return m.RevertOptions + } + return RevertOptions{} +} + func init() { proto.RegisterEnum("zetachain.zetacore.crosschain.CctxStatus", CctxStatus_name, CctxStatus_value) proto.RegisterEnum("zetachain.zetacore.crosschain.TxFinalizationStatus", TxFinalizationStatus_name, TxFinalizationStatus_value) + proto.RegisterEnum("zetachain.zetacore.crosschain.ProtocolContractVersion", ProtocolContractVersion_name, ProtocolContractVersion_value) proto.RegisterType((*InboundParams)(nil), "zetachain.zetacore.crosschain.InboundParams") proto.RegisterType((*ZetaAccounting)(nil), "zetachain.zetacore.crosschain.ZetaAccounting") proto.RegisterType((*OutboundParams)(nil), "zetachain.zetacore.crosschain.OutboundParams") proto.RegisterType((*Status)(nil), "zetachain.zetacore.crosschain.Status") + proto.RegisterType((*RevertOptions)(nil), "zetachain.zetacore.crosschain.RevertOptions") proto.RegisterType((*CrossChainTx)(nil), "zetachain.zetacore.crosschain.CrossChainTx") } @@ -576,77 +691,88 @@ func init() { } var fileDescriptor_d4c1966807fb5cb2 = []byte{ - // 1119 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x72, 0x13, 0xc7, - 0x13, 0xd6, 0x22, 0x59, 0x96, 0x5a, 0xff, 0x96, 0xb1, 0xf0, 0x6f, 0x7f, 0x4e, 0x21, 0x1c, 0xa5, - 0x00, 0x41, 0x82, 0x54, 0x98, 0x4b, 0x2a, 0x37, 0xdb, 0x85, 0xc1, 0x21, 0x80, 0x6b, 0x31, 0x39, - 0x70, 0xc8, 0x66, 0xb4, 0xdb, 0x5a, 0x4d, 0x59, 0xda, 0x51, 0x76, 0x46, 0xae, 0x15, 0x95, 0x87, - 0x48, 0xde, 0x21, 0x87, 0x1c, 0xf3, 0x04, 0x39, 0x73, 0xe4, 0x98, 0xca, 0x81, 0x4a, 0xc1, 0x1b, - 0xe4, 0x9c, 0x43, 0x6a, 0x66, 0x76, 0x25, 0xcb, 0xe5, 0xb2, 0x09, 0xc9, 0x69, 0xbb, 0xbf, 0x9e, - 0xf9, 0x7a, 0xb6, 0xe7, 0xeb, 0x99, 0x81, 0xad, 0x97, 0x28, 0xa9, 0x3f, 0xa4, 0x2c, 0xea, 0x69, - 0x8b, 0xc7, 0xd8, 0xf3, 0x63, 0x2e, 0x84, 0xc1, 0xb4, 0xe9, 0x69, 0xdb, 0x93, 0x49, 0x77, 0x12, - 0x73, 0xc9, 0xc9, 0xd5, 0xf9, 0x9c, 0x6e, 0x36, 0xa7, 0xbb, 0x98, 0xb3, 0xd1, 0x0c, 0x79, 0xc8, - 0xf5, 0xc8, 0x9e, 0xb2, 0xcc, 0xa4, 0x8d, 0x1b, 0x67, 0x24, 0x9a, 0x1c, 0x85, 0x3d, 0x9f, 0xab, - 0x34, 0x9c, 0x45, 0x66, 0x5c, 0xfb, 0x97, 0x02, 0xd4, 0xf6, 0xa3, 0x3e, 0x9f, 0x46, 0xc1, 0x01, - 0x8d, 0xe9, 0x58, 0x90, 0x75, 0x28, 0x0a, 0x8c, 0x02, 0x8c, 0x1d, 0x6b, 0xd3, 0xea, 0x94, 0xdd, - 0xd4, 0x23, 0x37, 0xa0, 0x61, 0xac, 0x74, 0x7d, 0x2c, 0x70, 0x2e, 0x6d, 0x5a, 0x9d, 0xbc, 0x5b, - 0x33, 0xf0, 0xae, 0x42, 0xf7, 0x03, 0xf2, 0x11, 0x94, 0x65, 0xe2, 0xf1, 0x98, 0x85, 0x2c, 0x72, - 0xf2, 0x9a, 0xa2, 0x24, 0x93, 0xa7, 0xda, 0x27, 0x3b, 0x50, 0x56, 0xc9, 0x3d, 0x39, 0x9b, 0xa0, - 0x53, 0xd8, 0xb4, 0x3a, 0xf5, 0xad, 0xeb, 0xdd, 0x33, 0xfe, 0x6f, 0x72, 0x14, 0x76, 0xf5, 0x2a, - 0x77, 0x39, 0x8b, 0x0e, 0x67, 0x13, 0x74, 0x4b, 0x7e, 0x6a, 0x91, 0x26, 0xac, 0x50, 0x21, 0x50, - 0x3a, 0x2b, 0x9a, 0xdc, 0x38, 0xe4, 0x01, 0x14, 0xe9, 0x98, 0x4f, 0x23, 0xe9, 0x14, 0x15, 0xbc, - 0xd3, 0x7b, 0xf5, 0xe6, 0x5a, 0xee, 0xf7, 0x37, 0xd7, 0x6e, 0x86, 0x4c, 0x0e, 0xa7, 0xfd, 0xae, - 0xcf, 0xc7, 0x3d, 0x9f, 0x8b, 0x31, 0x17, 0xe9, 0xe7, 0x8e, 0x08, 0x8e, 0x7a, 0x6a, 0x1d, 0xa2, - 0xfb, 0x9c, 0x45, 0xd2, 0x4d, 0xa7, 0x93, 0x4f, 0xa0, 0xc6, 0xfb, 0x02, 0xe3, 0x63, 0x0c, 0xbc, - 0x21, 0x15, 0x43, 0x67, 0x55, 0xa7, 0xa9, 0x66, 0xe0, 0x43, 0x2a, 0x86, 0xe4, 0x73, 0x70, 0xe6, - 0x83, 0x30, 0x91, 0x18, 0x47, 0x74, 0xe4, 0x0d, 0x91, 0x85, 0x43, 0xe9, 0x94, 0x36, 0xad, 0x4e, - 0xc1, 0x5d, 0xcf, 0xe2, 0xf7, 0xd3, 0xf0, 0x43, 0x1d, 0x25, 0x1f, 0x43, 0xb5, 0x4f, 0x47, 0x23, - 0x2e, 0x3d, 0x16, 0x05, 0x98, 0x38, 0x65, 0xcd, 0x5e, 0x31, 0xd8, 0xbe, 0x82, 0xc8, 0x16, 0x5c, - 0x19, 0xb0, 0x88, 0x8e, 0xd8, 0x4b, 0x0c, 0x3c, 0x55, 0x92, 0x8c, 0x19, 0x34, 0xf3, 0xda, 0x3c, - 0xf8, 0x02, 0x25, 0x4d, 0x69, 0x19, 0xac, 0xcb, 0xc4, 0x4b, 0x23, 0x54, 0x32, 0x1e, 0x79, 0x42, - 0x52, 0x39, 0x15, 0x4e, 0x45, 0x57, 0xf9, 0x5e, 0xf7, 0x5c, 0x15, 0x75, 0x0f, 0x93, 0xbd, 0x13, - 0x73, 0x9f, 0xe9, 0xa9, 0x6e, 0x53, 0x9e, 0x81, 0xb6, 0xbf, 0x83, 0xba, 0x4a, 0xbc, 0xed, 0xfb, - 0xaa, 0x5e, 0x2c, 0x0a, 0x89, 0x07, 0x6b, 0xb4, 0xcf, 0x63, 0x99, 0x2d, 0x37, 0xdd, 0x08, 0xeb, - 0xc3, 0x36, 0xe2, 0x72, 0xca, 0xa5, 0x93, 0x68, 0xa6, 0xf6, 0x8f, 0x45, 0xa8, 0x3f, 0x9d, 0xca, - 0x93, 0x32, 0xdd, 0x80, 0x52, 0x8c, 0x3e, 0xb2, 0xe3, 0xb9, 0x50, 0xe7, 0x3e, 0xb9, 0x05, 0x76, - 0x66, 0x1b, 0xb1, 0xee, 0x67, 0x5a, 0x6d, 0x64, 0x78, 0xa6, 0xd6, 0x25, 0x41, 0xe6, 0x3f, 0x4c, - 0x90, 0x0b, 0xe9, 0x15, 0xfe, 0x9d, 0xf4, 0x54, 0xeb, 0x08, 0xe1, 0x45, 0x3c, 0xf2, 0x51, 0xab, - 0xbb, 0xe0, 0x96, 0xa4, 0x10, 0x4f, 0x94, 0xaf, 0x82, 0x21, 0x15, 0xde, 0x88, 0x8d, 0x99, 0xd1, - 0x78, 0xc1, 0x2d, 0x85, 0x54, 0x7c, 0xa5, 0xfc, 0x2c, 0x38, 0x89, 0x99, 0x8f, 0xa9, 0x60, 0x55, - 0xf0, 0x40, 0xf9, 0xa4, 0x03, 0x76, 0x1a, 0xe4, 0x31, 0x93, 0x33, 0x6f, 0x80, 0xe8, 0xfc, 0x4f, - 0x8f, 0xa9, 0x9b, 0x31, 0x1a, 0xde, 0x43, 0x24, 0x04, 0x0a, 0x5a, 0xf2, 0x25, 0x1d, 0xd5, 0xf6, - 0xfb, 0x08, 0xf6, 0xbc, 0x6e, 0x80, 0x73, 0xbb, 0xe1, 0xff, 0xa0, 0x96, 0xe9, 0x4d, 0x05, 0x06, - 0x4e, 0x53, 0x8f, 0x5c, 0x0d, 0xa9, 0x78, 0x2e, 0x30, 0x20, 0xdf, 0xc0, 0x1a, 0x0e, 0x06, 0xe8, - 0x4b, 0x76, 0x8c, 0xde, 0xe2, 0xe7, 0xae, 0xe8, 0x12, 0x77, 0xd3, 0x12, 0xdf, 0x78, 0x8f, 0x12, - 0xef, 0x2b, 0x4d, 0xcd, 0xa9, 0x1e, 0x64, 0x55, 0xe9, 0x9e, 0xe6, 0x37, 0x95, 0x5d, 0xd7, 0xab, - 0x58, 0x1a, 0x6f, 0x4a, 0x7c, 0x15, 0x40, 0x6d, 0xce, 0x64, 0xda, 0x3f, 0xc2, 0x99, 0xee, 0xaa, - 0xb2, 0xab, 0xb6, 0xeb, 0x40, 0x03, 0xe7, 0x34, 0x60, 0xf5, 0x3f, 0x6e, 0xc0, 0x2f, 0x0b, 0xa5, - 0x9a, 0xdd, 0x6c, 0xff, 0x65, 0x41, 0xd1, 0x00, 0x64, 0x1b, 0x8a, 0x69, 0x2e, 0x4b, 0xe7, 0xba, - 0x75, 0x41, 0xae, 0x5d, 0x5f, 0x26, 0x69, 0x86, 0x74, 0x22, 0xb9, 0x0e, 0x75, 0x63, 0x79, 0x63, - 0x14, 0x82, 0x86, 0xa8, 0x1b, 0xa6, 0xec, 0xd6, 0x0c, 0xfa, 0xd8, 0x80, 0xe4, 0x2e, 0x34, 0x47, - 0x54, 0xc8, 0xe7, 0x93, 0x80, 0x4a, 0xf4, 0x24, 0x1b, 0xa3, 0x90, 0x74, 0x3c, 0xd1, 0x9d, 0x93, - 0x77, 0xd7, 0x16, 0xb1, 0xc3, 0x2c, 0x44, 0x3a, 0xd0, 0x60, 0x62, 0x5b, 0xb5, 0xb4, 0x8b, 0x83, - 0x69, 0x14, 0x60, 0xa0, 0xdb, 0xa4, 0xe4, 0x9e, 0x86, 0xc9, 0xa7, 0x70, 0xd9, 0x8f, 0x91, 0xaa, - 0x63, 0x64, 0xc1, 0xbc, 0xa2, 0x99, 0xed, 0x34, 0x30, 0xa7, 0x6d, 0xff, 0x9a, 0x87, 0xea, 0xae, - 0xfa, 0x25, 0xdd, 0xc9, 0x87, 0x09, 0x71, 0x60, 0x55, 0x0f, 0xe2, 0xd9, 0x79, 0x90, 0xb9, 0xea, - 0xc2, 0x30, 0xd2, 0x35, 0xbf, 0x64, 0x1c, 0xf2, 0x2d, 0x94, 0xf5, 0x61, 0x35, 0x40, 0x14, 0xe6, - 0x2a, 0xd9, 0xd9, 0xfd, 0x87, 0x8d, 0xfb, 0xe7, 0x9b, 0x6b, 0xf6, 0x8c, 0x8e, 0x47, 0x5f, 0xb4, - 0xe7, 0x4c, 0x6d, 0xb7, 0xa4, 0xec, 0x3d, 0x44, 0x41, 0x6e, 0x42, 0x23, 0xc6, 0x11, 0x9d, 0x61, - 0x30, 0x2f, 0x6a, 0xd1, 0xb4, 0x5d, 0x0a, 0x67, 0x55, 0xdd, 0x83, 0x8a, 0xef, 0xcb, 0x24, 0x13, - 0x8c, 0xea, 0xbe, 0xca, 0xd9, 0xc7, 0xd0, 0x89, 0x4d, 0x4c, 0x37, 0x10, 0xfc, 0xf9, 0x66, 0x92, - 0x67, 0x50, 0x67, 0xe6, 0x2e, 0xf7, 0x26, 0xfa, 0x94, 0xd4, 0xcd, 0x5a, 0xd9, 0xfa, 0xec, 0x02, - 0xaa, 0xa5, 0x07, 0x80, 0x5b, 0x63, 0x4b, 0xef, 0x81, 0xaf, 0xa1, 0xc1, 0xd3, 0xa3, 0x37, 0x63, - 0x85, 0xcd, 0x7c, 0xa7, 0xb2, 0x75, 0xe7, 0x02, 0xd6, 0xe5, 0x03, 0xdb, 0xad, 0xf3, 0x25, 0xff, - 0xf6, 0xf7, 0x00, 0x0b, 0x1d, 0x12, 0x02, 0xf5, 0x03, 0x8c, 0x02, 0x16, 0x85, 0xe9, 0x62, 0xec, - 0x1c, 0x59, 0x83, 0x46, 0x8a, 0x65, 0x54, 0xb6, 0x45, 0x2e, 0x43, 0x2d, 0xf3, 0x1e, 0xb3, 0x08, - 0x03, 0x3b, 0xaf, 0xa0, 0x74, 0x9c, 0x8b, 0xc7, 0x18, 0x4b, 0xbb, 0x40, 0xaa, 0x50, 0x32, 0x36, - 0x06, 0xf6, 0x0a, 0xa9, 0xc0, 0xea, 0xb6, 0xb9, 0x53, 0xec, 0xe2, 0x46, 0xe1, 0xe7, 0x9f, 0x5a, - 0xd6, 0xed, 0x47, 0xd0, 0x3c, 0xab, 0xe3, 0x88, 0x0d, 0xd5, 0x27, 0x5c, 0xee, 0x65, 0x37, 0xac, - 0x9d, 0x23, 0x35, 0x28, 0x2f, 0x5c, 0x4b, 0x31, 0xdf, 0x4f, 0xd0, 0x9f, 0x2a, 0xb2, 0x4b, 0x86, - 0x6c, 0xe7, 0xd1, 0xab, 0xb7, 0x2d, 0xeb, 0xf5, 0xdb, 0x96, 0xf5, 0xc7, 0xdb, 0x96, 0xf5, 0xc3, - 0xbb, 0x56, 0xee, 0xf5, 0xbb, 0x56, 0xee, 0xb7, 0x77, 0xad, 0xdc, 0x8b, 0xbb, 0x27, 0x94, 0xa4, - 0x6a, 0x74, 0xe7, 0xd4, 0x93, 0x2c, 0x39, 0xf9, 0xfa, 0xd3, 0xc2, 0xea, 0x17, 0xf5, 0xc3, 0xec, - 0xde, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x84, 0xca, 0xb3, 0xff, 0x2b, 0x0a, 0x00, 0x00, + // 1286 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x4f, 0x6f, 0x13, 0x47, + 0x14, 0xf7, 0x26, 0x8e, 0x63, 0x3f, 0xff, 0xc9, 0x32, 0x31, 0x61, 0x49, 0x85, 0x49, 0xdd, 0x02, + 0x86, 0x16, 0x5b, 0x04, 0xa9, 0xaa, 0x7a, 0x4b, 0x22, 0x02, 0x29, 0x85, 0x44, 0x4b, 0x40, 0x82, + 0x43, 0xb7, 0xe3, 0xdd, 0xc9, 0x7a, 0x14, 0x7b, 0xc7, 0xdd, 0x19, 0x47, 0x0e, 0xea, 0xad, 0xe7, + 0x4a, 0xed, 0x77, 0xe8, 0xa1, 0xc7, 0x7e, 0x0c, 0x8e, 0x1c, 0xab, 0x1e, 0x50, 0x05, 0xdf, 0xa0, + 0xe7, 0x1e, 0xaa, 0xf9, 0xb7, 0x8e, 0x91, 0x9b, 0x50, 0xda, 0x93, 0x67, 0x7e, 0x6f, 0xde, 0xef, + 0xcd, 0xbe, 0xf9, 0xbd, 0x37, 0x63, 0x58, 0x7f, 0x4e, 0x04, 0x0e, 0x7b, 0x98, 0x26, 0x1d, 0x35, + 0x62, 0x29, 0xe9, 0x84, 0x29, 0xe3, 0x5c, 0x63, 0x6a, 0x18, 0xa8, 0x71, 0x20, 0xc6, 0xed, 0x61, + 0xca, 0x04, 0x43, 0x97, 0x32, 0x9f, 0xb6, 0xf5, 0x69, 0x4f, 0x7c, 0x56, 0xeb, 0x31, 0x8b, 0x99, + 0x5a, 0xd9, 0x91, 0x23, 0xed, 0xb4, 0x7a, 0x75, 0x46, 0xa0, 0xe1, 0x61, 0xdc, 0x09, 0x99, 0x0c, + 0xc3, 0x68, 0xa2, 0xd7, 0x35, 0x7f, 0xcd, 0x43, 0x75, 0x27, 0xe9, 0xb2, 0x51, 0x12, 0xed, 0xe1, + 0x14, 0x0f, 0x38, 0x5a, 0x81, 0x02, 0x27, 0x49, 0x44, 0x52, 0xcf, 0x59, 0x73, 0x5a, 0x25, 0xdf, + 0xcc, 0xd0, 0x55, 0x58, 0xd2, 0x23, 0xb3, 0x3f, 0x1a, 0x79, 0x73, 0x6b, 0x4e, 0x6b, 0xde, 0xaf, + 0x6a, 0x78, 0x4b, 0xa2, 0x3b, 0x11, 0xfa, 0x00, 0x4a, 0x62, 0x1c, 0xb0, 0x94, 0xc6, 0x34, 0xf1, + 0xe6, 0x15, 0x45, 0x51, 0x8c, 0x77, 0xd5, 0x1c, 0x6d, 0x42, 0x49, 0x06, 0x0f, 0xc4, 0xf1, 0x90, + 0x78, 0xf9, 0x35, 0xa7, 0x55, 0x5b, 0xbf, 0xd2, 0x9e, 0xf1, 0x7d, 0xc3, 0xc3, 0xb8, 0xad, 0x76, + 0xb9, 0xc5, 0x68, 0xb2, 0x7f, 0x3c, 0x24, 0x7e, 0x31, 0x34, 0x23, 0x54, 0x87, 0x05, 0xcc, 0x39, + 0x11, 0xde, 0x82, 0x22, 0xd7, 0x13, 0x74, 0x17, 0x0a, 0x78, 0xc0, 0x46, 0x89, 0xf0, 0x0a, 0x12, + 0xde, 0xec, 0xbc, 0x78, 0x75, 0x39, 0xf7, 0xfb, 0xab, 0xcb, 0xd7, 0x62, 0x2a, 0x7a, 0xa3, 0x6e, + 0x3b, 0x64, 0x83, 0x4e, 0xc8, 0xf8, 0x80, 0x71, 0xf3, 0x73, 0x93, 0x47, 0x87, 0x1d, 0xb9, 0x0f, + 0xde, 0x7e, 0x4c, 0x13, 0xe1, 0x1b, 0x77, 0xf4, 0x11, 0x54, 0x59, 0x97, 0x93, 0xf4, 0x88, 0x44, + 0x41, 0x0f, 0xf3, 0x9e, 0xb7, 0xa8, 0xc2, 0x54, 0x2c, 0x78, 0x0f, 0xf3, 0x1e, 0xfa, 0x1c, 0xbc, + 0x6c, 0x11, 0x19, 0x0b, 0x92, 0x26, 0xb8, 0x1f, 0xf4, 0x08, 0x8d, 0x7b, 0xc2, 0x2b, 0xae, 0x39, + 0xad, 0xbc, 0xbf, 0x62, 0xed, 0x77, 0x8c, 0xf9, 0x9e, 0xb2, 0xa2, 0x0f, 0xa1, 0xd2, 0xc5, 0xfd, + 0x3e, 0x13, 0x01, 0x4d, 0x22, 0x32, 0xf6, 0x4a, 0x8a, 0xbd, 0xac, 0xb1, 0x1d, 0x09, 0xa1, 0x75, + 0x38, 0x7f, 0x40, 0x13, 0xdc, 0xa7, 0xcf, 0x49, 0x14, 0xc8, 0x94, 0x58, 0x66, 0x50, 0xcc, 0xcb, + 0x99, 0xf1, 0x19, 0x11, 0xd8, 0xd0, 0x52, 0x58, 0x11, 0xe3, 0xc0, 0x58, 0xb0, 0xa0, 0x2c, 0x09, + 0xb8, 0xc0, 0x62, 0xc4, 0xbd, 0xb2, 0xca, 0xf2, 0xed, 0xf6, 0xa9, 0x2a, 0x6a, 0xef, 0x8f, 0xb7, + 0x4f, 0xf8, 0x3e, 0x52, 0xae, 0x7e, 0x5d, 0xcc, 0x40, 0x9b, 0xdf, 0x42, 0x4d, 0x06, 0xde, 0x08, + 0x43, 0x99, 0x2f, 0x9a, 0xc4, 0x28, 0x80, 0x65, 0xdc, 0x65, 0xa9, 0xb0, 0xdb, 0x35, 0x07, 0xe1, + 0xbc, 0xdf, 0x41, 0x9c, 0x33, 0x5c, 0x2a, 0x88, 0x62, 0x6a, 0xfe, 0x54, 0x80, 0xda, 0xee, 0x48, + 0x9c, 0x94, 0xe9, 0x2a, 0x14, 0x53, 0x12, 0x12, 0x7a, 0x94, 0x09, 0x35, 0x9b, 0xa3, 0xeb, 0xe0, + 0xda, 0xb1, 0x16, 0xeb, 0x8e, 0xd5, 0xea, 0x92, 0xc5, 0xad, 0x5a, 0xa7, 0x04, 0x39, 0xff, 0x7e, + 0x82, 0x9c, 0x48, 0x2f, 0xff, 0xdf, 0xa4, 0x27, 0x4b, 0x87, 0xf3, 0x20, 0x61, 0x49, 0x48, 0x94, + 0xba, 0xf3, 0x7e, 0x51, 0x70, 0xfe, 0x50, 0xce, 0xa5, 0x31, 0xc6, 0x3c, 0xe8, 0xd3, 0x01, 0xd5, + 0x1a, 0xcf, 0xfb, 0xc5, 0x18, 0xf3, 0xaf, 0xe4, 0xdc, 0x1a, 0x87, 0x29, 0x0d, 0x89, 0x11, 0xac, + 0x34, 0xee, 0xc9, 0x39, 0x6a, 0x81, 0x6b, 0x8c, 0x2c, 0xa5, 0xe2, 0x38, 0x38, 0x20, 0xc4, 0xbb, + 0xa0, 0xd6, 0xd4, 0xf4, 0x1a, 0x05, 0x6f, 0x13, 0x82, 0x10, 0xe4, 0x95, 0xe4, 0x8b, 0xca, 0xaa, + 0xc6, 0xef, 0x22, 0xd8, 0xd3, 0xaa, 0x01, 0x4e, 0xad, 0x86, 0x8b, 0x20, 0xb7, 0x19, 0x8c, 0x38, + 0x89, 0xbc, 0xba, 0x5a, 0xb9, 0x18, 0x63, 0xfe, 0x98, 0x93, 0x08, 0x7d, 0x0d, 0xcb, 0xe4, 0xe0, + 0x80, 0x84, 0x82, 0x1e, 0x91, 0x60, 0xf2, 0x71, 0xe7, 0x55, 0x8a, 0xdb, 0x26, 0xc5, 0x57, 0xdf, + 0x21, 0xc5, 0x3b, 0x52, 0x53, 0x19, 0xd5, 0x5d, 0x9b, 0x95, 0xf6, 0xdb, 0xfc, 0x3a, 0xb3, 0x2b, + 0x6a, 0x17, 0x53, 0xeb, 0x75, 0x8a, 0x2f, 0x01, 0xc8, 0xc3, 0x19, 0x8e, 0xba, 0x87, 0xe4, 0x58, + 0x55, 0x55, 0xc9, 0x97, 0xc7, 0xb5, 0xa7, 0x80, 0x53, 0x0a, 0xb0, 0xf2, 0x3f, 0x17, 0xe0, 0x97, + 0xf9, 0x62, 0xd5, 0xad, 0x37, 0xff, 0x72, 0xa0, 0xa0, 0x01, 0xb4, 0x01, 0x05, 0x13, 0xcb, 0x51, + 0xb1, 0xae, 0x9f, 0x11, 0x6b, 0x2b, 0x14, 0x63, 0x13, 0xc1, 0x38, 0xa2, 0x2b, 0x50, 0xd3, 0xa3, + 0x60, 0x40, 0x38, 0xc7, 0x31, 0x51, 0x05, 0x53, 0xf2, 0xab, 0x1a, 0x7d, 0xa0, 0x41, 0x74, 0x0b, + 0xea, 0x7d, 0xcc, 0xc5, 0xe3, 0x61, 0x84, 0x05, 0x09, 0x04, 0x1d, 0x10, 0x2e, 0xf0, 0x60, 0xa8, + 0x2a, 0x67, 0xde, 0x5f, 0x9e, 0xd8, 0xf6, 0xad, 0x09, 0xb5, 0x60, 0x89, 0xf2, 0x0d, 0x59, 0xd2, + 0x3e, 0x39, 0x18, 0x25, 0x11, 0x89, 0x54, 0x99, 0x14, 0xfd, 0xb7, 0x61, 0xf4, 0x09, 0x9c, 0x0b, + 0x53, 0x82, 0x65, 0x1b, 0x99, 0x30, 0x2f, 0x28, 0x66, 0xd7, 0x18, 0x32, 0xda, 0xe6, 0xf7, 0x73, + 0x50, 0xf5, 0xc9, 0x11, 0x49, 0xc5, 0xee, 0x50, 0xe6, 0x46, 0x7d, 0x42, 0xaa, 0x80, 0x00, 0x47, + 0x51, 0x4a, 0x38, 0x37, 0x7d, 0xa1, 0xaa, 0xd1, 0x0d, 0x0d, 0xa2, 0x8f, 0xa1, 0x16, 0xe2, 0x7e, + 0x3f, 0x60, 0x49, 0xa0, 0x0d, 0xea, 0x4b, 0x8b, 0x7e, 0x45, 0xa2, 0xbb, 0x89, 0xe6, 0x94, 0xb7, + 0x80, 0x6a, 0x43, 0x19, 0x97, 0xbe, 0xc9, 0x2a, 0x0a, 0xb4, 0x54, 0x93, 0x88, 0x36, 0x69, 0xf2, + 0xcb, 0x2a, 0x36, 0xa2, 0x4d, 0xda, 0x53, 0xd9, 0x8e, 0xd4, 0xb2, 0x89, 0xcc, 0x16, 0xde, 0xaf, + 0x53, 0x98, 0x78, 0x56, 0x94, 0xcd, 0x1f, 0x16, 0xa0, 0xb2, 0x25, 0x0f, 0x56, 0xf5, 0xb3, 0xfd, + 0x31, 0xf2, 0x60, 0x51, 0xa5, 0x8a, 0xd9, 0xae, 0x68, 0xa7, 0xf2, 0xda, 0xd4, 0x05, 0xac, 0x0f, + 0x56, 0x4f, 0xd0, 0x37, 0x50, 0x52, 0x2d, 0xfb, 0x80, 0x10, 0x6e, 0x36, 0xb5, 0xf5, 0x2f, 0x37, + 0xf5, 0xe7, 0xab, 0xcb, 0xee, 0x31, 0x1e, 0xf4, 0xbf, 0x68, 0x66, 0x4c, 0x4d, 0xbf, 0x28, 0xc7, + 0xdb, 0x84, 0x70, 0x74, 0x0d, 0x96, 0x52, 0xd2, 0xc7, 0xc7, 0x24, 0xca, 0xb2, 0x54, 0xd0, 0xcd, + 0xc7, 0xc0, 0x36, 0x4d, 0xdb, 0x50, 0x0e, 0x43, 0x31, 0xb6, 0x65, 0x23, 0x7b, 0x50, 0x79, 0x76, + 0x33, 0x3e, 0x21, 0x65, 0x23, 0x63, 0x08, 0x33, 0x49, 0xa3, 0x47, 0x50, 0xa3, 0xfa, 0x45, 0x13, + 0x0c, 0xd5, 0x5d, 0xa1, 0x5a, 0x56, 0x79, 0xfd, 0xd3, 0x33, 0xa8, 0xa6, 0x9e, 0x41, 0x7e, 0x95, + 0x4e, 0xbd, 0x8a, 0x9e, 0xc0, 0x12, 0x33, 0x17, 0x90, 0x65, 0x85, 0xb5, 0xf9, 0x56, 0x79, 0xfd, + 0xe6, 0x19, 0xac, 0xd3, 0xd7, 0x96, 0x5f, 0x63, 0xd3, 0xd7, 0x58, 0x0a, 0x17, 0xd5, 0x43, 0x2c, + 0x64, 0xfd, 0x20, 0x64, 0x89, 0x48, 0x71, 0x28, 0x82, 0x23, 0x92, 0x72, 0xca, 0x12, 0x73, 0x75, + 0x7f, 0x76, 0x46, 0x84, 0x3d, 0xe3, 0xbf, 0x65, 0xdc, 0x9f, 0x68, 0x6f, 0xff, 0xc2, 0x70, 0xb6, + 0x01, 0x3d, 0xcd, 0x64, 0xcb, 0x74, 0xe9, 0xa8, 0x16, 0x75, 0x76, 0x82, 0xa6, 0xca, 0x6d, 0x33, + 0x2f, 0x65, 0x62, 0xa5, 0x6e, 0xc0, 0x1b, 0xdf, 0x01, 0x4c, 0x9a, 0x0b, 0x42, 0x50, 0xdb, 0x23, + 0x49, 0x44, 0x93, 0xd8, 0xe4, 0xd6, 0xcd, 0xa1, 0x65, 0x58, 0x32, 0x98, 0xcd, 0x8c, 0xeb, 0xa0, + 0x73, 0x50, 0xb5, 0xb3, 0x07, 0x34, 0x21, 0x91, 0x3b, 0x2f, 0x21, 0xb3, 0x4e, 0x87, 0x75, 0xf3, + 0xa8, 0x02, 0x45, 0x3d, 0x26, 0x91, 0xbb, 0x80, 0xca, 0xb0, 0xb8, 0xa1, 0x1f, 0x0a, 0x6e, 0x61, + 0x35, 0xff, 0xcb, 0xcf, 0x0d, 0xe7, 0xc6, 0x7d, 0xa8, 0xcf, 0x6a, 0xa3, 0xc8, 0x85, 0xca, 0x43, + 0x26, 0xb6, 0xed, 0xb3, 0xc9, 0xcd, 0xa1, 0x2a, 0x94, 0x26, 0x53, 0x47, 0x32, 0xdf, 0x19, 0x93, + 0x70, 0x24, 0xc9, 0xe6, 0x0c, 0x59, 0x07, 0x2e, 0xfc, 0x43, 0x66, 0x51, 0x01, 0xe6, 0x9e, 0xdc, + 0x72, 0x73, 0xea, 0x77, 0xdd, 0x75, 0xb4, 0xc3, 0xe6, 0xfd, 0x17, 0xaf, 0x1b, 0xce, 0xcb, 0xd7, + 0x0d, 0xe7, 0x8f, 0xd7, 0x0d, 0xe7, 0xc7, 0x37, 0x8d, 0xdc, 0xcb, 0x37, 0x8d, 0xdc, 0x6f, 0x6f, + 0x1a, 0xb9, 0x67, 0xb7, 0x4e, 0x54, 0x92, 0x4c, 0xec, 0xcd, 0xb7, 0x1e, 0xe6, 0xe3, 0x93, 0xff, + 0x01, 0x54, 0x61, 0x75, 0x0b, 0xea, 0xf0, 0x6e, 0xff, 0x1d, 0x00, 0x00, 0xff, 0xff, 0xe9, 0x16, + 0x38, 0x3b, 0x31, 0x0c, 0x00, 0x00, } func (m *InboundParams) Marshal() (dAtA []byte, err error) { @@ -963,6 +1089,70 @@ func (m *Status) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *RevertOptions) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RevertOptions) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RevertOptions) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.RevertGasLimit.Size() + i -= size + if _, err := m.RevertGasLimit.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintCrossChainTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.RevertMessage) > 0 { + i -= len(m.RevertMessage) + copy(dAtA[i:], m.RevertMessage) + i = encodeVarintCrossChainTx(dAtA, i, uint64(len(m.RevertMessage))) + i-- + dAtA[i] = 0x22 + } + if len(m.AbortAddress) > 0 { + i -= len(m.AbortAddress) + copy(dAtA[i:], m.AbortAddress) + i = encodeVarintCrossChainTx(dAtA, i, uint64(len(m.AbortAddress))) + i-- + dAtA[i] = 0x1a + } + if m.CallOnRevert { + i-- + if m.CallOnRevert { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.RevertAddress) > 0 { + i -= len(m.RevertAddress) + copy(dAtA[i:], m.RevertAddress) + i = encodeVarintCrossChainTx(dAtA, i, uint64(len(m.RevertAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *CrossChainTx) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -983,6 +1173,21 @@ func (m *CrossChainTx) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.RevertOptions.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCrossChainTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x62 + if m.ProtocolContractVersion != 0 { + i = encodeVarintCrossChainTx(dAtA, i, uint64(m.ProtocolContractVersion)) + i-- + dAtA[i] = 0x58 + } if len(m.OutboundParams) > 0 { for iNdEx := len(m.OutboundParams) - 1; iNdEx >= 0; iNdEx-- { { @@ -1209,6 +1414,32 @@ func (m *Status) Size() (n int) { return n } +func (m *RevertOptions) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.RevertAddress) + if l > 0 { + n += 1 + l + sovCrossChainTx(uint64(l)) + } + if m.CallOnRevert { + n += 2 + } + l = len(m.AbortAddress) + if l > 0 { + n += 1 + l + sovCrossChainTx(uint64(l)) + } + l = len(m.RevertMessage) + if l > 0 { + n += 1 + l + sovCrossChainTx(uint64(l)) + } + l = m.RevertGasLimit.Size() + n += 1 + l + sovCrossChainTx(uint64(l)) + return n +} + func (m *CrossChainTx) Size() (n int) { if m == nil { return 0 @@ -1243,6 +1474,11 @@ func (m *CrossChainTx) Size() (n int) { n += 1 + l + sovCrossChainTx(uint64(l)) } } + if m.ProtocolContractVersion != 0 { + n += 1 + sovCrossChainTx(uint64(m.ProtocolContractVersion)) + } + l = m.RevertOptions.Size() + n += 1 + l + sovCrossChainTx(uint64(l)) return n } @@ -2296,6 +2532,208 @@ func (m *Status) Unmarshal(dAtA []byte) error { } return nil } +func (m *RevertOptions) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RevertOptions: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RevertOptions: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevertAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RevertAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CallOnRevert", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CallOnRevert = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AbortAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AbortAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevertMessage", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RevertMessage = append(m.RevertMessage[:0], dAtA[iNdEx:postIndex]...) + if m.RevertMessage == nil { + m.RevertMessage = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevertGasLimit", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RevertGasLimit.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCrossChainTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthCrossChainTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *CrossChainTx) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -2561,6 +2999,58 @@ func (m *CrossChainTx) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProtocolContractVersion", wireType) + } + m.ProtocolContractVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProtocolContractVersion |= ProtocolContractVersion(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevertOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCrossChainTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCrossChainTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCrossChainTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RevertOptions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipCrossChainTx(dAtA[iNdEx:]) diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index 1422538b2f..1305c31b7c 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -145,7 +145,18 @@ type FungibleKeeper interface { data []byte, coinType coin.CoinType, asset string, + protocolContractVersion ProtocolContractVersion, ) (*evmtypes.MsgEthereumTxResponse, bool, error) + ProcessV2RevertDeposit( + ctx sdk.Context, + amount *big.Int, + chainID int64, + coinType coin.CoinType, + asset string, + revertAddress ethcommon.Address, + callOnRevert bool, + revertMessage []byte, + ) error CallUniswapV2RouterSwapExactTokensForTokens( ctx sdk.Context, sender ethcommon.Address, diff --git a/x/crosschain/types/message_vote_inbound.go b/x/crosschain/types/message_vote_inbound.go index b2ee24ce2d..b5720e5e6f 100644 --- a/x/crosschain/types/message_vote_inbound.go +++ b/x/crosschain/types/message_vote_inbound.go @@ -6,6 +6,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/crypto" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" "github.com/zeta-chain/zetacore/pkg/authz" "github.com/zeta-chain/zetacore/pkg/coin" @@ -17,6 +19,25 @@ import ( // https://github.com/zeta-chain/node/issues/862 const MaxMessageLength = 10240 +// InboundVoteOption is a function that sets some option on the inbound vote message +type InboundVoteOption func(*MsgVoteInbound) + +// WithZEVMRevertOptions sets the revert options for the inbound vote message (ZEVM format) +// the function convert the type from abigen to type defined in proto +func WithZEVMRevertOptions(revertOptions gatewayzevm.RevertOptions) InboundVoteOption { + return func(msg *MsgVoteInbound) { + msg.RevertOptions = NewRevertOptionsFromZEVM(revertOptions) + } +} + +// WithEVMRevertOptions sets the revert options for the inbound vote message (EVM format) +// the function convert the type from abigen to type defined in proto +func WithEVMRevertOptions(revertOptions gatewayevm.RevertOptions) InboundVoteOption { + return func(msg *MsgVoteInbound) { + msg.RevertOptions = NewRevertOptionsFromEVM(revertOptions) + } +} + var _ sdk.Msg = &MsgVoteInbound{} func NewMsgVoteInbound( @@ -34,23 +55,33 @@ func NewMsgVoteInbound( coinType coin.CoinType, asset string, eventIndex uint, + protocolContractVersion ProtocolContractVersion, + options ...InboundVoteOption, ) *MsgVoteInbound { - return &MsgVoteInbound{ - Creator: creator, - Sender: sender, - SenderChainId: senderChain, - TxOrigin: txOrigin, - Receiver: receiver, - ReceiverChain: receiverChain, - Amount: amount, - Message: message, - InboundHash: inboundHash, - InboundBlockHeight: inboundBlockHeight, - GasLimit: gasLimit, - CoinType: coinType, - Asset: asset, - EventIndex: uint64(eventIndex), + msg := &MsgVoteInbound{ + Creator: creator, + Sender: sender, + SenderChainId: senderChain, + TxOrigin: txOrigin, + Receiver: receiver, + ReceiverChain: receiverChain, + Amount: amount, + Message: message, + InboundHash: inboundHash, + InboundBlockHeight: inboundBlockHeight, + GasLimit: gasLimit, + CoinType: coinType, + Asset: asset, + EventIndex: uint64(eventIndex), + ProtocolContractVersion: protocolContractVersion, + RevertOptions: NewEmptyRevertOptions(), } + + for _, option := range options { + option(msg) + } + + return msg } func (msg *MsgVoteInbound) Route() string { diff --git a/x/crosschain/types/message_vote_inbound_test.go b/x/crosschain/types/message_vote_inbound_test.go index f96f640b9e..29cf49ba40 100644 --- a/x/crosschain/types/message_vote_inbound_test.go +++ b/x/crosschain/types/message_vote_inbound_test.go @@ -1,6 +1,9 @@ package types_test import ( + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + "math/big" "math/rand" "testing" @@ -15,6 +18,168 @@ import ( "github.com/zeta-chain/zetacore/x/crosschain/types" ) +func TestNewMsgVoteInbound(t *testing.T) { + t.Run("empty revert options by default", func(t *testing.T) { + msg := types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + ) + require.EqualValues(t, types.NewEmptyRevertOptions(), msg.RevertOptions) + }) + + t.Run("can set ZEVM revert options", func(t *testing.T) { + revertAddress := sample.EthAddress() + abortAddress := sample.EthAddress() + revertMessage := sample.Bytes() + + msg := types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + types.WithZEVMRevertOptions(gatewayzevm.RevertOptions{ + RevertAddress: revertAddress, + CallOnRevert: true, + AbortAddress: abortAddress, + RevertMessage: revertMessage, + OnRevertGasLimit: big.NewInt(1000), + }), + ) + require.EqualValues(t, types.RevertOptions{ + RevertAddress: revertAddress.Hex(), + CallOnRevert: true, + AbortAddress: abortAddress.Hex(), + RevertMessage: revertMessage, + RevertGasLimit: math.NewUint(1000), + }, msg.RevertOptions) + + // if revertGasLimit not specified, it should be zero + msg = types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + types.WithZEVMRevertOptions(gatewayzevm.RevertOptions{ + RevertAddress: revertAddress, + CallOnRevert: true, + AbortAddress: abortAddress, + RevertMessage: revertMessage, + }), + ) + require.EqualValues(t, types.RevertOptions{ + RevertAddress: revertAddress.Hex(), + CallOnRevert: true, + AbortAddress: abortAddress.Hex(), + RevertMessage: revertMessage, + RevertGasLimit: math.ZeroUint(), + }, msg.RevertOptions) + }) + + t.Run("can set EVM revert options", func(t *testing.T) { + revertAddress := sample.EthAddress() + abortAddress := sample.EthAddress() + revertMessage := sample.Bytes() + + msg := types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + types.WithEVMRevertOptions(gatewayevm.RevertOptions{ + RevertAddress: revertAddress, + CallOnRevert: true, + AbortAddress: abortAddress, + RevertMessage: revertMessage, + OnRevertGasLimit: big.NewInt(1000), + }), + ) + require.EqualValues(t, types.RevertOptions{ + RevertAddress: revertAddress.Hex(), + CallOnRevert: true, + AbortAddress: abortAddress.Hex(), + RevertMessage: revertMessage, + RevertGasLimit: math.NewUint(1000), + }, msg.RevertOptions) + + msg = types.NewMsgVoteInbound( + sample.AccAddress(), + sample.AccAddress(), + 42, + sample.String(), + sample.String(), + 42, + math.NewUint(42), + sample.String(), + sample.String(), + 42, + 42, + coin.CoinType_Zeta, + sample.String(), + 42, + types.ProtocolContractVersion_V1, + types.WithEVMRevertOptions(gatewayevm.RevertOptions{ + RevertAddress: revertAddress, + CallOnRevert: true, + AbortAddress: abortAddress, + RevertMessage: revertMessage, + }), + ) + require.EqualValues(t, types.RevertOptions{ + RevertAddress: revertAddress.Hex(), + CallOnRevert: true, + AbortAddress: abortAddress.Hex(), + RevertMessage: revertMessage, + RevertGasLimit: math.ZeroUint(), + }, msg.RevertOptions) + }) +} + func TestMsgVoteInbound_ValidateBasic(t *testing.T) { r := rand.New(rand.NewSource(42)) @@ -40,6 +205,7 @@ func TestMsgVoteInbound_ValidateBasic(t *testing.T) { coin.CoinType_Zeta, sample.String(), 42, + types.ProtocolContractVersion_V1, ), }, { @@ -59,6 +225,7 @@ func TestMsgVoteInbound_ValidateBasic(t *testing.T) { coin.CoinType_Zeta, sample.String(), 42, + types.ProtocolContractVersion_V1, ), err: sdkerrors.ErrInvalidAddress, }, @@ -79,6 +246,7 @@ func TestMsgVoteInbound_ValidateBasic(t *testing.T) { coin.CoinType_Zeta, sample.String(), 42, + types.ProtocolContractVersion_V1, ), err: types.ErrInvalidChainID, }, @@ -99,6 +267,7 @@ func TestMsgVoteInbound_ValidateBasic(t *testing.T) { coin.CoinType_Zeta, sample.String(), 42, + types.ProtocolContractVersion_V1, ), err: types.ErrInvalidChainID, }, @@ -119,6 +288,7 @@ func TestMsgVoteInbound_ValidateBasic(t *testing.T) { coin.CoinType_Zeta, sample.String(), 42, + types.ProtocolContractVersion_V1, ), err: sdkerrors.ErrInvalidRequest, }, @@ -139,20 +309,21 @@ func TestMsgVoteInbound_Digest(t *testing.T) { r := rand.New(rand.NewSource(42)) msg := types.MsgVoteInbound{ - Creator: sample.AccAddress(), - Sender: sample.AccAddress(), - SenderChainId: 42, - TxOrigin: sample.String(), - Receiver: sample.String(), - ReceiverChain: 42, - Amount: math.NewUint(42), - Message: sample.String(), - InboundHash: sample.String(), - InboundBlockHeight: 42, - GasLimit: 42, - CoinType: coin.CoinType_Zeta, - Asset: sample.String(), - EventIndex: 42, + Creator: sample.AccAddress(), + Sender: sample.AccAddress(), + SenderChainId: 42, + TxOrigin: sample.String(), + Receiver: sample.String(), + ReceiverChain: 42, + Amount: math.NewUint(42), + Message: sample.String(), + InboundHash: sample.String(), + InboundBlockHeight: 42, + GasLimit: 42, + CoinType: coin.CoinType_Zeta, + Asset: sample.String(), + EventIndex: 42, + ProtocolContractVersion: types.ProtocolContractVersion_V1, } hash := msg.Digest() require.NotEmpty(t, hash, "hash should not be empty") @@ -240,6 +411,12 @@ func TestMsgVoteInbound_Digest(t *testing.T) { msg.EventIndex = 43 hash2 = msg.Digest() require.NotEqual(t, hash, hash2, "event index should change hash") + + // protocol contract version used + msg = msg + msg.ProtocolContractVersion = types.ProtocolContractVersion_V2 + hash2 = msg.Digest() + require.NotEqual(t, hash, hash2, "protocol contract version should change hash") } func TestMsgVoteInbound_GetSigners(t *testing.T) { diff --git a/x/crosschain/types/revert_options.go b/x/crosschain/types/revert_options.go new file mode 100644 index 0000000000..1f07e96239 --- /dev/null +++ b/x/crosschain/types/revert_options.go @@ -0,0 +1,83 @@ +package types + +import ( + sdkmath "cosmossdk.io/math" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/zetacore/pkg/crypto" +) + +// NewEmptyRevertOptions initializes a new empty RevertOptions +func NewEmptyRevertOptions() RevertOptions { + return RevertOptions{ + RevertGasLimit: sdkmath.ZeroUint(), // default to 0 instead of nil + } +} + +// NewRevertOptionsFromZEVM initializes a new RevertOptions from a gatewayzevm.RevertOptions +func NewRevertOptionsFromZEVM(revertOptions gatewayzevm.RevertOptions) RevertOptions { + revertGasLimit := sdkmath.ZeroUint() + if revertOptions.OnRevertGasLimit != nil { + revertGasLimit = sdkmath.NewUintFromBigInt(revertOptions.OnRevertGasLimit) + } + + return RevertOptions{ + RevertAddress: revertOptions.RevertAddress.Hex(), + CallOnRevert: revertOptions.CallOnRevert, + AbortAddress: revertOptions.AbortAddress.Hex(), + RevertMessage: revertOptions.RevertMessage, + RevertGasLimit: revertGasLimit, + } +} + +// NewRevertOptionsFromEVM initializes a new RevertOptions from a gatewayevm.RevertOptions +func NewRevertOptionsFromEVM(revertOptions gatewayevm.RevertOptions) RevertOptions { + revertGasLimit := sdkmath.ZeroUint() + if revertOptions.OnRevertGasLimit != nil { + revertGasLimit = sdkmath.NewUintFromBigInt(revertOptions.OnRevertGasLimit) + } + + return RevertOptions{ + RevertAddress: revertOptions.RevertAddress.Hex(), + CallOnRevert: revertOptions.CallOnRevert, + AbortAddress: revertOptions.AbortAddress.Hex(), + RevertMessage: revertOptions.RevertMessage, + RevertGasLimit: revertGasLimit, + } +} + +// ToZEVMRevertOptions converts the RevertOptions to a gatewayzevm.RevertOptions +func (r RevertOptions) ToZEVMRevertOptions() gatewayzevm.RevertOptions { + return gatewayzevm.RevertOptions{ + RevertAddress: ethcommon.HexToAddress(r.RevertAddress), + CallOnRevert: r.CallOnRevert, + AbortAddress: ethcommon.HexToAddress(r.AbortAddress), + RevertMessage: r.RevertMessage, + } +} + +// ToEVMRevertOptions converts the RevertOptions to a gatewayevm.RevertOptions +func (r RevertOptions) ToEVMRevertOptions() gatewayevm.RevertOptions { + return gatewayevm.RevertOptions{ + RevertAddress: ethcommon.HexToAddress(r.RevertAddress), + CallOnRevert: r.CallOnRevert, + AbortAddress: ethcommon.HexToAddress(r.AbortAddress), + RevertMessage: r.RevertMessage, + } +} + +// GetEVMRevertAddress returns the EVM revert address +// if the revert address is not a valid address, it returns false +func (r RevertOptions) GetEVMRevertAddress() (ethcommon.Address, bool) { + addr := ethcommon.HexToAddress(r.RevertAddress) + return addr, !crypto.IsEmptyAddress(addr) +} + +// GetEVMAbortAddress returns the EVM abort address +// if the abort address is not a valid address, it returns false +func (r RevertOptions) GetEVMAbortAddress() (ethcommon.Address, bool) { + addr := ethcommon.HexToAddress(r.AbortAddress) + return addr, !crypto.IsEmptyAddress(addr) +} diff --git a/x/crosschain/types/revert_options_test.go b/x/crosschain/types/revert_options_test.go new file mode 100644 index 0000000000..2b178e7c3f --- /dev/null +++ b/x/crosschain/types/revert_options_test.go @@ -0,0 +1,81 @@ +package types_test + +import ( + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" + "testing" +) + +func TestRevertOptions_GetEVMRevertAddress(t *testing.T) { + t.Run("valid revert address", func(t *testing.T) { + addr := sample.EthAddress() + actualAddr, valid := types.RevertOptions{ + RevertAddress: addr.Hex(), + }.GetEVMRevertAddress() + + require.True(t, valid) + require.Equal(t, addr.Hex(), actualAddr.Hex()) + }) + + t.Run("invalid revert address", func(t *testing.T) { + _, valid := types.RevertOptions{ + RevertAddress: "invalid", + }.GetEVMRevertAddress() + + require.False(t, valid) + }) + + t.Run("empty revert address", func(t *testing.T) { + _, valid := types.RevertOptions{ + RevertAddress: "", + }.GetEVMRevertAddress() + + require.False(t, valid) + }) + + t.Run("zero revert address", func(t *testing.T) { + _, valid := types.RevertOptions{ + RevertAddress: constant.EVMZeroAddress, + }.GetEVMRevertAddress() + + require.False(t, valid) + }) +} + +func TestRevertOptions_GetEVMAbortAddress(t *testing.T) { + t.Run("valid abort address", func(t *testing.T) { + addr := sample.EthAddress() + actualAddr, valid := types.RevertOptions{ + AbortAddress: addr.Hex(), + }.GetEVMAbortAddress() + + require.True(t, valid) + require.Equal(t, addr.Hex(), actualAddr.Hex()) + }) + + t.Run("invalid abort address", func(t *testing.T) { + _, valid := types.RevertOptions{ + AbortAddress: "invalid", + }.GetEVMAbortAddress() + + require.False(t, valid) + }) + + t.Run("empty abort address", func(t *testing.T) { + _, valid := types.RevertOptions{ + AbortAddress: "", + }.GetEVMAbortAddress() + + require.False(t, valid) + }) + + t.Run("zero abort address", func(t *testing.T) { + _, valid := types.RevertOptions{ + AbortAddress: constant.EVMZeroAddress, + }.GetEVMAbortAddress() + + require.False(t, valid) + }) +} diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index e5c9f99bab..bbf88f017b 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -997,6 +997,10 @@ type MsgVoteInbound struct { Asset string `protobuf:"bytes,14,opt,name=asset,proto3" json:"asset,omitempty"` // event index of the sent asset in the observed tx EventIndex uint64 `protobuf:"varint,15,opt,name=event_index,json=eventIndex,proto3" json:"event_index,omitempty"` + // protocol contract version to use for the cctx workflow + ProtocolContractVersion ProtocolContractVersion `protobuf:"varint,16,opt,name=protocol_contract_version,json=protocolContractVersion,proto3,enum=zetachain.zetacore.crosschain.ProtocolContractVersion" json:"protocol_contract_version,omitempty"` + // revert options provided by the sender + RevertOptions RevertOptions `protobuf:"bytes,17,opt,name=revert_options,json=revertOptions,proto3" json:"revert_options"` } func (m *MsgVoteInbound) Reset() { *m = MsgVoteInbound{} } @@ -1123,6 +1127,20 @@ func (m *MsgVoteInbound) GetEventIndex() uint64 { return 0 } +func (m *MsgVoteInbound) GetProtocolContractVersion() ProtocolContractVersion { + if m != nil { + return m.ProtocolContractVersion + } + return ProtocolContractVersion_V1 +} + +func (m *MsgVoteInbound) GetRevertOptions() RevertOptions { + if m != nil { + return m.RevertOptions + } + return RevertOptions{} +} + type MsgVoteInboundResponse struct { } @@ -1688,113 +1706,119 @@ func init() { } var fileDescriptor_15f0860550897740 = []byte{ - // 1689 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0x36, 0x8e, 0x63, 0x3f, 0x27, 0x4e, 0xba, 0x4d, 0x53, 0x67, 0xd3, 0x38, 0xa9, 0x4b, - 0x43, 0x84, 0x5a, 0x3b, 0x75, 0x4b, 0x29, 0x29, 0xa2, 0x34, 0x6e, 0x9b, 0x06, 0xea, 0x36, 0xda, - 0xa6, 0x80, 0xb8, 0xac, 0xd6, 0xbb, 0x93, 0xcd, 0x2a, 0xf6, 0x8e, 0xb5, 0x33, 0x4e, 0x9d, 0x0a, - 0x09, 0x84, 0x84, 0xc4, 0x11, 0x10, 0xa7, 0x1e, 0xb8, 0x21, 0xc1, 0x37, 0xe9, 0xb1, 0xe2, 0x84, - 0x38, 0x54, 0xa8, 0xfd, 0x02, 0xc0, 0x95, 0x0b, 0xda, 0x99, 0xd9, 0x8d, 0x77, 0xfd, 0x37, 0x8e, - 0x10, 0x97, 0x78, 0xe7, 0xed, 0xfb, 0xff, 0xde, 0xcc, 0xfb, 0xcd, 0x06, 0x96, 0x9f, 0x22, 0xaa, - 0x1b, 0xbb, 0xba, 0xed, 0x14, 0xd8, 0x13, 0x76, 0x51, 0xc1, 0x70, 0x31, 0x21, 0x9c, 0x46, 0x9b, - 0xf9, 0xba, 0x8b, 0x29, 0x96, 0x17, 0x02, 0xbe, 0xbc, 0xcf, 0x97, 0x3f, 0xe4, 0x53, 0x66, 0x2c, - 0x6c, 0x61, 0xc6, 0x59, 0xf0, 0x9e, 0xb8, 0x90, 0xf2, 0x56, 0x07, 0xe5, 0xf5, 0x3d, 0xab, 0xc0, - 0x48, 0x44, 0xfc, 0x08, 0xde, 0xe5, 0x6e, 0xbc, 0xd8, 0x76, 0xd8, 0x9f, 0x3e, 0x3a, 0xeb, 0x2e, - 0xc6, 0x3b, 0x44, 0xfc, 0x08, 0xde, 0x6b, 0xbd, 0x83, 0x73, 0x75, 0x8a, 0xb4, 0xaa, 0x5d, 0xb3, - 0x29, 0x72, 0xb5, 0x9d, 0xaa, 0x6e, 0x09, 0xb9, 0xdc, 0xf7, 0x12, 0xc8, 0x65, 0x62, 0x95, 0x6d, - 0xcb, 0x63, 0xd9, 0x26, 0xe4, 0x6e, 0xc3, 0x31, 0x89, 0x9c, 0x81, 0x71, 0xc3, 0x45, 0x3a, 0xc5, - 0x6e, 0x46, 0x5a, 0x92, 0x56, 0x92, 0xaa, 0xbf, 0x94, 0xe7, 0x20, 0xc1, 0x54, 0x6a, 0xb6, 0x99, - 0x39, 0xb1, 0x24, 0xad, 0x8c, 0xaa, 0xe3, 0x6c, 0xbd, 0x69, 0xca, 0x1b, 0x10, 0xd7, 0x6b, 0xb8, - 0xe1, 0xd0, 0xcc, 0xa8, 0x27, 0xb3, 0x5e, 0x78, 0xfe, 0x72, 0x71, 0xe4, 0xf7, 0x97, 0x8b, 0x6f, - 0x5a, 0x36, 0xdd, 0x6d, 0x54, 0xf2, 0x06, 0xae, 0x15, 0x0c, 0x4c, 0x6a, 0x98, 0x88, 0x9f, 0x4b, - 0xc4, 0xdc, 0x2b, 0xd0, 0x83, 0x3a, 0x22, 0xf9, 0xc7, 0xb6, 0x43, 0x55, 0x21, 0x9e, 0x3b, 0x0b, - 0x4a, 0xbb, 0x4f, 0x2a, 0x22, 0x75, 0xec, 0x10, 0x94, 0x7b, 0x00, 0xa7, 0xca, 0xc4, 0x7a, 0x5c, - 0x37, 0xf9, 0xcb, 0x5b, 0xa6, 0xe9, 0x22, 0xd2, 0xcb, 0xe5, 0x05, 0x00, 0x4a, 0x88, 0x56, 0x6f, - 0x54, 0xf6, 0xd0, 0x01, 0x73, 0x3a, 0xa9, 0x26, 0x29, 0x21, 0x5b, 0x8c, 0x90, 0x5b, 0x80, 0xf9, - 0x0e, 0xfa, 0x02, 0x73, 0x3f, 0x9e, 0x80, 0x99, 0x32, 0xb1, 0x6e, 0x99, 0xe6, 0xa6, 0x53, 0xc1, - 0x0d, 0xc7, 0xdc, 0x76, 0x75, 0x63, 0x0f, 0xb9, 0xc3, 0xe5, 0xe8, 0x0c, 0x8c, 0xd3, 0xa6, 0xb6, - 0xab, 0x93, 0x5d, 0x9e, 0x24, 0x35, 0x4e, 0x9b, 0xf7, 0x74, 0xb2, 0x2b, 0xaf, 0x43, 0xd2, 0x2b, - 0xbd, 0xe6, 0xa5, 0x23, 0x13, 0x5b, 0x92, 0x56, 0xd2, 0xc5, 0x0b, 0xf9, 0x0e, 0x9d, 0x58, 0xdf, - 0xb3, 0xf2, 0xac, 0x47, 0x4a, 0xd8, 0x76, 0xb6, 0x0f, 0xea, 0x48, 0x4d, 0x18, 0xe2, 0x49, 0x5e, - 0x83, 0x31, 0xd6, 0x14, 0x99, 0xb1, 0x25, 0x69, 0x25, 0x55, 0x7c, 0xa3, 0x9b, 0xbc, 0xe8, 0x9c, - 0x2d, 0xef, 0x47, 0xe5, 0x22, 0x5e, 0x92, 0x2a, 0x55, 0x6c, 0xec, 0x71, 0xdf, 0xe2, 0x3c, 0x49, - 0x8c, 0xc2, 0xdc, 0x9b, 0x83, 0x04, 0x6d, 0x6a, 0xb6, 0x63, 0xa2, 0x66, 0x66, 0x9c, 0x87, 0x44, - 0x9b, 0x9b, 0xde, 0x32, 0x97, 0x85, 0xb3, 0x9d, 0xf2, 0x13, 0x24, 0xf0, 0x57, 0x09, 0x4e, 0x96, - 0x89, 0xf5, 0xc9, 0xae, 0x4d, 0x51, 0xd5, 0x26, 0xf4, 0x8e, 0x5a, 0x2a, 0xae, 0xf6, 0xc8, 0xde, - 0x79, 0x98, 0x44, 0xae, 0x51, 0x5c, 0xd5, 0x74, 0x5e, 0x09, 0x51, 0xb1, 0x09, 0x46, 0xf4, 0xab, - 0xdd, 0x9a, 0xe2, 0xd1, 0x70, 0x8a, 0x65, 0x88, 0x39, 0x7a, 0x8d, 0x27, 0x31, 0xa9, 0xb2, 0x67, - 0x79, 0x16, 0xe2, 0xe4, 0xa0, 0x56, 0xc1, 0x55, 0x96, 0x9a, 0xa4, 0x2a, 0x56, 0xb2, 0x02, 0x09, - 0x13, 0x19, 0x76, 0x4d, 0xaf, 0x12, 0x16, 0xf3, 0xa4, 0x1a, 0xac, 0xe5, 0x79, 0x48, 0x5a, 0x3a, - 0xe1, 0xbb, 0x46, 0xc4, 0x9c, 0xb0, 0x74, 0x72, 0xdf, 0x5b, 0xe7, 0x34, 0x98, 0x6b, 0x8b, 0xc9, - 0x8f, 0xd8, 0x8b, 0xe0, 0x69, 0x28, 0x02, 0x1e, 0xe1, 0xc4, 0xd3, 0xd6, 0x08, 0x16, 0x00, 0x0c, - 0x23, 0xc8, 0xa9, 0xe8, 0x4a, 0x8f, 0xc2, 0xb3, 0xfa, 0x97, 0x04, 0xa7, 0x79, 0x5a, 0x1f, 0x36, - 0xe8, 0xf1, 0xfb, 0x6e, 0x06, 0xc6, 0x1c, 0xec, 0x18, 0x88, 0x25, 0x2b, 0xa6, 0xf2, 0x45, 0x6b, - 0x37, 0xc6, 0x42, 0xdd, 0xf8, 0xff, 0x74, 0xd2, 0xfb, 0xb0, 0xd0, 0x31, 0xe4, 0x20, 0xb1, 0x0b, - 0x00, 0x36, 0xd1, 0x5c, 0x54, 0xc3, 0xfb, 0xc8, 0x64, 0xd1, 0x27, 0xd4, 0xa4, 0x4d, 0x54, 0x4e, - 0xc8, 0x21, 0xc8, 0x94, 0x89, 0xc5, 0x57, 0xff, 0x5d, 0xd6, 0x72, 0x39, 0x58, 0xea, 0x66, 0x26, - 0x68, 0xfa, 0x9f, 0x25, 0x98, 0x2a, 0x13, 0xeb, 0x63, 0x4c, 0xd1, 0x86, 0x4e, 0xb6, 0x5c, 0xdb, - 0x40, 0x43, 0xbb, 0x50, 0xf7, 0xa4, 0x7d, 0x17, 0xd8, 0x42, 0x3e, 0x07, 0x13, 0x75, 0xd7, 0xc6, - 0xae, 0x4d, 0x0f, 0xb4, 0x1d, 0x84, 0x58, 0x96, 0x63, 0x6a, 0xca, 0xa7, 0xdd, 0x45, 0x8c, 0x85, - 0x97, 0xc1, 0x69, 0xd4, 0x2a, 0xc8, 0x65, 0x05, 0x8e, 0xa9, 0x29, 0x46, 0x7b, 0xc0, 0x48, 0x1f, - 0xc6, 0x12, 0x63, 0xd3, 0xf1, 0xdc, 0x1c, 0x9c, 0x89, 0x78, 0x1a, 0x44, 0xf1, 0x53, 0x3c, 0x88, - 0xc2, 0x0f, 0xb4, 0x47, 0x14, 0xf3, 0xc0, 0xfa, 0x97, 0xd7, 0x9d, 0x37, 0x74, 0xc2, 0x23, 0xb0, - 0xb2, 0x5f, 0x85, 0x59, 0x5c, 0x21, 0xc8, 0xdd, 0x47, 0xa6, 0x86, 0x85, 0xae, 0xd6, 0x73, 0x70, - 0xc6, 0x7f, 0xeb, 0x1b, 0x62, 0x52, 0x25, 0xc8, 0xb6, 0x4b, 0x89, 0xee, 0x42, 0xb6, 0xb5, 0x4b, - 0x45, 0x58, 0xf3, 0x51, 0xe9, 0x75, 0xd6, 0x6f, 0x8c, 0x45, 0xbe, 0x01, 0x4a, 0xbb, 0x12, 0x6f, - 0x6b, 0x37, 0x08, 0x32, 0x33, 0xc0, 0x14, 0x9c, 0x89, 0x2a, 0xd8, 0xd0, 0xc9, 0x63, 0x82, 0x4c, - 0xf9, 0x4b, 0x09, 0x2e, 0xb4, 0x4b, 0xa3, 0x9d, 0x1d, 0x64, 0x50, 0x7b, 0x1f, 0x31, 0x3d, 0xbc, - 0x40, 0x29, 0x36, 0xf4, 0xf2, 0x62, 0xe8, 0x2d, 0x0f, 0x30, 0xf4, 0x36, 0x1d, 0xaa, 0x9e, 0x8b, - 0x1a, 0xbe, 0xe3, 0xab, 0x0e, 0xfa, 0x66, 0xab, 0xbf, 0x07, 0xfc, 0x90, 0x9a, 0x60, 0xa1, 0xf4, - 0xd4, 0xc8, 0x4e, 0x2f, 0x19, 0x43, 0x7a, 0x5f, 0xaf, 0x36, 0x90, 0xe6, 0x22, 0x03, 0xd9, 0xde, - 0x5e, 0x62, 0xc7, 0xe2, 0xfa, 0xbd, 0x23, 0x4e, 0xec, 0xbf, 0x5f, 0x2e, 0x9e, 0x3e, 0xd0, 0x6b, - 0xd5, 0xb5, 0x5c, 0x58, 0x5d, 0x4e, 0x9d, 0x64, 0x04, 0x55, 0xac, 0xe5, 0xdb, 0x10, 0x27, 0x54, - 0xa7, 0x0d, 0x7e, 0xca, 0xa6, 0x8b, 0x17, 0xbb, 0x8e, 0x36, 0x0e, 0x94, 0x84, 0xe0, 0x23, 0x26, - 0xa3, 0x0a, 0x59, 0xf9, 0x02, 0xa4, 0x83, 0xf8, 0x19, 0xa3, 0x38, 0x40, 0x26, 0x7d, 0x6a, 0xc9, - 0x23, 0xca, 0x17, 0x41, 0x0e, 0xd8, 0xbc, 0xc1, 0xcf, 0xb7, 0x70, 0x82, 0x25, 0x67, 0xda, 0x7f, - 0xb3, 0x4d, 0xc8, 0x03, 0x76, 0x06, 0x86, 0x06, 0x6f, 0x72, 0xa8, 0xc1, 0xdb, 0xb2, 0x85, 0xfc, - 0x9c, 0x07, 0x5b, 0xe8, 0x59, 0x0c, 0xd2, 0xe2, 0x9d, 0x98, 0x8f, 0x3d, 0x76, 0x90, 0x37, 0xa6, - 0x90, 0x63, 0x22, 0x57, 0x6c, 0x1f, 0xb1, 0x92, 0x97, 0x61, 0x8a, 0x3f, 0x69, 0x91, 0xa1, 0x37, - 0xc9, 0xc9, 0x25, 0x71, 0x58, 0x28, 0x90, 0x10, 0x25, 0x70, 0xc5, 0x81, 0x1e, 0xac, 0xbd, 0xe4, - 0xf9, 0xcf, 0x22, 0x79, 0x63, 0x5c, 0x85, 0x4f, 0xe5, 0xc9, 0x3b, 0x04, 0x71, 0xf1, 0x63, 0x81, - 0x38, 0x2f, 0xca, 0x1a, 0x22, 0x44, 0xb7, 0x78, 0xea, 0x93, 0xaa, 0xbf, 0xf4, 0x4e, 0x26, 0xdb, - 0x69, 0x39, 0x00, 0x92, 0xec, 0x75, 0x4a, 0xd0, 0xd8, 0xbe, 0x5f, 0x85, 0x19, 0x9f, 0x25, 0xb4, - 0xdb, 0xf9, 0x66, 0x95, 0xc5, 0xbb, 0xd6, 0x4d, 0x1e, 0x9a, 0xd6, 0x29, 0xc6, 0x16, 0x4c, 0xeb, - 0x70, 0x8d, 0x27, 0x86, 0x03, 0x57, 0xf3, 0x90, 0xa4, 0x4d, 0x0d, 0xbb, 0xb6, 0x65, 0x3b, 0x99, - 0x49, 0x9e, 0x5c, 0xda, 0x7c, 0xc8, 0xd6, 0xde, 0x29, 0xad, 0x13, 0x82, 0x68, 0x26, 0xcd, 0x5e, - 0xf0, 0x85, 0xbc, 0x08, 0x29, 0xb4, 0x8f, 0x1c, 0x2a, 0xa6, 0xdd, 0x14, 0xf3, 0x0a, 0x18, 0x89, - 0x0f, 0xbc, 0x0c, 0xcc, 0x86, 0x7b, 0x23, 0x68, 0x9b, 0xfb, 0x0c, 0x33, 0xdd, 0xaa, 0x60, 0x97, - 0x3e, 0xa2, 0x0d, 0x63, 0xaf, 0x54, 0xda, 0xfe, 0xb4, 0x37, 0xc4, 0xed, 0x05, 0x26, 0xe6, 0x19, - 0x5a, 0x09, 0x6b, 0x0b, 0x4c, 0xed, 0x33, 0x7c, 0xab, 0xa2, 0x9d, 0x86, 0x63, 0x32, 0x16, 0x64, - 0x1e, 0xcb, 0x1a, 0xef, 0x34, 0x4f, 0x5b, 0x80, 0x7f, 0xf8, 0x11, 0x3f, 0xc9, 0xa9, 0x02, 0x00, - 0x09, 0xdc, 0xd8, 0x66, 0xf7, 0x70, 0xe7, 0x48, 0xcc, 0x6b, 0x0e, 0xcc, 0x55, 0x9d, 0xa2, 0xfb, - 0xfc, 0xfe, 0x72, 0xd7, 0xbb, 0xbe, 0xf4, 0xf0, 0xce, 0x00, 0xb9, 0xfd, 0xba, 0xc3, 0xbc, 0x4c, - 0x15, 0x0b, 0xf9, 0x9e, 0x97, 0xbb, 0x7c, 0xd4, 0xcc, 0x7a, 0xcc, 0x6b, 0x7f, 0x75, 0xda, 0x8d, - 0xd0, 0x73, 0xe7, 0xe1, 0x5c, 0x57, 0xdf, 0x82, 0x08, 0xfe, 0x94, 0xd8, 0xd5, 0x42, 0x5c, 0x64, - 0x18, 0x46, 0x2c, 0x35, 0x08, 0xc5, 0xe6, 0xc1, 0x31, 0x6e, 0x59, 0x79, 0x38, 0xe5, 0xa0, 0x27, - 0x9a, 0xc1, 0x15, 0x45, 0x52, 0x7c, 0xd2, 0x41, 0x4f, 0x84, 0x09, 0x1f, 0x67, 0xb6, 0xc1, 0xe9, - 0x58, 0x07, 0x38, 0x7d, 0xb8, 0xeb, 0xc7, 0x8e, 0x77, 0x75, 0xbb, 0x0d, 0xe7, 0x7b, 0x44, 0xdc, - 0x0a, 0xe4, 0x5a, 0x3a, 0x48, 0x8a, 0xf6, 0x6b, 0x8d, 0x21, 0x2c, 0x9e, 0xdd, 0x56, 0x25, 0x5b, - 0x7a, 0x83, 0x88, 0xa1, 0x30, 0x3c, 0x9a, 0xf2, 0x74, 0xb0, 0x74, 0x25, 0x54, 0xbe, 0xc8, 0x6d, - 0xc2, 0x4a, 0x3f, 0x73, 0x03, 0x7a, 0x5e, 0xfc, 0x27, 0x0d, 0xa3, 0x65, 0x62, 0xc9, 0xdf, 0x48, - 0x20, 0x77, 0xc0, 0xee, 0x57, 0xfb, 0xf4, 0x5f, 0x47, 0xf8, 0xab, 0xbc, 0x37, 0x8c, 0x54, 0xe0, - 0xf1, 0xd7, 0x12, 0x9c, 0x6c, 0xbf, 0xbd, 0x5e, 0x19, 0x48, 0x67, 0x58, 0x48, 0xb9, 0x31, 0x84, - 0x50, 0xe0, 0xc7, 0x77, 0x12, 0x9c, 0xee, 0x8c, 0xcd, 0xdf, 0xe9, 0xaf, 0xb6, 0xa3, 0xa0, 0x72, - 0x73, 0x48, 0xc1, 0xc0, 0xa7, 0x7d, 0x98, 0x08, 0x41, 0xf4, 0x7c, 0x7f, 0x85, 0xad, 0xfc, 0xca, - 0xb5, 0xa3, 0xf1, 0x47, 0xed, 0x06, 0xa0, 0x7a, 0x40, 0xbb, 0x3e, 0xff, 0xa0, 0x76, 0xa3, 0x68, - 0x44, 0x26, 0x90, 0x6a, 0x45, 0x22, 0x97, 0x06, 0x53, 0x23, 0xd8, 0x95, 0xb7, 0x8f, 0xc4, 0x1e, - 0x18, 0xfd, 0x1c, 0xd2, 0x91, 0xcb, 0xff, 0x6a, 0x7f, 0x45, 0x61, 0x09, 0xe5, 0xfa, 0x51, 0x25, - 0x02, 0xeb, 0x5f, 0x49, 0x30, 0xdd, 0xf6, 0xb1, 0xa8, 0xd8, 0x5f, 0x5d, 0x54, 0x46, 0x59, 0x3b, - 0xba, 0x4c, 0xe0, 0xc4, 0x17, 0x30, 0x15, 0xfd, 0xc4, 0x76, 0xb9, 0xbf, 0xba, 0x88, 0x88, 0xf2, - 0xee, 0x91, 0x45, 0x5a, 0x6b, 0x10, 0x01, 0x13, 0x03, 0xd4, 0x20, 0x2c, 0x31, 0x48, 0x0d, 0x3a, - 0x43, 0x0c, 0x76, 0x04, 0xb5, 0x03, 0x8c, 0x2b, 0x83, 0xec, 0xde, 0x88, 0xd0, 0x20, 0x47, 0x50, - 0x57, 0x48, 0x21, 0xff, 0x20, 0xc1, 0x6c, 0x17, 0x3c, 0x71, 0x7d, 0xd0, 0xea, 0x46, 0x25, 0x95, - 0x0f, 0x86, 0x95, 0x0c, 0xdc, 0x7a, 0x26, 0x41, 0xa6, 0x2b, 0x48, 0x58, 0x1b, 0xb8, 0xe8, 0x6d, - 0xb2, 0xca, 0xfa, 0xf0, 0xb2, 0x81, 0x73, 0xbf, 0x48, 0xb0, 0xd0, 0x7b, 0x12, 0xdf, 0x1c, 0x34, - 0x01, 0x5d, 0x14, 0x28, 0x1b, 0xc7, 0x54, 0xe0, 0xfb, 0xba, 0xfe, 0xd1, 0xf3, 0x57, 0x59, 0xe9, - 0xc5, 0xab, 0xac, 0xf4, 0xc7, 0xab, 0xac, 0xf4, 0xed, 0xeb, 0xec, 0xc8, 0x8b, 0xd7, 0xd9, 0x91, - 0xdf, 0x5e, 0x67, 0x47, 0x3e, 0xbb, 0xdc, 0x02, 0x64, 0x3c, 0x13, 0x97, 0x22, 0xdf, 0xca, 0x9b, - 0xa1, 0x7f, 0x05, 0x78, 0xb8, 0xa6, 0x12, 0x67, 0x5f, 0xc8, 0xaf, 0xfc, 0x1b, 0x00, 0x00, 0xff, - 0xff, 0x00, 0xf4, 0xf1, 0x8e, 0x38, 0x18, 0x00, 0x00, + // 1779 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xcd, 0x6f, 0xdb, 0xc8, + 0x15, 0x37, 0xd7, 0xb2, 0x2c, 0x3d, 0xd9, 0xb2, 0xcd, 0x75, 0x6c, 0x99, 0x5e, 0xcb, 0x8e, 0xd2, + 0xb8, 0x46, 0x91, 0x48, 0x5e, 0x65, 0x9b, 0x6e, 0xbd, 0x45, 0xb7, 0xb1, 0x76, 0xe3, 0x75, 0x1b, + 0x25, 0x06, 0xd7, 0xd9, 0x7e, 0x5c, 0x08, 0x8a, 0x1c, 0xd3, 0x84, 0x25, 0x8e, 0xc0, 0x19, 0x29, + 0x72, 0x50, 0xa0, 0x45, 0x81, 0x02, 0x3d, 0xb6, 0x45, 0x4f, 0x39, 0xf4, 0x56, 0xa0, 0xfd, 0x4f, + 0x72, 0x0c, 0x7a, 0x2a, 0x7a, 0x08, 0x8a, 0xe4, 0xd4, 0x5b, 0xdb, 0x6b, 0x2f, 0x05, 0xdf, 0x0c, + 0x19, 0x89, 0xfa, 0xb4, 0x8c, 0x62, 0x2f, 0x16, 0xe7, 0xf1, 0xfd, 0xde, 0xbc, 0xaf, 0x99, 0xf7, + 0x1e, 0x0d, 0xbb, 0xcf, 0x09, 0x37, 0xad, 0x73, 0xd3, 0xf5, 0x4a, 0xf8, 0x44, 0x7d, 0x52, 0xb2, + 0x7c, 0xca, 0x98, 0xa0, 0xf1, 0x4e, 0xb1, 0xe9, 0x53, 0x4e, 0xd5, 0xad, 0x88, 0xaf, 0x18, 0xf2, + 0x15, 0xdf, 0xf1, 0x69, 0xab, 0x0e, 0x75, 0x28, 0x72, 0x96, 0x82, 0x27, 0x01, 0xd2, 0xbe, 0x35, + 0x40, 0x78, 0xf3, 0xc2, 0x29, 0x21, 0x89, 0xc9, 0x1f, 0xc9, 0xbb, 0x3b, 0x8c, 0x97, 0xba, 0x1e, + 0xfe, 0x19, 0x23, 0xb3, 0xe9, 0x53, 0x7a, 0xc6, 0xe4, 0x8f, 0xe4, 0xbd, 0x3f, 0xda, 0x38, 0xdf, + 0xe4, 0xc4, 0xa8, 0xbb, 0x0d, 0x97, 0x13, 0xdf, 0x38, 0xab, 0x9b, 0x4e, 0x88, 0x2b, 0x8f, 0xc6, + 0xe1, 0xa3, 0x81, 0xcf, 0x46, 0xe8, 0xa0, 0xc2, 0xef, 0x15, 0x50, 0xab, 0xcc, 0xa9, 0xba, 0x4e, + 0x20, 0xf6, 0x94, 0xb1, 0x87, 0x2d, 0xcf, 0x66, 0x6a, 0x0e, 0xe6, 0x2d, 0x9f, 0x98, 0x9c, 0xfa, + 0x39, 0x65, 0x47, 0xd9, 0x4b, 0xeb, 0xe1, 0x52, 0xdd, 0x80, 0x94, 0x10, 0xe1, 0xda, 0xb9, 0xf7, + 0x76, 0x94, 0xbd, 0x59, 0x7d, 0x1e, 0xd7, 0xc7, 0xb6, 0x7a, 0x04, 0x49, 0xb3, 0x41, 0x5b, 0x1e, + 0xcf, 0xcd, 0x06, 0x98, 0xc3, 0xd2, 0xcb, 0xd7, 0xdb, 0x33, 0x7f, 0x7f, 0xbd, 0xfd, 0x4d, 0xc7, + 0xe5, 0xe7, 0xad, 0x5a, 0xd1, 0xa2, 0x8d, 0x92, 0x45, 0x59, 0x83, 0x32, 0xf9, 0x73, 0x97, 0xd9, + 0x17, 0x25, 0x7e, 0xd9, 0x24, 0xac, 0xf8, 0xd4, 0xf5, 0xb8, 0x2e, 0xe1, 0x85, 0x0f, 0x40, 0xeb, + 0xd7, 0x49, 0x27, 0xac, 0x49, 0x3d, 0x46, 0x0a, 0x8f, 0xe1, 0xfd, 0x2a, 0x73, 0x9e, 0x36, 0x6d, + 0xf1, 0xf2, 0x81, 0x6d, 0xfb, 0x84, 0x8d, 0x52, 0x79, 0x0b, 0x80, 0x33, 0x66, 0x34, 0x5b, 0xb5, + 0x0b, 0x72, 0x89, 0x4a, 0xa7, 0xf5, 0x34, 0x67, 0xec, 0x04, 0x09, 0x85, 0x2d, 0xd8, 0x1c, 0x20, + 0x2f, 0xda, 0xee, 0x8f, 0xef, 0xc1, 0x6a, 0x95, 0x39, 0x0f, 0x6c, 0xfb, 0xd8, 0xab, 0xd1, 0x96, + 0x67, 0x9f, 0xfa, 0xa6, 0x75, 0x41, 0xfc, 0xe9, 0x7c, 0xb4, 0x0e, 0xf3, 0xbc, 0x63, 0x9c, 0x9b, + 0xec, 0x5c, 0x38, 0x49, 0x4f, 0xf2, 0xce, 0x17, 0x26, 0x3b, 0x57, 0x0f, 0x21, 0x1d, 0xa4, 0x8b, + 0x11, 0xb8, 0x23, 0x97, 0xd8, 0x51, 0xf6, 0xb2, 0xe5, 0xdb, 0xc5, 0x01, 0xd9, 0xdb, 0xbc, 0x70, + 0x8a, 0x98, 0x57, 0x15, 0xea, 0x7a, 0xa7, 0x97, 0x4d, 0xa2, 0xa7, 0x2c, 0xf9, 0xa4, 0x1e, 0xc0, + 0x1c, 0x26, 0x52, 0x6e, 0x6e, 0x47, 0xd9, 0xcb, 0x94, 0xbf, 0x31, 0x0c, 0x2f, 0xb3, 0xed, 0x24, + 0xf8, 0xd1, 0x05, 0x24, 0x70, 0x52, 0xad, 0x4e, 0xad, 0x0b, 0xa1, 0x5b, 0x52, 0x38, 0x09, 0x29, + 0xa8, 0xde, 0x06, 0xa4, 0x78, 0xc7, 0x70, 0x3d, 0x9b, 0x74, 0x72, 0xf3, 0xc2, 0x24, 0xde, 0x39, + 0x0e, 0x96, 0x85, 0x3c, 0x7c, 0x30, 0xc8, 0x3f, 0x91, 0x03, 0xff, 0xaa, 0xc0, 0x4a, 0x95, 0x39, + 0x3f, 0x3e, 0x77, 0x39, 0xa9, 0xbb, 0x8c, 0x7f, 0xae, 0x57, 0xca, 0xfb, 0x23, 0xbc, 0x77, 0x0b, + 0x16, 0x89, 0x6f, 0x95, 0xf7, 0x0d, 0x53, 0x44, 0x42, 0x46, 0x6c, 0x01, 0x89, 0x61, 0xb4, 0xbb, + 0x5d, 0x3c, 0xdb, 0xeb, 0x62, 0x15, 0x12, 0x9e, 0xd9, 0x10, 0x4e, 0x4c, 0xeb, 0xf8, 0xac, 0xae, + 0x41, 0x92, 0x5d, 0x36, 0x6a, 0xb4, 0x8e, 0xae, 0x49, 0xeb, 0x72, 0xa5, 0x6a, 0x90, 0xb2, 0x89, + 0xe5, 0x36, 0xcc, 0x3a, 0x43, 0x9b, 0x17, 0xf5, 0x68, 0xad, 0x6e, 0x42, 0xda, 0x31, 0x99, 0x38, + 0x69, 0xd2, 0xe6, 0x94, 0x63, 0xb2, 0x47, 0xc1, 0xba, 0x60, 0xc0, 0x46, 0x9f, 0x4d, 0xa1, 0xc5, + 0x81, 0x05, 0xcf, 0x7b, 0x2c, 0x10, 0x16, 0x2e, 0x3c, 0xef, 0xb6, 0x60, 0x0b, 0xc0, 0xb2, 0x22, + 0x9f, 0xca, 0xac, 0x0c, 0x28, 0xc2, 0xab, 0xff, 0x56, 0xe0, 0x86, 0x70, 0xeb, 0x93, 0x16, 0xbf, + 0x7e, 0xde, 0xad, 0xc2, 0x9c, 0x47, 0x3d, 0x8b, 0xa0, 0xb3, 0x12, 0xba, 0x58, 0x74, 0x67, 0x63, + 0xa2, 0x27, 0x1b, 0xbf, 0x9e, 0x4c, 0xfa, 0x3e, 0x6c, 0x0d, 0x34, 0x39, 0x72, 0xec, 0x16, 0x80, + 0xcb, 0x0c, 0x9f, 0x34, 0x68, 0x9b, 0xd8, 0x68, 0x7d, 0x4a, 0x4f, 0xbb, 0x4c, 0x17, 0x84, 0x02, + 0x81, 0x5c, 0x95, 0x39, 0x62, 0xf5, 0xff, 0xf3, 0x5a, 0xa1, 0x00, 0x3b, 0xc3, 0xb6, 0x89, 0x92, + 0xfe, 0xcf, 0x0a, 0x2c, 0x55, 0x99, 0xf3, 0x15, 0xe5, 0xe4, 0xc8, 0x64, 0x27, 0xbe, 0x6b, 0x91, + 0xa9, 0x55, 0x68, 0x06, 0xe8, 0x50, 0x05, 0x5c, 0xa8, 0x37, 0x61, 0xa1, 0xe9, 0xbb, 0xd4, 0x77, + 0xf9, 0xa5, 0x71, 0x46, 0x08, 0x7a, 0x39, 0xa1, 0x67, 0x42, 0xda, 0x43, 0x82, 0x2c, 0x22, 0x0c, + 0x5e, 0xab, 0x51, 0x23, 0x3e, 0x06, 0x38, 0xa1, 0x67, 0x90, 0xf6, 0x18, 0x49, 0x3f, 0x4c, 0xa4, + 0xe6, 0x96, 0x93, 0x85, 0x0d, 0x58, 0x8f, 0x69, 0x1a, 0x59, 0xf1, 0xa7, 0x64, 0x64, 0x45, 0x68, + 0xe8, 0x08, 0x2b, 0x36, 0x01, 0xf3, 0x57, 0xc4, 0x5d, 0x24, 0x74, 0x2a, 0x20, 0x60, 0xd8, 0x3f, + 0x82, 0x35, 0x5a, 0x63, 0xc4, 0x6f, 0x13, 0xdb, 0xa0, 0x52, 0x56, 0xf7, 0x3d, 0xb8, 0x1a, 0xbe, + 0x0d, 0x37, 0x42, 0x54, 0x05, 0xf2, 0xfd, 0x28, 0x99, 0x5d, 0xc4, 0x75, 0xce, 0xb9, 0x34, 0x6b, + 0x33, 0x8e, 0x3e, 0xc4, 0x7c, 0x43, 0x16, 0xf5, 0x13, 0xd0, 0xfa, 0x85, 0x04, 0x47, 0xbb, 0xc5, + 0x88, 0x9d, 0x03, 0x14, 0xb0, 0x1e, 0x17, 0x70, 0x64, 0xb2, 0xa7, 0x8c, 0xd8, 0xea, 0x2f, 0x15, + 0xb8, 0xdd, 0x8f, 0x26, 0x67, 0x67, 0xc4, 0xe2, 0x6e, 0x9b, 0xa0, 0x1c, 0x11, 0xa0, 0x0c, 0x16, + 0xbd, 0xa2, 0x2c, 0x7a, 0xbb, 0x13, 0x14, 0xbd, 0x63, 0x8f, 0xeb, 0x37, 0xe3, 0x1b, 0x7f, 0x1e, + 0x8a, 0x8e, 0xf2, 0xe6, 0x64, 0xbc, 0x06, 0xe2, 0x92, 0x5a, 0x40, 0x53, 0x46, 0x4a, 0xc4, 0xdb, + 0x4b, 0xa5, 0x90, 0x6d, 0x9b, 0xf5, 0x16, 0x31, 0x7c, 0x62, 0x11, 0x37, 0x38, 0x4b, 0x78, 0x2d, + 0x1e, 0x7e, 0x71, 0xc5, 0x8a, 0xfd, 0x9f, 0xd7, 0xdb, 0x37, 0x2e, 0xcd, 0x46, 0xfd, 0xa0, 0xd0, + 0x2b, 0xae, 0xa0, 0x2f, 0x22, 0x41, 0x97, 0x6b, 0xf5, 0x33, 0x48, 0x32, 0x6e, 0xf2, 0x96, 0xb8, + 0x65, 0xb3, 0xe5, 0x3b, 0x43, 0x4b, 0x9b, 0x68, 0xae, 0x24, 0xf0, 0x4b, 0xc4, 0xe8, 0x12, 0xab, + 0xde, 0x86, 0x6c, 0x64, 0x3f, 0x32, 0xca, 0x0b, 0x64, 0x31, 0xa4, 0x56, 0x02, 0xa2, 0x7a, 0x07, + 0xd4, 0x88, 0x2d, 0x28, 0xfc, 0xe2, 0x08, 0xa7, 0xd0, 0x39, 0xcb, 0xe1, 0x9b, 0x53, 0xc6, 0x1e, + 0xe3, 0x1d, 0xd8, 0x53, 0x78, 0xd3, 0x53, 0x15, 0xde, 0xae, 0x23, 0x14, 0xfa, 0x3c, 0x3a, 0x42, + 0xff, 0x9c, 0x83, 0xac, 0x7c, 0x27, 0xeb, 0xe3, 0x88, 0x13, 0x14, 0x94, 0x29, 0xe2, 0xd9, 0xc4, + 0x97, 0xc7, 0x47, 0xae, 0xd4, 0x5d, 0x58, 0x12, 0x4f, 0x46, 0xac, 0xe8, 0x2d, 0x0a, 0x72, 0x45, + 0x5e, 0x16, 0x1a, 0xa4, 0x64, 0x08, 0x7c, 0x79, 0xa1, 0x47, 0xeb, 0xc0, 0x79, 0xe1, 0xb3, 0x74, + 0xde, 0x9c, 0x10, 0x11, 0x52, 0x85, 0xf3, 0xde, 0x35, 0x71, 0xc9, 0x6b, 0x35, 0x71, 0x81, 0x95, + 0x0d, 0xc2, 0x98, 0xe9, 0x08, 0xd7, 0xa7, 0xf5, 0x70, 0x19, 0xdc, 0x4c, 0xae, 0xd7, 0x75, 0x01, + 0xa4, 0xf1, 0x75, 0x46, 0xd2, 0xf0, 0xdc, 0xef, 0xc3, 0x6a, 0xc8, 0xd2, 0x73, 0xda, 0xc5, 0x61, + 0x55, 0xe5, 0xbb, 0xee, 0x43, 0xde, 0x53, 0xad, 0x33, 0xc8, 0x16, 0x55, 0xeb, 0xde, 0x18, 0x2f, + 0x4c, 0xd7, 0x5c, 0x6d, 0x42, 0x9a, 0x77, 0x0c, 0xea, 0xbb, 0x8e, 0xeb, 0xe5, 0x16, 0x85, 0x73, + 0x79, 0xe7, 0x09, 0xae, 0x83, 0x5b, 0xda, 0x64, 0x8c, 0xf0, 0x5c, 0x16, 0x5f, 0x88, 0x85, 0xba, + 0x0d, 0x19, 0xd2, 0x26, 0x1e, 0x97, 0xd5, 0x6e, 0x09, 0xb5, 0x02, 0x24, 0x61, 0xc1, 0x53, 0x7d, + 0xd8, 0xc0, 0x36, 0xdc, 0xa2, 0x75, 0xc3, 0xa2, 0x1e, 0xf7, 0x4d, 0x8b, 0x1b, 0x6d, 0xe2, 0x33, + 0x97, 0x7a, 0xb9, 0x65, 0xd4, 0xf3, 0x7e, 0x71, 0xe4, 0x08, 0x13, 0x94, 0x5e, 0xc4, 0x57, 0x24, + 0xfc, 0x2b, 0x81, 0xd6, 0xd7, 0x9b, 0x83, 0x5f, 0xa8, 0x3f, 0x0d, 0xf2, 0xa0, 0x4d, 0x7c, 0x6e, + 0xd0, 0x26, 0x77, 0xa9, 0xc7, 0x72, 0x2b, 0x58, 0xe3, 0xef, 0x8c, 0xd9, 0x48, 0x47, 0xd0, 0x13, + 0x81, 0x39, 0x4c, 0x04, 0x69, 0x11, 0xe4, 0x4e, 0x17, 0xb1, 0x90, 0x83, 0xb5, 0xde, 0x54, 0x8f, + 0x4e, 0xc1, 0x23, 0x6c, 0x01, 0x1f, 0xd4, 0xa8, 0xcf, 0xbf, 0xe4, 0x2d, 0xeb, 0xa2, 0x52, 0x39, + 0xfd, 0xc9, 0xe8, 0x8e, 0x7d, 0x54, 0x6f, 0xb4, 0x89, 0xcd, 0x57, 0xaf, 0xb4, 0x68, 0xab, 0x36, + 0xb6, 0xeb, 0x3a, 0x39, 0x6b, 0x79, 0x36, 0xb2, 0x10, 0xfb, 0x5a, 0xbb, 0x89, 0x83, 0x13, 0x48, + 0x8b, 0xda, 0x39, 0x51, 0xb1, 0x16, 0x05, 0x55, 0xf6, 0x73, 0xb2, 0x0d, 0xee, 0xdb, 0x37, 0xd2, + 0xeb, 0x85, 0x82, 0x5a, 0x8b, 0x39, 0x43, 0x37, 0x39, 0x79, 0x24, 0x46, 0xb8, 0x87, 0xc1, 0x04, + 0x37, 0x42, 0x3b, 0x0b, 0xd4, 0xfe, 0x89, 0x0f, 0xb5, 0xcc, 0x94, 0x4b, 0xe3, 0x62, 0x16, 0xdb, + 0x46, 0x86, 0x6d, 0xd9, 0x8f, 0xd1, 0x0b, 0xb7, 0xe0, 0xe6, 0x50, 0xdd, 0x22, 0x0b, 0xfe, 0xa5, + 0xe0, 0xa4, 0x24, 0xe7, 0x32, 0x6c, 0x79, 0x2b, 0x2d, 0xc6, 0xa9, 0x7d, 0x79, 0x8d, 0xa1, 0xb1, + 0x08, 0xef, 0x7b, 0xe4, 0x99, 0x61, 0x09, 0x41, 0x31, 0x17, 0xaf, 0x78, 0xe4, 0x99, 0xdc, 0x22, + 0x6c, 0x9b, 0xfb, 0xa6, 0x83, 0xc4, 0x80, 0xe9, 0xe0, 0xdd, 0x25, 0x36, 0x77, 0xbd, 0x49, 0xf4, + 0x33, 0xb8, 0x35, 0xc2, 0xe2, 0xee, 0xbe, 0xb4, 0x2b, 0x83, 0x94, 0x78, 0xbe, 0x36, 0xb0, 0x61, + 0x14, 0xde, 0xed, 0x16, 0x72, 0x62, 0xb6, 0x98, 0xac, 0x71, 0xd3, 0x37, 0x87, 0x81, 0x0c, 0x74, + 0x57, 0x4a, 0x17, 0x8b, 0xc2, 0x31, 0xec, 0x8d, 0xdb, 0x6e, 0x42, 0xcd, 0xcb, 0xff, 0xcd, 0xc2, + 0x6c, 0x95, 0x39, 0xea, 0x6f, 0x14, 0x50, 0x07, 0x8c, 0x22, 0x1f, 0x8d, 0xc9, 0xbf, 0x81, 0xdd, + 0xbc, 0xf6, 0xbd, 0x69, 0x50, 0x91, 0xc6, 0xbf, 0x56, 0x60, 0xa5, 0x7f, 0x18, 0xbf, 0x37, 0x91, + 0xcc, 0x5e, 0x90, 0xf6, 0xc9, 0x14, 0xa0, 0x48, 0x8f, 0xdf, 0x29, 0x70, 0x63, 0xf0, 0xa8, 0xf1, + 0x9d, 0xf1, 0x62, 0x07, 0x02, 0xb5, 0x4f, 0xa7, 0x04, 0x46, 0x3a, 0xb5, 0x61, 0xa1, 0x67, 0xe2, + 0x28, 0x8e, 0x17, 0xd8, 0xcd, 0xaf, 0xdd, 0xbf, 0x1a, 0x7f, 0x7c, 0xdf, 0x68, 0x46, 0x98, 0x70, + 0xdf, 0x90, 0x7f, 0xd2, 0x7d, 0xe3, 0xcd, 0x95, 0xca, 0x20, 0xd3, 0xdd, 0x58, 0xdd, 0x9d, 0x4c, + 0x8c, 0x64, 0xd7, 0xbe, 0x7d, 0x25, 0xf6, 0x68, 0xd3, 0x9f, 0x43, 0x36, 0xf6, 0x2d, 0x63, 0x7f, + 0xbc, 0xa0, 0x5e, 0x84, 0xf6, 0xf1, 0x55, 0x11, 0xd1, 0xee, 0xbf, 0x52, 0x60, 0xb9, 0xef, 0xdb, + 0x57, 0x79, 0xbc, 0xb8, 0x38, 0x46, 0x3b, 0xb8, 0x3a, 0x26, 0x52, 0xe2, 0x17, 0xb0, 0x14, 0xff, + 0x62, 0xf8, 0xe1, 0x78, 0x71, 0x31, 0x88, 0xf6, 0xdd, 0x2b, 0x43, 0xba, 0x63, 0x10, 0x6b, 0x26, + 0x26, 0x88, 0x41, 0x2f, 0x62, 0x92, 0x18, 0x0c, 0x6e, 0x31, 0xf0, 0x0a, 0xea, 0x6f, 0x30, 0xee, + 0x4d, 0x72, 0x7a, 0x63, 0xa0, 0x49, 0xae, 0xa0, 0xa1, 0x2d, 0x85, 0xfa, 0x07, 0x05, 0xd6, 0x86, + 0xf4, 0x13, 0x1f, 0x4f, 0x1a, 0xdd, 0x38, 0x52, 0xfb, 0xc1, 0xb4, 0xc8, 0x48, 0xad, 0x17, 0x0a, + 0xe4, 0x86, 0x36, 0x09, 0x07, 0x13, 0x07, 0xbd, 0x0f, 0xab, 0x1d, 0x4e, 0x8f, 0x8d, 0x94, 0xfb, + 0x8b, 0x02, 0x5b, 0xa3, 0x2b, 0xf1, 0xa7, 0x93, 0x3a, 0x60, 0x88, 0x00, 0xed, 0xe8, 0x9a, 0x02, + 0x42, 0x5d, 0x0f, 0x7f, 0xf4, 0xf2, 0x4d, 0x5e, 0x79, 0xf5, 0x26, 0xaf, 0xfc, 0xe3, 0x4d, 0x5e, + 0xf9, 0xed, 0xdb, 0xfc, 0xcc, 0xab, 0xb7, 0xf9, 0x99, 0xbf, 0xbd, 0xcd, 0xcf, 0xfc, 0xec, 0xc3, + 0xae, 0x46, 0x26, 0xd8, 0xe2, 0x6e, 0xec, 0xb3, 0x7f, 0xa7, 0xe7, 0xbf, 0x21, 0x41, 0x5f, 0x53, + 0x4b, 0xe2, 0x40, 0x70, 0xef, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x41, 0xaf, 0x76, 0x5a, 0x3b, + 0x19, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3052,6 +3076,25 @@ func (m *MsgVoteInbound) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.RevertOptions.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + if m.ProtocolContractVersion != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ProtocolContractVersion)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } if m.EventIndex != 0 { i = encodeVarintTx(dAtA, i, uint64(m.EventIndex)) i-- @@ -3896,6 +3939,11 @@ func (m *MsgVoteInbound) Size() (n int) { if m.EventIndex != 0 { n += 1 + sovTx(uint64(m.EventIndex)) } + if m.ProtocolContractVersion != 0 { + n += 2 + sovTx(uint64(m.ProtocolContractVersion)) + } + l = m.RevertOptions.Size() + n += 2 + l + sovTx(uint64(l)) return n } @@ -6537,6 +6585,58 @@ func (m *MsgVoteInbound) Unmarshal(dAtA []byte) error { break } } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProtocolContractVersion", wireType) + } + m.ProtocolContractVersion = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProtocolContractVersion |= ProtocolContractVersion(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RevertOptions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.RevertOptions.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:]) diff --git a/x/fungible/keeper/deposits.go b/x/fungible/keeper/deposits.go index 4af00de688..e77a8ba16b 100644 --- a/x/fungible/keeper/deposits.go +++ b/x/fungible/keeper/deposits.go @@ -6,7 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" eth "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" "github.com/zeta-chain/zetacore/pkg/coin" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" @@ -25,72 +25,99 @@ func (k Keeper) DepositCoinsToFungibleModule(ctx sdk.Context, amount *big.Int) e // ZRC20DepositAndCallContract deposits ZRC20 to the EVM account and calls the contract // returns [txResponse, isContractCall, error] -// isContractCall is true if the receiver is a contract and a contract call was made func (k Keeper) ZRC20DepositAndCallContract( ctx sdk.Context, from []byte, to eth.Address, amount *big.Int, senderChainID int64, - data []byte, + message []byte, coinType coin.CoinType, asset string, + protocolContractVersion crosschaintypes.ProtocolContractVersion, ) (*evmtypes.MsgEthereumTxResponse, bool, error) { - var ZRC20Contract eth.Address + // get ZRC20 contract + zrc20Contract, _, err := k.getAndCheckZRC20(ctx, amount, senderChainID, coinType, asset) + if err != nil { + return nil, false, err + } + + // handle the deposit for protocol contract version 2 + if protocolContractVersion == crosschaintypes.ProtocolContractVersion_V2 { + return k.ProcessV2Deposit(ctx, from, senderChainID, zrc20Contract, to, amount, message, coinType) + } + + // check if the receiver is a contract + // if it is, then the hook onCrossChainCall() will be called + // if not, the zrc20 are simply transferred to the receiver + acc := k.evmKeeper.GetAccount(ctx, to) + if acc != nil && acc.IsContract() { + context := systemcontract.ZContext{ + Origin: from, + Sender: eth.Address{}, + ChainID: big.NewInt(senderChainID), + } + res, err := k.DepositZRC20AndCallContract(ctx, context, zrc20Contract, to, amount, message) + return res, true, err + } + + // if the account is a EOC, no contract call can be made with the data + if len(message) > 0 { + return nil, false, types.ErrCallNonContract + } + + res, err := k.DepositZRC20(ctx, zrc20Contract, to, amount) + return res, false, err +} + +// getAndCheckZRC20 returns the ZRC20 contract address and foreign coin for the given chainID and asset +// it also checks if the foreign coin is paused and if the cap is reached +func (k Keeper) getAndCheckZRC20( + ctx sdk.Context, + amount *big.Int, + chainID int64, + coinType coin.CoinType, + asset string, +) (eth.Address, types.ForeignCoins, error) { + var zrc20Contract eth.Address var foreignCoin types.ForeignCoins var found bool // get foreign coin - if coinType == coin.CoinType_Gas { - foreignCoin, found = k.GetGasCoinForForeignCoin(ctx, senderChainID) + // retrieve the gas token of the chain for no asset call + // this simplify the current workflow and allow to pause calls by pausing the gas token + // TODO: refactor this logic and create specific workflow for no asset call + // https://github.com/zeta-chain/node/issues/2627 + if coinType == coin.CoinType_Gas || coinType == coin.CoinType_NoAssetCall { + foreignCoin, found = k.GetGasCoinForForeignCoin(ctx, chainID) if !found { - return nil, false, crosschaintypes.ErrGasCoinNotFound + return eth.Address{}, types.ForeignCoins{}, crosschaintypes.ErrGasCoinNotFound } } else { - foreignCoin, found = k.GetForeignCoinFromAsset(ctx, asset, senderChainID) + foreignCoin, found = k.GetForeignCoinFromAsset(ctx, asset, chainID) if !found { - return nil, false, crosschaintypes.ErrForeignCoinNotFound + return eth.Address{}, types.ForeignCoins{}, crosschaintypes.ErrForeignCoinNotFound } } - ZRC20Contract = eth.HexToAddress(foreignCoin.Zrc20ContractAddress) + zrc20Contract = eth.HexToAddress(foreignCoin.Zrc20ContractAddress) // check if foreign coin is paused if foreignCoin.Paused { - return nil, false, types.ErrPausedZRC20 + return eth.Address{}, types.ForeignCoins{}, types.ErrPausedZRC20 } // check foreign coins cap if it has a cap if !foreignCoin.LiquidityCap.IsNil() && !foreignCoin.LiquidityCap.IsZero() { liquidityCap := foreignCoin.LiquidityCap.BigInt() - totalSupply, err := k.TotalSupplyZRC4(ctx, ZRC20Contract) + totalSupply, err := k.TotalSupplyZRC4(ctx, zrc20Contract) if err != nil { - return nil, false, err + return eth.Address{}, types.ForeignCoins{}, err } newSupply := new(big.Int).Add(totalSupply, amount) if newSupply.Cmp(liquidityCap) > 0 { - return nil, false, types.ErrForeignCoinCapReached + return eth.Address{}, types.ForeignCoins{}, types.ErrForeignCoinCapReached } } - // check if the receiver is a contract - // if it is, then the hook onCrossChainCall() will be called - // if not, the zrc20 are simply transferred to the receiver - acc := k.evmKeeper.GetAccount(ctx, to) - if acc != nil && acc.IsContract() { - context := systemcontract.ZContext{ - Origin: from, - Sender: eth.Address{}, - ChainID: big.NewInt(senderChainID), - } - res, err := k.DepositZRC20AndCallContract(ctx, context, ZRC20Contract, to, amount, data) - return res, true, err - } - - // if the account is a EOC, no contract call can be made with the data - if len(data) > 0 { - return nil, false, types.ErrCallNonContract - } - - res, err := k.DepositZRC20(ctx, ZRC20Contract, to, amount) - return res, false, err + return zrc20Contract, foreignCoin, nil } diff --git a/x/fungible/keeper/deposits_test.go b/x/fungible/keeper/deposits_test.go index e394053edd..2f715bad30 100644 --- a/x/fungible/keeper/deposits_test.go +++ b/x/fungible/keeper/deposits_test.go @@ -44,6 +44,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.NoError(t, err) require.False(t, contractCall) @@ -76,6 +77,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_ERC20, assetAddress, + crosschaintypes.ProtocolContractVersion_V1, ) require.NoError(t, err) require.False(t, contractCall) @@ -108,6 +110,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte("DEADBEEF"), coin.CoinType_ERC20, assetAddress, + crosschaintypes.ProtocolContractVersion_V1, ) require.ErrorIs(t, err, types.ErrCallNonContract) }) @@ -149,6 +152,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.NoError(t, err) require.False(t, contractCall) @@ -186,6 +190,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.ErrorIs(t, err, types.ErrPausedZRC20) }) @@ -227,6 +232,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.ErrorIs(t, err, types.ErrForeignCoinCapReached) }) @@ -253,6 +259,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.ErrorIs(t, err, crosschaintypes.ErrGasCoinNotFound) }) @@ -279,6 +286,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_ERC20, assetAddress, + crosschaintypes.ProtocolContractVersion_V1, ) require.ErrorIs(t, err, crosschaintypes.ErrForeignCoinNotFound) }) @@ -309,6 +317,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.NoError(t, err) require.True(t, contractCall) @@ -347,6 +356,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { []byte{}, coin.CoinType_Gas, sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V1, ) require.Error(t, err) require.True(t, contractCall) @@ -355,6 +365,39 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { require.NoError(t, err) require.EqualValues(t, int64(0), balance.Int64()) }) + + t.Run("can deposit using V2", func(t *testing.T) { + // setup gas coin + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainList := chains.DefaultChainsList() + chain := chainList[0].ChainId + + // deploy the system contracts + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chain, "foobar", "foobar") + + // deposit + to := sample.EthAddress() + _, contractCall, err := k.ZRC20DepositAndCallContract( + ctx, + sample.EthAddress().Bytes(), + to, + big.NewInt(42), + chain, + []byte{}, + coin.CoinType_Gas, + sample.EthAddress().String(), + crosschaintypes.ProtocolContractVersion_V2, + ) + require.NoError(t, err) + require.False(t, contractCall) + + balance, err := k.BalanceOfZRC4(ctx, zrc20, to) + require.NoError(t, err) + require.Equal(t, big.NewInt(42), balance) + }) } func TestKeeper_DepositCoinZeta(t *testing.T) { diff --git a/x/fungible/keeper/evm.go b/x/fungible/keeper/evm.go index dbbae8294f..7e426d4646 100644 --- a/x/fungible/keeper/evm.go +++ b/x/fungible/keeper/evm.go @@ -20,18 +20,18 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" evmtypes "github.com/evmos/ethermint/x/evm/types" - systemcontract "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - zevmconnectorcontract "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zetaconnectorzevm.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + zevmconnectorcontract "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/zetaconnectorzevm.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-core/contracts/uniswapv2factory.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/server/config" "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // TODO USE string constant @@ -46,15 +46,15 @@ var ( func (k Keeper) DeployContract( ctx sdk.Context, metadata *bind.MetaData, - ctorArguments ...interface{}, + constructorArguments ...interface{}, ) (common.Address, error) { contractABI, err := metadata.GetAbi() if err != nil { return common.Address{}, cosmoserrors.Wrapf(types.ErrABIGet, "failed to get ABI: %s", err.Error()) } - ctorArgs, err := contractABI.Pack( - "", // function--empty string for constructor - ctorArguments..., // feeToSetter + constructorArgumentsPacked, err := contractABI.Pack( + "", // function--empty string for constructor + constructorArguments..., // feeToSetter ) if err != nil { return common.Address{}, cosmoserrors.Wrapf( @@ -78,9 +78,9 @@ func (k Keeper) DeployContract( ) } - data := make([]byte, len(bin)+len(ctorArgs)) + data := make([]byte, len(bin)+len(constructorArgumentsPacked)) copy(data[:len(bin)], bin) - copy(data[len(bin):], ctorArgs) + copy(data[len(bin):], constructorArgumentsPacked) nonce, err := k.authKeeper.GetSequence(ctx, types.ModuleAddress.Bytes()) if err != nil { @@ -112,7 +112,7 @@ func (k Keeper) DeployZRC20Contract( ) (common.Address, error) { chain, found := chains.GetChainFromChainID(chainID, k.GetAuthorityKeeper().GetAdditionalChainList(ctx)) if !found { - return common.Address{}, cosmoserrors.Wrapf(zetaObserverTypes.ErrSupportedChains, "chain %d not found", chainID) + return common.Address{}, cosmoserrors.Wrapf(observertypes.ErrSupportedChains, "chain %d not found", chainID) } // Check if Contract has already been deployed for Asset @@ -125,7 +125,17 @@ func (k Keeper) DeployZRC20Contract( if !found { return common.Address{}, cosmoserrors.Wrapf(types.ErrSystemContractNotFound, "system contract not found") } - contractAddr, err := k.DeployContract(ctx, zrc20.ZRC20MetaData, + + // deployment fails if gateway is zero address + // if the gateway is not defined in the protocol yet, use the system contract as the gateway + gateway := system.Gateway + if gateway == "" { + gateway = system.SystemContract + } + + contractAddr, err := k.DeployContract( + ctx, + zrc20.ZRC20MetaData, name, // name symbol, // symbol decimals, // decimals @@ -134,6 +144,7 @@ func (k Keeper) DeployZRC20Contract( uint8(coinType), // coinType: 0: Zeta 1: gas 2 ERC20 gasLimit, //gas limit for transfer; 21k for gas asset; around 70k for ERC20 common.HexToAddress(system.SystemContract), + common.HexToAddress(gateway), ) if err != nil { return common.Address{}, cosmoserrors.Wrapf( diff --git a/x/fungible/keeper/evm_test.go b/x/fungible/keeper/evm_test.go index 2ef0f4dfb9..787d08ad87 100644 --- a/x/fungible/keeper/evm_test.go +++ b/x/fungible/keeper/evm_test.go @@ -2,6 +2,8 @@ package keeper_test import ( "encoding/json" + "github.com/zeta-chain/protocol-contracts/v2/pkg/erc1967proxy.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" "math/big" "testing" @@ -12,9 +14,9 @@ import ( evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -54,6 +56,44 @@ func deploySystemContractsWithMockEvmKeeper( return deploySystemContracts(t, ctx, k, mockEVMKeeper) } +// deploy upgradable gateway contract and return its address +func deployGatewayContract( + t *testing.T, + ctx sdk.Context, + k *fungiblekeeper.Keeper, + evmk types.EVMKeeper, + wzeta, admin common.Address, +) common.Address { + // Deploy the gateway contract + implAddr, err := k.DeployContract(ctx, gatewayzevm.GatewayZEVMMetaData) + require.NoError(t, err) + require.NotEmpty(t, implAddr) + assertContractDeployment(t, evmk, ctx, implAddr) + + // Deploy the proxy contract + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + require.NoError(t, err) + + // Encode the initializer data + initializerData, err := gatewayABI.Pack("initialize", wzeta, admin) + require.NoError(t, err) + + gatewayContract, err := k.DeployContract(ctx, erc1967proxy.ERC1967ProxyMetaData, implAddr, initializerData) + require.NoError(t, err) + require.NotEmpty(t, gatewayContract) + assertContractDeployment(t, evmk, ctx, gatewayContract) + + // store the gateway in the system contract object + sys, found := k.GetSystemContract(ctx) + if !found { + sys = types.SystemContract{} + } + sys.Gateway = gatewayContract.Hex() + k.SetSystemContract(ctx, sys) + + return gatewayContract +} + // deploySystemContracts deploys the system contracts and returns their addresses. func deploySystemContracts( t *testing.T, @@ -88,6 +128,10 @@ func deploySystemContracts( require.NotEmpty(t, systemContract) assertContractDeployment(t, evmk, ctx, systemContract) + // deploy the gateway contract + contract := deployGatewayContract(t, ctx, k, evmk, wzeta, sample.EthAddress()) + require.NotEmpty(t, contract) + return } @@ -292,6 +336,66 @@ func TestKeeper_DeployZRC20Contract(t *testing.T) { require.NotNil(t, newBalance) require.Equal(t, amount.Int64(), newBalance.Int64()) }) + + t.Run("can deploy the zrc20 contract without a gateway address", func(t *testing.T) { + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := getValidChainID(t) + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + + systemContract, found := k.GetSystemContract(ctx) + require.True(t, found) + systemContract.Gateway = "" + k.SetSystemContract(ctx, systemContract) + + addr, err := k.DeployZRC20Contract( + ctx, + "foo", + "bar", + 8, + chainID, + coin.CoinType_Gas, + "foobar", + big.NewInt(1000), + ) + require.NoError(t, err) + assertContractDeployment(t, sdkk.EvmKeeper, ctx, addr) + + // check foreign coin + foreignCoins, found := k.GetForeignCoins(ctx, addr.Hex()) + require.True(t, found) + require.Equal(t, "foobar", foreignCoins.Asset) + require.Equal(t, chainID, foreignCoins.ForeignChainId) + require.Equal(t, uint32(8), foreignCoins.Decimals) + require.Equal(t, "foo", foreignCoins.Name) + require.Equal(t, "bar", foreignCoins.Symbol) + require.Equal(t, coin.CoinType_Gas, foreignCoins.CoinType) + require.Equal(t, uint64(1000), foreignCoins.GasLimit) + + // can get the zrc20 data + zrc20Data, err := k.QueryZRC20Data(ctx, addr) + require.NoError(t, err) + require.Equal(t, "foo", zrc20Data.Name) + require.Equal(t, "bar", zrc20Data.Symbol) + require.Equal(t, uint8(8), zrc20Data.Decimals) + + // can deposit tokens + to := sample.EthAddress() + oldBalance, err := k.BalanceOfZRC4(ctx, addr, to) + require.NoError(t, err) + require.NotNil(t, oldBalance) + require.Equal(t, int64(0), oldBalance.Int64()) + + amount := big.NewInt(100) + _, err = k.DepositZRC20(ctx, addr, to, amount) + require.NoError(t, err) + + newBalance, err := k.BalanceOfZRC4(ctx, addr, to) + require.NoError(t, err) + require.NotNil(t, newBalance) + require.Equal(t, amount.Int64(), newBalance.Int64()) + }) } func TestKeeper_DeploySystemContracts(t *testing.T) { diff --git a/x/fungible/keeper/gas_coin_and_pool.go b/x/fungible/keeper/gas_coin_and_pool.go index 6d4d71bf92..877fae2edd 100644 --- a/x/fungible/keeper/gas_coin_and_pool.go +++ b/x/fungible/keeper/gas_coin_and_pool.go @@ -6,9 +6,9 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" - systemcontract "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - uniswapv2router02 "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" diff --git a/x/fungible/keeper/gas_coin_and_pool_test.go b/x/fungible/keeper/gas_coin_and_pool_test.go index 0d76945c0f..b703b212f4 100644 --- a/x/fungible/keeper/gas_coin_and_pool_test.go +++ b/x/fungible/keeper/gas_coin_and_pool_test.go @@ -12,8 +12,8 @@ import ( evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - uniswapv2router02 "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + uniswapv2router02 "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" "github.com/zeta-chain/zetacore/cmd/zetacored/config" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" diff --git a/x/fungible/keeper/gas_price.go b/x/fungible/keeper/gas_price.go index bc28f777f8..600e1171a2 100644 --- a/x/fungible/keeper/gas_price.go +++ b/x/fungible/keeper/gas_price.go @@ -6,7 +6,7 @@ import ( cosmoserrors "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" - systemcontract "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + systemcontract "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" "github.com/zeta-chain/zetacore/x/fungible/types" ) diff --git a/x/fungible/keeper/gas_price_test.go b/x/fungible/keeper/gas_price_test.go index ae79e105e7..dec3c729e9 100644 --- a/x/fungible/keeper/gas_price_test.go +++ b/x/fungible/keeper/gas_price_test.go @@ -6,7 +6,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" diff --git a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go index 9461080fbc..2784c59d28 100644 --- a/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go +++ b/x/fungible/keeper/msg_server_deploy_fungible_coin_zrc20.go @@ -64,7 +64,16 @@ func (k msgServer) DeployFungibleCoinZRC20( } } else { // #nosec G115 always in range - address, err = k.DeployZRC20Contract(ctx, msg.Name, msg.Symbol, uint8(msg.Decimals), msg.ForeignChainId, msg.CoinType, msg.ERC20, big.NewInt(msg.GasLimit)) + address, err = k.DeployZRC20Contract( + ctx, + msg.Name, + msg.Symbol, + uint8(msg.Decimals), + msg.ForeignChainId, + msg.CoinType, + msg.ERC20, + big.NewInt(msg.GasLimit), + ) if err != nil { return nil, err } diff --git a/x/fungible/keeper/msg_server_update_system_contract.go b/x/fungible/keeper/msg_server_update_system_contract.go index 5477396eeb..750f0e44bf 100644 --- a/x/fungible/keeper/msg_server_update_system_contract.go +++ b/x/fungible/keeper/msg_server_update_system_contract.go @@ -8,8 +8,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/coin" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" diff --git a/x/fungible/keeper/msg_server_update_system_contract_test.go b/x/fungible/keeper/msg_server_update_system_contract_test.go index 72b7bac265..1d3e6b7ce6 100644 --- a/x/fungible/keeper/msg_server_update_system_contract_test.go +++ b/x/fungible/keeper/msg_server_update_system_contract_test.go @@ -8,8 +8,8 @@ import ( "github.com/ethereum/go-ethereum/common" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - zrc20 "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/pkg/chains" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" diff --git a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go index 160b2cfc46..9794be84d2 100644 --- a/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go +++ b/x/fungible/keeper/msg_server_update_zrc20_withdraw_fee_test.go @@ -9,7 +9,7 @@ import ( evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" keepertest "github.com/zeta-chain/zetacore/testutil/keeper" "github.com/zeta-chain/zetacore/testutil/sample" diff --git a/x/fungible/keeper/system_contract.go b/x/fungible/keeper/system_contract.go index 656593238d..03f864afc5 100644 --- a/x/fungible/keeper/system_contract.go +++ b/x/fungible/keeper/system_contract.go @@ -8,10 +8,10 @@ import ( "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/wzeta.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" - "github.com/zeta-chain/protocol-contracts/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/uniswap/v2-periphery/contracts/uniswapv2router02.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" "github.com/zeta-chain/zetacore/x/fungible/types" ) diff --git a/x/fungible/keeper/v2_deposits.go b/x/fungible/keeper/v2_deposits.go new file mode 100644 index 0000000000..66df27d86e --- /dev/null +++ b/x/fungible/keeper/v2_deposits.go @@ -0,0 +1,104 @@ +package keeper + +import ( + "fmt" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + ethcommon "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v2/pkg/systemcontract.sol" + + "github.com/zeta-chain/zetacore/pkg/coin" +) + +// ProcessV2Deposit handles a deposit from an inbound tx with protocol version 2 +// returns [txResponse, isContractCall, error] +// isContractCall is true if the message is non empty +func (k Keeper) ProcessV2Deposit( + ctx sdk.Context, + from []byte, + senderChainID int64, + zrc20Addr ethcommon.Address, + to ethcommon.Address, + amount *big.Int, + message []byte, + coinType coin.CoinType, +) (*evmtypes.MsgEthereumTxResponse, bool, error) { + context := systemcontract.ZContext{ + Origin: from, + Sender: ethcommon.Address{}, + ChainID: big.NewInt(senderChainID), + } + + if len(message) == 0 { + // simple deposit + res, err := k.DepositZRC20(ctx, zrc20Addr, to, amount) + return res, false, err + } else if coinType == coin.CoinType_NoAssetCall { + // simple call + res, err := k.CallExecute(ctx, context, zrc20Addr, amount, to, message) + return res, true, err + } + // deposit and call + res, err := k.CallDepositAndCallZRC20(ctx, context, zrc20Addr, amount, to, message) + return res, true, err +} + +// ProcessV2RevertDeposit handles a revert deposit from an inbound tx with protocol version 2 +func (k Keeper) ProcessV2RevertDeposit( + ctx sdk.Context, + amount *big.Int, + chainID int64, + coinType coin.CoinType, + asset string, + revertAddress ethcommon.Address, + callOnRevert bool, + revertMessage []byte, +) error { + // get the zrc20 contract + zrc20Addr, _, err := k.getAndCheckZRC20( + ctx, + amount, + chainID, + coinType, + asset, + ) + if err != nil { + return err + } + + switch coinType { + case coin.CoinType_NoAssetCall: + + if callOnRevert { + // no asset, call simple revert + _, err := k.CallExecuteRevert(ctx, zrc20Addr, amount, revertAddress, revertMessage) + return err + } else { + // no asset, no call, do nothing + return nil + } + case coin.CoinType_Zeta: + return errors.New("ZETA asset is currently unsupported for revert with V2 protocol contracts") + case coin.CoinType_ERC20, coin.CoinType_Gas: + if callOnRevert { + // revert with a ZRC20 asset + _, err := k.CallDepositAndRevert( + ctx, + zrc20Addr, + amount, + revertAddress, + revertMessage, + ) + return err + } else { + // simply deposit back to the revert address + _, err := k.DepositZRC20(ctx, zrc20Addr, revertAddress, amount) + return err + } + } + + return fmt.Errorf("unsupported coin type for revert %s", coinType) +} diff --git a/x/fungible/keeper/v2_deposits_test.go b/x/fungible/keeper/v2_deposits_test.go new file mode 100644 index 0000000000..acf11bf7ae --- /dev/null +++ b/x/fungible/keeper/v2_deposits_test.go @@ -0,0 +1,153 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/contracts/testdappv2" + keepertest "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + fungiblekeeper "github.com/zeta-chain/zetacore/x/fungible/keeper" + "github.com/zeta-chain/zetacore/x/fungible/types" + "math/big" + "testing" +) + +// deployTestDAppV2 deploys the test dapp v2 contract and returns its address +func deployTestDAppV2(t *testing.T, ctx sdk.Context, k *fungiblekeeper.Keeper, evmk types.EVMKeeper) common.Address { + testDAppV2, err := k.DeployContract(ctx, testdappv2.TestDAppV2MetaData) + require.NoError(t, err) + require.NotEmpty(t, testDAppV2) + assertContractDeployment(t, evmk, ctx, testDAppV2) + + return testDAppV2 +} + +// assertTestDAppV2MessageAndAmount asserts the message and amount of the test dapp v2 contract +func assertTestDAppV2MessageAndAmount( + t *testing.T, + ctx sdk.Context, + k *fungiblekeeper.Keeper, + contract common.Address, + expectedMessage string, + expectedAmount int64, +) { + testDAppABI, err := testdappv2.TestDAppV2MetaData.GetAbi() + require.NoError(t, err) + + // message + res, err := k.CallEVM( + ctx, + *testDAppABI, + types.ModuleAddressEVM, + contract, + fungiblekeeper.BigIntZero, + nil, + false, + false, + "getCalledWithMessage", + expectedMessage, + ) + require.NoError(t, err) + + unpacked, err := testDAppABI.Unpack("getCalledWithMessage", res.Ret) + require.NoError(t, err) + require.Len(t, unpacked, 1) + found, ok := unpacked[0].(bool) + require.True(t, ok) + require.True(t, found) + + // amount + res, err = k.CallEVM( + ctx, + *testDAppABI, + types.ModuleAddressEVM, + contract, + fungiblekeeper.BigIntZero, + nil, + false, + false, + "getAmountWithMessage", + expectedMessage, + ) + require.NoError(t, err) + + unpacked, err = testDAppABI.Unpack("getAmountWithMessage", res.Ret) + require.NoError(t, err) + require.Len(t, unpacked, 1) + amount, ok := unpacked[0].(*big.Int) + require.True(t, ok) + require.Equal(t, expectedAmount, amount.Int64()) +} + +func TestKeeper_ProcessV2Deposit(t *testing.T) { + t.Run("should process no-call deposit", func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := chains.DefaultChainsList()[0].ChainId + receiver := sample.EthAddress() + + // deploy the system contracts + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + // ACT + _, contractCall, err := k.ProcessV2Deposit( + ctx, + sample.EthAddress().Bytes(), + chainID, + zrc20, + receiver, + big.NewInt(42), + []byte{}, + coin.CoinType_Gas, + ) + + // ASSERT + require.NoError(t, err) + require.False(t, contractCall) + + balance, err := k.BalanceOfZRC4(ctx, zrc20, receiver) + require.NoError(t, err) + require.Equal(t, big.NewInt(42), balance) + }) + + t.Run("should process deposit and call", func(t *testing.T) { + // ARRANGE + k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) + _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) + + chainID := chains.DefaultChainsList()[0].ChainId + + // deploy test dapp + testDapp := deployTestDAppV2(t, ctx, k, sdkk.EvmKeeper) + + // deploy the system contracts + deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) + zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "foobar") + + // ACT + _, contractCall, err := k.ProcessV2Deposit( + ctx, + sample.EthAddress().Bytes(), + chainID, + zrc20, + testDapp, + big.NewInt(82), + []byte("foo"), + coin.CoinType_Gas, + ) + + // ASSERT + require.NoError(t, err) + require.True(t, contractCall) + balance, err := k.BalanceOfZRC4(ctx, zrc20, testDapp) + require.NoError(t, err) + require.Equal(t, big.NewInt(82), balance) + assertTestDAppV2MessageAndAmount(t, ctx, k, testDapp, "foo", 82) + }) +} diff --git a/x/fungible/keeper/v2_evm.go b/x/fungible/keeper/v2_evm.go new file mode 100644 index 0000000000..e77c04b580 --- /dev/null +++ b/x/fungible/keeper/v2_evm.go @@ -0,0 +1,216 @@ +package keeper + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + evmtypes "github.com/evmos/ethermint/x/evm/types" + "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/zetacore/pkg/crypto" + "github.com/zeta-chain/zetacore/x/fungible/types" +) + +// CallDepositAndCallZRC20 calls the depositAndCall (ZRC20 version) function on the gateway contract +// Callable only by the fungible module account +// returns directly CallEVM() +// function depositAndCall( +// +// zContext calldata context, +// address zrc20, +// uint256 amount, +// address target, +// bytes calldata message +// ) +func (k Keeper) CallDepositAndCallZRC20( + ctx sdk.Context, + context systemcontract.ZContext, + zrc20 common.Address, + amount *big.Int, + target common.Address, + message []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + if err != nil { + return nil, err + } + + systemContract, found := k.GetSystemContract(ctx) + if !found { + return nil, types.ErrSystemContractNotFound + } + gatewayAddr := common.HexToAddress(systemContract.Gateway) + if crypto.IsEmptyAddress(gatewayAddr) { + return nil, types.ErrGatewayContractNotSet + } + + // NOTE: + // depositAndCall: ZETA version for depositAndCall method + // depositAndCall0: ZRC20 version for depositAndCall method + return k.CallEVM( + ctx, + *gatewayABI, + types.ModuleAddressEVM, + gatewayAddr, + BigIntZero, + nil, + true, + false, + "depositAndCall0", + context, + zrc20, + amount, + target, + message, + ) +} + +// CallExecute calls the execute function on the gateway contract +// function execute( +// +// zContext calldata context, +// address zrc20, +// uint256 amount, +// address target, +// bytes calldata message +// +// ) +func (k Keeper) CallExecute( + ctx sdk.Context, + context systemcontract.ZContext, + zrc20 common.Address, + amount *big.Int, + target common.Address, + message []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + if err != nil { + return nil, err + } + + systemContract, found := k.GetSystemContract(ctx) + if !found { + return nil, types.ErrSystemContractNotFound + } + gatewayAddr := common.HexToAddress(systemContract.Gateway) + if crypto.IsEmptyAddress(gatewayAddr) { + return nil, types.ErrGatewayContractNotSet + } + + return k.CallEVM( + ctx, + *gatewayABI, + types.ModuleAddressEVM, + gatewayAddr, + BigIntZero, + nil, + true, + false, + "execute", + context, + zrc20, + amount, + target, + message, + ) +} + +// CallExecuteRevert calls the executeRevert function on the gateway contract +// +// function executeRevert( +// address target, +// RevertContext revertContext, +// ) +func (k Keeper) CallExecuteRevert( + ctx sdk.Context, + zrc20 common.Address, + amount *big.Int, + target common.Address, + message []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + if err != nil { + return nil, err + } + + systemContract, found := k.GetSystemContract(ctx) + if !found { + return nil, types.ErrSystemContractNotFound + } + gatewayAddr := common.HexToAddress(systemContract.Gateway) + if crypto.IsEmptyAddress(gatewayAddr) { + return nil, types.ErrGatewayContractNotSet + } + + return k.CallEVM( + ctx, + *gatewayABI, + types.ModuleAddressEVM, + gatewayAddr, + BigIntZero, + nil, + true, + false, + "executeRevert", + target, + revert.RevertContext{ + Asset: zrc20, + Amount: amount.Uint64(), + RevertMessage: message, + }, + ) +} + +// CallDepositAndRevert calls the depositAndRevert function on the gateway contract +// +//function depositAndRevert( +// address zrc20, +// uint256 amount, +// address target, +// RevertContext revertContext +//) + +func (k Keeper) CallDepositAndRevert( + ctx sdk.Context, + zrc20 common.Address, + amount *big.Int, + target common.Address, + message []byte, +) (*evmtypes.MsgEthereumTxResponse, error) { + gatewayABI, err := gatewayzevm.GatewayZEVMMetaData.GetAbi() + if err != nil { + return nil, err + } + + systemContract, found := k.GetSystemContract(ctx) + if !found { + return nil, types.ErrSystemContractNotFound + } + gatewayAddr := common.HexToAddress(systemContract.Gateway) + if crypto.IsEmptyAddress(gatewayAddr) { + return nil, types.ErrGatewayContractNotSet + } + + return k.CallEVM( + ctx, + *gatewayABI, + types.ModuleAddressEVM, + gatewayAddr, + BigIntZero, + nil, + true, + false, + "depositAndRevert", + zrc20, + amount, + target, + revert.RevertContext{ + Asset: zrc20, + Amount: amount.Uint64(), + RevertMessage: message, + }, + ) +} diff --git a/x/fungible/types/errors.go b/x/fungible/types/errors.go index dad99cfd9b..7e426a3178 100644 --- a/x/fungible/types/errors.go +++ b/x/fungible/types/errors.go @@ -28,4 +28,5 @@ var ( ErrForeignCoinAlreadyExist = cosmoserrors.Register(ModuleName, 1125, "foreign coin already exist") ErrNilGasPrice = cosmoserrors.Register(ModuleName, 1127, "nil gas price") ErrAccountNotFound = cosmoserrors.Register(ModuleName, 1128, "account not found") + ErrGatewayContractNotSet = cosmoserrors.Register(ModuleName, 1129, "gateway contract not set") ) diff --git a/x/observer/client/querytests/chain_nonces.go b/x/observer/client/querytests/chain_nonces.go deleted file mode 100644 index 2eacbc99c8..0000000000 --- a/x/observer/client/querytests/chain_nonces.go +++ /dev/null @@ -1,123 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - "github.com/cosmos/cosmos-sdk/client/flags" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/observer/client/cli" - "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestShowChainNonces() { - ctx := s.network.Validators[0].ClientCtx - objs := s.observerState.ChainNonces - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - id int64 - args []string - err error - obj types.ChainNonces - }{ - { - desc: "found", - id: objs[0].ChainId, - args: common, - obj: objs[0], - }, - { - desc: "not found", - id: 1000, - args: common, - err: status.Error(codes.InvalidArgument, "not found"), - }, - } { - tc := tc - s.Run(tc.desc, func() { - chainIDStr := fmt.Sprintf("%d", tc.id) - args := []string{chainIDStr} - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowChainNonces(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetChainNoncesResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.ChainNonces) - s.Require().Equal(tc.obj, resp.ChainNonces) - } - }) - } -} - -func (s *CliTestSuite) TestListChainNonces() { - ctx := s.network.Validators[0].ClientCtx - objs := s.observerState.ChainNonces - request := func(next []byte, offset, limit uint64, total bool) []string { - args := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - if next == nil { - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagOffset, offset)) - } else { - args = append(args, fmt.Sprintf("--%s=%s", flags.FlagPageKey, next)) - } - args = append(args, fmt.Sprintf("--%s=%d", flags.FlagLimit, limit)) - if total { - args = append(args, fmt.Sprintf("--%s", flags.FlagCountTotal)) - } - return args - } - s.Run("ByOffset", func() { - step := 2 - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(nil, uint64(i), uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) - s.Require().NoError(err) - var resp types.QueryAllChainNoncesResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.ChainNonces[j-i]) - } - } - }) - s.Run("ByKey", func() { - step := 2 - var next []byte - for i := 0; i < len(objs); i += step { - // #nosec G115 always in range - args := request(next, 0, uint64(step), false) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) - s.Require().NoError(err) - var resp types.QueryAllChainNoncesResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - for j := i; j < len(objs) && j < i+step; j++ { - s.Assert().Equal(objs[j], resp.ChainNonces[j-i]) - } - next = resp.Pagination.NextKey - } - }) - s.Run("Total", func() { - // #nosec G115 always in range - args := request(nil, 0, uint64(len(objs)), true) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdListChainNonces(), args) - s.Require().NoError(err) - var resp types.QueryAllChainNoncesResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NoError(err) - // #nosec G115 always in range - s.Require().Equal(len(objs), int(resp.Pagination.Total)) - s.Require().Equal(objs, resp.ChainNonces) - }) -} diff --git a/x/observer/client/querytests/cli_test.go b/x/observer/client/querytests/cli_test.go deleted file mode 100644 index 777fc81528..0000000000 --- a/x/observer/client/querytests/cli_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package querytests - -import ( - "testing" - - tmdb "github.com/cometbft/cometbft-db" - "github.com/cosmos/cosmos-sdk/baseapp" - servertypes "github.com/cosmos/cosmos-sdk/server/types" - pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" - simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" - "github.com/cosmos/cosmos-sdk/types/module/testutil" - "github.com/stretchr/testify/suite" - - "github.com/zeta-chain/zetacore/app" - "github.com/zeta-chain/zetacore/testutil/network" -) - -func TestCLIQuerySuite(t *testing.T) { - cfg := network.DefaultConfig(NewTestNetworkFixture) - suite.Run(t, NewCLITestSuite(cfg)) -} - -func NewTestNetworkFixture() network.TestFixture { - encoding := app.MakeEncodingConfig() - appCtr := func(val network.ValidatorI) servertypes.Application { - return app.New( - val.GetCtx().Logger, tmdb.NewMemDB(), nil, true, map[int64]bool{}, val.GetCtx().Config.RootDir, 0, - encoding, - simtestutil.EmptyAppOptions{}, - baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)), - baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices), - baseapp.SetChainID("athens_8888-2"), - ) - } - - return network.TestFixture{ - AppConstructor: appCtr, - GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Codec), - EncodingConfig: testutil.TestEncodingConfig{ - InterfaceRegistry: encoding.InterfaceRegistry, - Codec: encoding.Codec, - TxConfig: encoding.TxConfig, - Amino: encoding.Amino, - }, - } -} diff --git a/x/observer/client/querytests/crosschain_flags.go b/x/observer/client/querytests/crosschain_flags.go deleted file mode 100644 index e9756e750f..0000000000 --- a/x/observer/client/querytests/crosschain_flags.go +++ /dev/null @@ -1,53 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/testutil/nullify" - "github.com/zeta-chain/zetacore/x/observer/client/cli" - "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestShowCrosschainFlags() { - ctx := s.network.Validators[0].ClientCtx - obj := s.observerState.CrosschainFlags - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - args []string - err error - obj *types.CrosschainFlags - }{ - { - desc: "get", - args: common, - obj: obj, - }, - } { - s.Run(tc.desc, func() { - var args []string - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowCrosschainFlags(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetCrosschainFlagsResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.CrosschainFlags) - tc := tc - s.Require().Equal(nullify.Fill(&tc.obj), - nullify.Fill(&resp.CrosschainFlags), - ) - } - }) - } -} diff --git a/x/observer/client/querytests/keygen.go b/x/observer/client/querytests/keygen.go deleted file mode 100644 index 904f254a15..0000000000 --- a/x/observer/client/querytests/keygen.go +++ /dev/null @@ -1,50 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/observer/client/cli" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestShowKeygen() { - ctx := s.network.Validators[0].ClientCtx - obj := s.observerState.Keygen - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - args []string - err error - obj *observerTypes.Keygen - }{ - { - desc: "get", - args: common, - obj: obj, - }, - } { - tc := tc - s.Run(tc.desc, func() { - var args []string - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowKeygen(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp observerTypes.QueryGetKeygenResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.Keygen) - s.Require().Equal(tc.obj, resp.Keygen) - } - }) - } -} diff --git a/x/observer/client/querytests/node_account.go b/x/observer/client/querytests/node_account.go deleted file mode 100644 index 1e72d561b0..0000000000 --- a/x/observer/client/querytests/node_account.go +++ /dev/null @@ -1,59 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/observer/client/cli" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestShowNodeAccount() { - ctx := s.network.Validators[0].ClientCtx - objs := s.observerState.NodeAccountList - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - id string - args []string - err error - obj *zetaObserverTypes.NodeAccount - }{ - { - desc: "found", - id: objs[0].Operator, - args: common, - obj: objs[0], - }, - { - desc: "not found", - id: "not_found", - args: common, - err: status.Error(codes.InvalidArgument, "not found"), - }, - } { - tc := tc - s.Run(tc.desc, func() { - args := []string{tc.id} - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowNodeAccount(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp zetaObserverTypes.QueryGetNodeAccountResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.NodeAccount) - s.Require().Equal(tc.obj, resp.NodeAccount) - } - }) - } -} diff --git a/x/observer/client/querytests/suite.go b/x/observer/client/querytests/suite.go deleted file mode 100644 index c0a0710c5b..0000000000 --- a/x/observer/client/querytests/suite.go +++ /dev/null @@ -1,59 +0,0 @@ -package querytests - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - ethcfg "github.com/evmos/ethermint/cmd/config" - "github.com/stretchr/testify/suite" - - "github.com/zeta-chain/zetacore/app" - cmdcfg "github.com/zeta-chain/zetacore/cmd/zetacored/config" - "github.com/zeta-chain/zetacore/testutil/network" - "github.com/zeta-chain/zetacore/x/crosschain/types" - observerTypes "github.com/zeta-chain/zetacore/x/observer/types" -) - -type CliTestSuite struct { - suite.Suite - - cfg network.Config - network *network.Network - crosschainState *types.GenesisState - observerState *observerTypes.GenesisState -} - -func NewCLITestSuite(cfg network.Config) *CliTestSuite { - return &CliTestSuite{cfg: cfg} -} - -func (s *CliTestSuite) Setconfig() { - config := sdk.GetConfig() - cmdcfg.SetBech32Prefixes(config) - ethcfg.SetBip44CoinType(config) - // Make sure address is compatible with ethereum - config.SetAddressVerifier(app.VerifyAddressFormat) - config.Seal() -} -func (s *CliTestSuite) SetupSuite() { - s.T().Log("setting up integration test suite") - s.Setconfig() - minOBsDel, ok := sdk.NewIntFromString("100000000000000000000") - s.Require().True(ok) - s.cfg.StakingTokens = minOBsDel.Mul(sdk.NewInt(int64(10))) - s.cfg.BondedTokens = minOBsDel - observerList := []string{"zeta13c7p3xrhd6q2rx3h235jpt8pjdwvacyw6twpax", - "zeta1f203dypqg5jh9hqfx0gfkmmnkdfuat3jr45ep2", - } - network.SetupZetaGenesisState(s.T(), s.cfg.GenesisState, s.cfg.Codec, observerList, false) - s.crosschainState = network.AddCrosschainData(s.T(), 2, s.cfg.GenesisState, s.cfg.Codec) - s.observerState = network.AddObserverData(s.T(), 2, s.cfg.GenesisState, s.cfg.Codec, nil) - net, err := network.New(s.T(), app.NodeDir, s.cfg) - s.Assert().NoError(err) - s.network = net - _, err = s.network.WaitForHeight(1) - s.Require().NoError(err) -} - -func (s *CliTestSuite) TearDownSuite() { - s.T().Log("tearing down genesis test suite") - s.network.Cleanup() -} diff --git a/x/observer/client/querytests/tss.go b/x/observer/client/querytests/tss.go deleted file mode 100644 index 72d15d1452..0000000000 --- a/x/observer/client/querytests/tss.go +++ /dev/null @@ -1,51 +0,0 @@ -package querytests - -import ( - "fmt" - - tmcli "github.com/cometbft/cometbft/libs/cli" - clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" - "google.golang.org/grpc/status" - - "github.com/zeta-chain/zetacore/x/observer/client/cli" - "github.com/zeta-chain/zetacore/x/observer/types" -) - -func (s *CliTestSuite) TestShowTSS() { - ctx := s.network.Validators[0].ClientCtx - obj := s.observerState.Tss - common := []string{ - fmt.Sprintf("--%s=json", tmcli.OutputFlag), - } - for _, tc := range []struct { - desc string - args []string - err error - obj *types.TSS - }{ - { - desc: "get", - args: common, - obj: obj, - err: nil, - }, - } { - tc := tc - s.Run(tc.desc, func() { - var args []string - args = append(args, tc.args...) - out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdShowTSS(), args) - if tc.err != nil { - stat, ok := status.FromError(tc.err) - s.Require().True(ok) - s.Require().ErrorIs(stat.Err(), tc.err) - } else { - s.Require().NoError(err) - var resp types.QueryGetTSSResponse - s.Require().NoError(s.network.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) - s.Require().NotNil(resp.TSS) - s.Require().Equal(*tc.obj, resp.TSS) - } - }) - } -} diff --git a/x/observer/types/chain_params.go b/x/observer/types/chain_params.go index 5fb41aab2e..d1bc9f1b3b 100644 --- a/x/observer/types/chain_params.go +++ b/x/observer/types/chain_params.go @@ -10,13 +10,10 @@ import ( ethchains "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/constant" solanacontracts "github.com/zeta-chain/zetacore/pkg/contracts/solana" ) -const ( - zeroAddress = "0x0000000000000000000000000000000000000000" -) - var ( DefaultMinObserverDelegation = sdk.MustNewDecFromStr("1000000000000000000000") DefaultBallotThreshold = sdk.MustNewDecFromStr("0.66") @@ -161,9 +158,9 @@ func GetDefaultEthMainnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.Ethereum.ChainId, ConfirmationCount: 14, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 12, OutboundTicker: 15, WatchUtxoTicker: 0, @@ -179,9 +176,9 @@ func GetDefaultBscMainnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.BscMainnet.ChainId, ConfirmationCount: 14, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 5, OutboundTicker: 15, WatchUtxoTicker: 0, @@ -197,9 +194,9 @@ func GetDefaultBtcMainnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.BitcoinMainnet.ChainId, ConfirmationCount: 2, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, WatchUtxoTicker: 30, InboundTicker: 120, OutboundTicker: 60, @@ -217,8 +214,8 @@ func GetDefaultGoerliTestnetChainParams() *ChainParams { ConfirmationCount: 6, // This is the actual Zeta token Goerli testnet, we need to specify this address for the integration tests to pass ZetaTokenContractAddress: "0x0000c304d2934c00db1d51995b9f6996affd17c0", - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 12, OutboundTicker: 15, WatchUtxoTicker: 0, @@ -234,9 +231,9 @@ func GetDefaultBscTestnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.BscTestnet.ChainId, ConfirmationCount: 6, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 5, OutboundTicker: 15, WatchUtxoTicker: 0, @@ -252,9 +249,9 @@ func GetDefaultMumbaiTestnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.Mumbai.ChainId, ConfirmationCount: 12, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 2, OutboundTicker: 15, WatchUtxoTicker: 0, @@ -270,9 +267,9 @@ func GetDefaultBtcTestnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.BitcoinTestnet.ChainId, ConfirmationCount: 2, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, WatchUtxoTicker: 30, InboundTicker: 120, OutboundTicker: 12, @@ -288,9 +285,9 @@ func GetDefaultBtcRegtestChainParams() *ChainParams { return &ChainParams{ ChainId: chains.BitcoinRegtest.ChainId, ConfirmationCount: 1, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, GasPriceTicker: 5, WatchUtxoTicker: 1, InboundTicker: 1, @@ -306,9 +303,9 @@ func GetDefaultSolanaLocalnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.SolanaLocalnet.ChainId, ConfirmationCount: 32, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, GasPriceTicker: 5, WatchUtxoTicker: 0, InboundTicker: 2, @@ -337,15 +334,16 @@ func GetDefaultGoerliLocalnetChainParams() *ChainParams { BallotThreshold: DefaultBallotThreshold, MinObserverDelegation: DefaultMinObserverDelegation, IsSupported: false, + GatewayAddress: "0xF0deebCB0E9C829519C4baa794c5445171973826", } } func GetDefaultZetaPrivnetChainParams() *ChainParams { return &ChainParams{ ChainId: chains.ZetaChainPrivnet.ChainId, ConfirmationCount: 1, - ZetaTokenContractAddress: zeroAddress, - ConnectorContractAddress: zeroAddress, - Erc20CustodyContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, InboundTicker: 2, OutboundTicker: 2, WatchUtxoTicker: 0, diff --git a/x/observer/types/chain_params_test.go b/x/observer/types/chain_params_test.go index dff1311a9c..64c27d5757 100644 --- a/x/observer/types/chain_params_test.go +++ b/x/observer/types/chain_params_test.go @@ -68,6 +68,7 @@ func (s *UpdateChainParamsSuite) SetupTest() { BallotThreshold: types.DefaultBallotThreshold, MinObserverDelegation: types.DefaultMinObserverDelegation, IsSupported: false, + GatewayAddress: "0xF0deebCB0E9C829519C4baa794c5445171973826", } s.btcParams = &types.ChainParams{ ConfirmationCount: 1, diff --git a/zetaclient/chains/bitcoin/signer/signer.go b/zetaclient/chains/bitcoin/signer/signer.go index 7e701e523d..b49278d25c 100644 --- a/zetaclient/chains/bitcoin/signer/signer.go +++ b/zetaclient/chains/bitcoin/signer/signer.go @@ -205,7 +205,7 @@ func (signer *Signer) SignWithdrawTx( signer.Logger(). Std.Error(). Err(err). - Msgf("SignWithdrawTx: FetchUTXOs error: nonce %d chain %d", nonce, chain.ChainId) + Msgf("SignGasWithdraw: FetchUTXOs error: nonce %d chain %d", nonce, chain.ChainId) } // select N UTXOs to cover the total expense @@ -416,7 +416,7 @@ func (signer *Signer) TryProcessOutbound( true, chain.ChainId, cctx.Index, cctx.InboundParams.Sender, params.Receiver, "BTC") amount = 0.0 // zero out the amount to cancel the tx } - logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", to.EncodeAddress(), params.Amount.Uint64()) + logger.Info().Msgf("SignGasWithdraw: to %s, value %d sats", to.EncodeAddress(), params.Amount.Uint64()) // sign withdraw tx tx, err := signer.SignWithdrawTx( @@ -432,7 +432,9 @@ func (signer *Signer) TryProcessOutbound( cancelTx, ) if err != nil { - logger.Warn().Err(err).Msgf("SignOutbound error: nonce %d chain %d", outboundTssNonce, params.ReceiverChainId) + logger.Warn(). + Err(err). + Msgf("SignConnectorOnReceive error: nonce %d chain %d", outboundTssNonce, params.ReceiverChainId) return } logger.Info(). diff --git a/zetaclient/chains/evm/cctx.go b/zetaclient/chains/evm/cctx.go new file mode 100644 index 0000000000..df8515e175 --- /dev/null +++ b/zetaclient/chains/evm/cctx.go @@ -0,0 +1,84 @@ +package evm + +import ( + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// OutboundType enumerate the different types of outbound transactions +// NOTE: only used for v2 protocol contracts and currently excludes ZETA withdraws +type OutboundType int + +const ( + // OutboundTypeUnknown is an unknown outbound transaction + OutboundTypeUnknown OutboundType = iota + + // OutboundTypeGasWithdraw is a gas withdraw transaction + OutboundTypeGasWithdraw + + // OutboundTypeERC20Withdraw is an ERC20 withdraw transaction + OutboundTypeERC20Withdraw + + // OutboundTypeGasWithdrawAndCall is a gas withdraw and call transaction + OutboundTypeGasWithdrawAndCall + + // OutboundTypeERC20WithdrawAndCall is an ERC20 withdraw and call transaction + OutboundTypeERC20WithdrawAndCall + + // OutboundTypeCall is a no-asset call transaction + OutboundTypeCall + + // OutboundTypeGasWithdrawRevert is a gas withdraw revert + OutboundTypeGasWithdrawRevert + + // OutboundTypeGasWithdrawRevertAndCallOnRevert is a gas withdraw revert and call on revert + OutboundTypeGasWithdrawRevertAndCallOnRevert + + // OutboundTypeERC20WithdrawRevert is an ERC20 withdraw revert + OutboundTypeERC20WithdrawRevert + + // OutboundTypeERC20WithdrawRevertAndCallOnRevert is an ERC20 withdraw revert and call on revert + OutboundTypeERC20WithdrawRevertAndCallOnRevert +) + +// ParseOutboundTypeFromCCTX returns the outbound type from the CCTX +func ParseOutboundTypeFromCCTX(cctx types.CrossChainTx) OutboundType { + switch cctx.InboundParams.CoinType { + case coin.CoinType_Gas: + switch cctx.CctxStatus.Status { + case types.CctxStatus_PendingOutbound: + if len(cctx.RelayedMessage) == 0 { + return OutboundTypeGasWithdraw + } else { + return OutboundTypeGasWithdrawAndCall + } + case types.CctxStatus_PendingRevert: + if cctx.RevertOptions.CallOnRevert { + return OutboundTypeGasWithdrawRevertAndCallOnRevert + } else { + return OutboundTypeGasWithdrawRevert + } + } + case coin.CoinType_ERC20: + switch cctx.CctxStatus.Status { + case types.CctxStatus_PendingOutbound: + if len(cctx.RelayedMessage) == 0 { + return OutboundTypeERC20Withdraw + } else { + return OutboundTypeERC20WithdrawAndCall + } + case types.CctxStatus_PendingRevert: + if cctx.RevertOptions.CallOnRevert { + return OutboundTypeERC20WithdrawRevertAndCallOnRevert + } else { + return OutboundTypeERC20WithdrawRevert + } + } + case coin.CoinType_NoAssetCall: + if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + return OutboundTypeCall + } + } + + return OutboundTypeUnknown +} diff --git a/zetaclient/chains/evm/constant.go b/zetaclient/chains/evm/constant.go index beaeb6143f..65398b3013 100644 --- a/zetaclient/chains/evm/constant.go +++ b/zetaclient/chains/evm/constant.go @@ -40,4 +40,34 @@ const ( // [signature, asset] // https://github.com/zeta-chain/protocol-contracts/blob/d65814debf17648a6c67d757ba03646415842790/contracts/evm/ERC20Custody.sol#L42 TopicsDeposited = 2 + + // V2 contracts + + // TopicsGatewayDeposit is the number of topics for a gateway deposit event + // [signature, sender, receiver] + TopicsGatewayDeposit = 3 + + // TopicsGatewayCall is the number of topics for a gateway call event + // [signature, sender, receiver] + TopicsGatewayCall = 3 + + // TopicsGatewayExecuted is the number of topics for a gateway executed event + // [signature, destination] + TopicsGatewayExecuted = 2 + + // TopicsGatewayExecutedWithERC20 is the number of topics for a gateway executed with ERC20 event + // [signature, token, destination] + TopicsGatewayExecutedWithERC20 = 3 + + // TopicsGatewayReverted is the number of topics for a reverted event + // [signature, destination] + TopicsGatewayReverted = 3 + + // TopicsERC20CustodyWithdraw is the number of topics for an ERC20 custody withdraw event + // [signature, recipient, asset] + TopicsERC20CustodyWithdraw = 3 + + // TopicsERC20CustodyWithdrawAndCall is the number of topics for an ERC20 custody withdraw and call event + // [signature, recipient, asset] + TopicsERC20CustodyWithdrawAndCall = 3 ) diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index abf21e7e5b..5d72d0a6e7 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -17,8 +17,8 @@ import ( "github.com/onrik/ethrpc" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" "github.com/zeta-chain/zetacore/pkg/bg" "github.com/zeta-chain/zetacore/pkg/chains" @@ -223,6 +223,22 @@ func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Lo return errors.Wrap(err, "unable to observe TSSReceive") } + // query the gateway logs + // TODO: refactor in a more declarative design. Example: storing the list of contract and events to listen in an array + // https://github.com/zeta-chain/node/issues/2493 + lastScannedGatewayDeposit, err := ob.ObserveGatewayDeposit(ctx, startBlock, toBlock) + if err != nil { + ob.Logger().Inbound.Error(). + Err(err). + Msgf("ObserveInbound: error observing deposit events from Gateway contract") + } + lastScannedGatewayCall, err := ob.ObserveGatewayCall(ctx, startBlock, toBlock) + if err != nil { + ob.Logger().Inbound.Error(). + Err(err). + Msgf("ObserveInbound: error observing call events from Gateway contract") + } + // note: using lowest height for all 3 events is not perfect, but it's simple and good enough lastScannedLowest := lastScannedZetaSent if lastScannedDeposited < lastScannedLowest { @@ -231,6 +247,12 @@ func (ob *Observer) ObserveInbound(ctx context.Context, sampledLogger zerolog.Lo if lastScannedTssRecvd < lastScannedLowest { lastScannedLowest = lastScannedTssRecvd } + if lastScannedGatewayDeposit < lastScannedLowest { + lastScannedLowest = lastScannedGatewayDeposit + } + if lastScannedGatewayCall < lastScannedLowest { + lastScannedLowest = lastScannedGatewayCall + } // update last scanned block height for all 3 events (ZetaSent, Deposited, TssRecvd), ignore db error if lastScannedLowest > lastScanned { diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index b5e7be3f6e..6dd1234018 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -13,10 +13,12 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/onrik/ethrpc" "github.com/pkg/errors" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zeta.non-eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.eth.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zeta.non-eth.sol" + zetaconnectoreth "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" "github.com/zeta-chain/zetacore/pkg/bg" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -149,6 +151,23 @@ func (ob *Observer) GetERC20CustodyContract() (ethcommon.Address, *erc20custody. return addr, contract, err } +// GetERC20CustodyV2Contract returns ERC20CustodyV2 contract address and binder +// NOTE: we use the same address as gateway v1 +// this simplify the migration process v1 will be completely removed in the future +// currently the ABI for withdraw is identical, therefore both contract instances can be used +func (ob *Observer) GetERC20CustodyV2Contract() (ethcommon.Address, *erc20custodyv2.ERC20Custody, error) { + addr := ethcommon.HexToAddress(ob.GetChainParams().Erc20CustodyContractAddress) + contract, err := erc20custodyv2.NewERC20Custody(addr, ob.evmClient) + return addr, contract, err +} + +// GetGatewayContract returns the gateway contract address and binder +func (ob *Observer) GetGatewayContract() (ethcommon.Address, *gatewayevm.GatewayEVM, error) { + addr := ethcommon.HexToAddress(ob.GetChainParams().GatewayAddress) + contract, err := gatewayevm.NewGatewayEVM(addr, ob.evmClient) + return addr, contract, err +} + // FetchConnectorContractEth returns the Eth connector address and binder // TODO(revamp): move this to a contract package func FetchConnectorContractEth( diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index 598b9b6a44..2a10ffc5af 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -13,8 +13,10 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" @@ -217,6 +219,14 @@ func (ob *Observer) VoteOutboundIfConfirmed( if err != nil { return true, errors.Wrapf(err, "error getting erc20 custody for chain %d", ob.Chain().ChainId) } + gatewayAddr, gateway, err := ob.GetGatewayContract() + if err != nil { + return true, errors.Wrap(err, "error getting gateway for chain") + } + _, custodyV2, err := ob.GetERC20CustodyV2Contract() + if err != nil { + return true, errors.Wrapf(err, "error getting erc20 custody v2 for chain %d", ob.Chain().ChainId) + } // define a few common variables var receiveValue *big.Int @@ -236,7 +246,7 @@ func (ob *Observer) VoteOutboundIfConfirmed( } // parse the received value from the outbound receipt - receiveValue, receiveStatus, err = ParseOutboundReceivedValue( + receiveValue, receiveStatus, err = parseOutboundReceivedValue( cctx, receipt, transaction, @@ -245,6 +255,9 @@ func (ob *Observer) VoteOutboundIfConfirmed( connector, custodyAddr, custody, + custodyV2, + gatewayAddr, + gateway, ) if err != nil { logger.Error(). @@ -258,6 +271,70 @@ func (ob *Observer) VoteOutboundIfConfirmed( return false, nil } +// parseOutboundReceivedValue parses the received value and status from the outbound receipt +// The receivd value is the amount of Zeta/ERC20/Gas token (released from connector/custody/TSS) sent to the receiver +// TODO: simplify this function and reduce the number of argument +// https://github.com/zeta-chain/node/issues/2627 +// https://github.com/zeta-chain/node/pull/2666#discussion_r1718379784 +func parseOutboundReceivedValue( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + transaction *ethtypes.Transaction, + cointype coin.CoinType, + connectorAddress ethcommon.Address, + connector *zetaconnector.ZetaConnectorNonEth, + custodyAddress ethcommon.Address, + custody *erc20custody.ERC20Custody, + custodyV2 *erc20custodyv2.ERC20Custody, + gatewayAddress ethcommon.Address, + gateway *gatewayevm.GatewayEVM, +) (*big.Int, chains.ReceiveStatus, error) { + // determine the receive status and value + // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ + receiveValue := big.NewInt(0) + receiveStatus := chains.ReceiveStatus_failed + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + receiveValue = transaction.Value() + receiveStatus = chains.ReceiveStatus_success + } + + // parse outbound event for protocol contract v2 + if cctx.ProtocolContractVersion == crosschaintypes.ProtocolContractVersion_V2 { + return parseOutboundEventV2(cctx, receipt, transaction, custodyAddress, custodyV2, gatewayAddress, gateway) + } + + // parse receive value from the outbound receipt for Zeta and ERC20 + switch cointype { + case coin.CoinType_Zeta: + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + receivedLog, revertedLog, err := ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + if err != nil { + return nil, chains.ReceiveStatus_failed, err + } + // use the value in ZetaReceived/ZetaReverted event for vote message + if receivedLog != nil { + receiveValue = receivedLog.ZetaValue + } else if revertedLog != nil { + receiveValue = revertedLog.RemainingZetaValue + } + } + case coin.CoinType_ERC20: + if receipt.Status == ethtypes.ReceiptStatusSuccessful { + withdrawn, err := ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + if err != nil { + return nil, chains.ReceiveStatus_failed, err + } + // use the value in Withdrawn event for vote message + receiveValue = withdrawn.Amount + } + case coin.CoinType_Gas, coin.CoinType_Cmd: + // nothing to do for CoinType_Gas/CoinType_Cmd, no need to parse event + default: + return nil, chains.ReceiveStatus_failed, fmt.Errorf("unknown coin type %s", cointype) + } + return receiveValue, receiveStatus, nil +} + // ParseAndCheckZetaEvent parses and checks ZetaReceived/ZetaReverted event from the outbound receipt // It either returns an ZetaReceived or an ZetaReverted event, or an error if no event found func ParseAndCheckZetaEvent( @@ -350,59 +427,6 @@ func ParseAndCheckWithdrawnEvent( return nil, errors.New("no ERC20 Withdrawn event found") } -// ParseOutboundReceivedValue parses the received value and status from the outbound receipt -// The receivd value is the amount of Zeta/ERC20/Gas token (released from connector/custody/TSS) sent to the receiver -func ParseOutboundReceivedValue( - cctx *crosschaintypes.CrossChainTx, - receipt *ethtypes.Receipt, - transaction *ethtypes.Transaction, - cointype coin.CoinType, - connectorAddress ethcommon.Address, - connector *zetaconnector.ZetaConnectorNonEth, - custodyAddress ethcommon.Address, - custody *erc20custody.ERC20Custody, -) (*big.Int, chains.ReceiveStatus, error) { - // determine the receive status and value - // https://docs.nethereum.com/en/latest/nethereum-receipt-status/ - receiveValue := big.NewInt(0) - receiveStatus := chains.ReceiveStatus_failed - if receipt.Status == ethtypes.ReceiptStatusSuccessful { - receiveValue = transaction.Value() - receiveStatus = chains.ReceiveStatus_success - } - - // parse receive value from the outbound receipt for Zeta and ERC20 - switch cointype { - case coin.CoinType_Zeta: - if receipt.Status == ethtypes.ReceiptStatusSuccessful { - receivedLog, revertedLog, err := ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) - if err != nil { - return nil, chains.ReceiveStatus_failed, err - } - // use the value in ZetaReceived/ZetaReverted event for vote message - if receivedLog != nil { - receiveValue = receivedLog.ZetaValue - } else if revertedLog != nil { - receiveValue = revertedLog.RemainingZetaValue - } - } - case coin.CoinType_ERC20: - if receipt.Status == ethtypes.ReceiptStatusSuccessful { - withdrawn, err := ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) - if err != nil { - return nil, chains.ReceiveStatus_failed, err - } - // use the value in Withdrawn event for vote message - receiveValue = withdrawn.Amount - } - case coin.CoinType_Gas, coin.CoinType_Cmd: - // nothing to do for CoinType_Gas/CoinType_Cmd, no need to parse event - default: - return nil, chains.ReceiveStatus_failed, fmt.Errorf("unknown coin type %s", cointype) - } - return receiveValue, receiveStatus, nil -} - // checkConfirmedTx checks if a txHash is confirmed // returns (receipt, transaction, true) if confirmed or (nil, nil, false) otherwise func (ob *Observer) checkConfirmedTx( diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 4081f9a76c..68b3da13ae 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -7,8 +7,8 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/rs/zerolog" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/testutil/sample" @@ -413,188 +413,191 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { }) } -func Test_ParseOutboundReceivedValue(t *testing.T) { - chainID := chains.Ethereum.ChainId - connector, connectorAddr, custody, custodyAddr := getContractsByChainID(t, chainID) - - t.Run("should parse and check ZetaReceived event from archived outbound receipt", func(t *testing.T) { - // load archived outbound receipt that contains ZetaReceived event - // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f - nonce := uint64(9718) - coinType := coin.CoinType_Zeta - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - chainID, - nonce, - testutils.EventZetaReceived, - ) - params := cctx.GetCurrentOutboundParam() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - connectorAddr, - connector, - custodyAddr, - custody, - ) - require.NoError(t, err) - require.True(t, params.Amount.BigInt().Cmp(value) == 0) - require.Equal(t, chains.ReceiveStatus_success, status) - }) - t.Run("should parse and check ZetaReverted event from archived outbound receipt", func(t *testing.T) { - // load archived outbound receipt that contains ZetaReverted event - // use local network tx: 0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7 - localChainID := chains.GoerliLocalnet.ChainId - nonce := uint64(14) - coinType := coin.CoinType_Zeta - connectorLocal, connectorAddrLocal, custodyLocal, custodyAddrLocal := getContractsByChainID(t, localChainID) - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - localChainID, - nonce, - testutils.EventZetaReverted, - ) - params := cctx.GetCurrentOutboundParam() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, receipt, outbound, coinType, connectorAddrLocal, connectorLocal, custodyAddrLocal, custodyLocal) - require.NoError(t, err) - require.True(t, params.Amount.BigInt().Cmp(value) == 0) - require.Equal(t, chains.ReceiveStatus_success, status) - }) - t.Run("should parse and check ERC20 Withdrawn event from archived outbound receipt", func(t *testing.T) { - // load archived outbound receipt that contains ERC20 Withdrawn event - // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 - nonce := uint64(8014) - coinType := coin.CoinType_ERC20 - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - chainID, - nonce, - testutils.EventERC20Withdraw, - ) - params := cctx.GetCurrentOutboundParam() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - connectorAddr, - connector, - custodyAddr, - custody, - ) - require.NoError(t, err) - require.True(t, params.Amount.BigInt().Cmp(value) == 0) - require.Equal(t, chains.ReceiveStatus_success, status) - }) - t.Run("nothing to parse if coinType is Gas", func(t *testing.T) { - // load archived outbound receipt of Gas token transfer - // https://etherscan.io/tx/0xd13b593eb62b5500a00e288cc2fb2c8af1339025c0e6bc6183b8bef2ebbed0d3 - nonce := uint64(7260) - coinType := coin.CoinType_Gas - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt(t, TestDataDir, chainID, nonce, "") - params := cctx.GetCurrentOutboundParam() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - connectorAddr, - connector, - custodyAddr, - custody, - ) - require.NoError(t, err) - require.True(t, params.Amount.BigInt().Cmp(value) == 0) - require.Equal(t, chains.ReceiveStatus_success, status) - }) - t.Run("should fail on unknown coin type", func(t *testing.T) { - // load archived outbound receipt that contains ZetaReceived event - // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f - nonce := uint64(9718) - coinType := coin.CoinType(5) // unknown coin type - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - chainID, - nonce, - testutils.EventZetaReceived, - ) - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - connectorAddr, - connector, - custodyAddr, - custody, - ) - require.ErrorContains(t, err, "unknown coin type") - require.Nil(t, value) - require.Equal(t, chains.ReceiveStatus_failed, status) - }) - t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { - // load archived outbound receipt that contains ZetaReceived event - // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f - nonce := uint64(9718) - coinType := coin.CoinType_Zeta - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - chainID, - nonce, - testutils.EventZetaReceived, - ) - - // use an arbitrary address to make event parsing fail - fakeConnectorAddress := sample.EthAddress() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - fakeConnectorAddress, - connector, - custodyAddr, - custody, - ) - require.Error(t, err) - require.Nil(t, value) - require.Equal(t, chains.ReceiveStatus_failed, status) - }) - t.Run("should fail if unable to parse ERC20 Withdrawn event", func(t *testing.T) { - // load archived outbound receipt that contains ERC20 Withdrawn event - // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 - nonce := uint64(8014) - coinType := coin.CoinType_ERC20 - cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( - t, - TestDataDir, - chainID, - nonce, - testutils.EventERC20Withdraw, - ) - - // use an arbitrary address to make event parsing fail - fakeCustodyAddress := sample.EthAddress() - value, status, err := observer.ParseOutboundReceivedValue( - cctx, - receipt, - outbound, - coinType, - connectorAddr, - connector, - fakeCustodyAddress, - custody, - ) - require.Error(t, err) - require.Nil(t, value) - require.Equal(t, chains.ReceiveStatus_failed, status) - }) -} +// TODO: create mocks for gateway and ERC20CustodyV2 and uncomment these tests +// https://github.com/zeta-chain/node/issues/2669 +// +//func Test_ParseOutboundReceivedValue(t *testing.T) { +// chainID := chains.Ethereum.ChainId +// connector, connectorAddr, custody, custodyAddr := getContractsByChainID(t, chainID) +// +// t.Run("should parse and check ZetaReceived event from archived outbound receipt", func(t *testing.T) { +// // load archived outbound receipt that contains ZetaReceived event +// // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f +// nonce := uint64(9718) +// coinType := coin.CoinType_Zeta +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// chainID, +// nonce, +// testutils.EventZetaReceived, +// ) +// params := cctx.GetCurrentOutboundParam() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// connectorAddr, +// connector, +// custodyAddr, +// custody, +// ) +// require.NoError(t, err) +// require.True(t, params.Amount.BigInt().Cmp(value) == 0) +// require.Equal(t, chains.ReceiveStatus_success, status) +// }) +// t.Run("should parse and check ZetaReverted event from archived outbound receipt", func(t *testing.T) { +// // load archived outbound receipt that contains ZetaReverted event +// // use local network tx: 0x1487e6a31dd430306667250b72bf15b8390b73108b69f3de5c1b2efe456036a7 +// localChainID := chains.GoerliLocalnet.ChainId +// nonce := uint64(14) +// coinType := coin.CoinType_Zeta +// connectorLocal, connectorAddrLocal, custodyLocal, custodyAddrLocal := getContractsByChainID(t, localChainID) +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// localChainID, +// nonce, +// testutils.EventZetaReverted, +// ) +// params := cctx.GetCurrentOutboundParam() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, receipt, outbound, coinType, connectorAddrLocal, connectorLocal, custodyAddrLocal, custodyLocal) +// require.NoError(t, err) +// require.True(t, params.Amount.BigInt().Cmp(value) == 0) +// require.Equal(t, chains.ReceiveStatus_success, status) +// }) +// t.Run("should parse and check ERC20 Withdrawn event from archived outbound receipt", func(t *testing.T) { +// // load archived outbound receipt that contains ERC20 Withdrawn event +// // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 +// nonce := uint64(8014) +// coinType := coin.CoinType_ERC20 +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// chainID, +// nonce, +// testutils.EventERC20Withdraw, +// ) +// params := cctx.GetCurrentOutboundParam() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// connectorAddr, +// connector, +// custodyAddr, +// custody, +// ) +// require.NoError(t, err) +// require.True(t, params.Amount.BigInt().Cmp(value) == 0) +// require.Equal(t, chains.ReceiveStatus_success, status) +// }) +// t.Run("nothing to parse if coinType is Gas", func(t *testing.T) { +// // load archived outbound receipt of Gas token transfer +// // https://etherscan.io/tx/0xd13b593eb62b5500a00e288cc2fb2c8af1339025c0e6bc6183b8bef2ebbed0d3 +// nonce := uint64(7260) +// coinType := coin.CoinType_Gas +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt(t, TestDataDir, chainID, nonce, "") +// params := cctx.GetCurrentOutboundParam() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// connectorAddr, +// connector, +// custodyAddr, +// custody, +// ) +// require.NoError(t, err) +// require.True(t, params.Amount.BigInt().Cmp(value) == 0) +// require.Equal(t, chains.ReceiveStatus_success, status) +// }) +// t.Run("should fail on unknown coin type", func(t *testing.T) { +// // load archived outbound receipt that contains ZetaReceived event +// // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f +// nonce := uint64(9718) +// coinType := coin.CoinType(5) // unknown coin type +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// chainID, +// nonce, +// testutils.EventZetaReceived, +// ) +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// connectorAddr, +// connector, +// custodyAddr, +// custody, +// ) +// require.ErrorContains(t, err, "unknown coin type") +// require.Nil(t, value) +// require.Equal(t, chains.ReceiveStatus_failed, status) +// }) +// t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { +// // load archived outbound receipt that contains ZetaReceived event +// // https://etherscan.io/tx/0x81342051b8a85072d3e3771c1a57c7bdb5318e8caf37f5a687b7a91e50a7257f +// nonce := uint64(9718) +// coinType := coin.CoinType_Zeta +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// chainID, +// nonce, +// testutils.EventZetaReceived, +// ) +// +// // use an arbitrary address to make event parsing fail +// fakeConnectorAddress := sample.EthAddress() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// fakeConnectorAddress, +// connector, +// custodyAddr, +// custody, +// ) +// require.Error(t, err) +// require.Nil(t, value) +// require.Equal(t, chains.ReceiveStatus_failed, status) +// }) +// t.Run("should fail if unable to parse ERC20 Withdrawn event", func(t *testing.T) { +// // load archived outbound receipt that contains ERC20 Withdrawn event +// // https://etherscan.io/tx/0xd2eba7ac3da1b62800165414ea4bcaf69a3b0fb9b13a0fc32f4be11bfef79146 +// nonce := uint64(8014) +// coinType := coin.CoinType_ERC20 +// cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( +// t, +// TestDataDir, +// chainID, +// nonce, +// testutils.EventERC20Withdraw, +// ) +// +// // use an arbitrary address to make event parsing fail +// fakeCustodyAddress := sample.EthAddress() +// value, status, err := observer.parseOutboundReceivedValue( +// cctx, +// receipt, +// outbound, +// coinType, +// connectorAddr, +// connector, +// fakeCustodyAddress, +// custody, +// ) +// require.Error(t, err) +// require.Nil(t, value) +// require.Equal(t, chains.ReceiveStatus_failed, status) +// }) +//} diff --git a/zetaclient/chains/evm/observer/v2_inbound.go b/zetaclient/chains/evm/observer/v2_inbound.go new file mode 100644 index 0000000000..88ddcf8017 --- /dev/null +++ b/zetaclient/chains/evm/observer/v2_inbound.go @@ -0,0 +1,330 @@ +package observer + +import ( + "bytes" + "context" + "encoding/hex" + "sort" + + "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/crypto" + "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/evm" + "github.com/zeta-chain/zetacore/zetaclient/compliance" + "github.com/zeta-chain/zetacore/zetaclient/config" + "github.com/zeta-chain/zetacore/zetaclient/metrics" + "github.com/zeta-chain/zetacore/zetaclient/zetacore" +) + +// checkEventProcessability checks if the event is processable +func (ob *Observer) checkEventProcessability( + sender, receiver ethcommon.Address, + txHash ethcommon.Hash, + payload []byte, +) bool { + // compliance check + if config.ContainRestrictedAddress(sender.Hex(), receiver.Hex()) { + compliance.PrintComplianceLog( + ob.Logger().Inbound, + ob.Logger().Compliance, + false, + ob.Chain().ChainId, + txHash.Hex(), + sender.Hex(), + receiver.Hex(), + "Deposit", + ) + return false + } + + // donation check + if bytes.Equal(payload, []byte(constant.DonationMessage)) { + logFields := map[string]any{ + "chain": ob.Chain().ChainId, + "tx": txHash.Hex(), + } + ob.Logger().Inbound.Info().Fields(logFields). + Msgf("thank you rich folk for your donation!") + return false + } + + return true +} + +// ObserveGatewayDeposit queries the gateway contract for deposit events +// returns the last block successfully scanned +func (ob *Observer) ObserveGatewayDeposit(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { + // filter ERC20CustodyDeposited logs + gatewayAddr, gatewayContract, err := ob.GetGatewayContract() + if err != nil { + // lastScanned is startBlock - 1 + return startBlock - 1, errors.Wrap(err, "can't get gateway contract") + } + + // get iterator for the events for the block range + eventIterator, err := gatewayContract.FilterDeposited(&bind.FilterOpts{ + Start: startBlock, + End: &toBlock, + Context: ctx, + }, []ethcommon.Address{}, []ethcommon.Address{}) + if err != nil { + return startBlock - 1, errors.Wrapf( + err, + "error filtering deposits from block %d to %d for chain %d", + startBlock, + toBlock, + ob.Chain().ChainId, + ) + } + + // parse and validate events + events := ob.parseAndValidateDepositEvents(eventIterator, gatewayAddr) + + // increment prom counter + metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() + + // post to zetacore + lastScanned := uint64(0) + for _, event := range events { + // remember which block we are scanning (there could be multiple events in the same block) + if event.Raw.BlockNumber > lastScanned { + lastScanned = event.Raw.BlockNumber + } + + // check if the event is processable + if !ob.checkEventProcessability(event.Sender, event.Receiver, event.Raw.TxHash, event.Payload) { + continue + } + + msg := ob.newDepositInboundVote(event) + + ob.Logger().Inbound.Info(). + Msgf("ObserveGateway: Deposit inbound detected on chain %d tx %s block %d from %s value %s message %s", + ob.Chain(). + ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.Sender.Hex(), event.Amount.String(), hex.EncodeToString(event.Payload)) + + _, err = ob.PostVoteInbound(ctx, &msg, zetacore.PostVoteInboundExecutionGasLimit) + if err != nil { + // decrement the last scanned block so we have to re-scan from this block next time + return lastScanned - 1, errors.Wrap(err, "error posting vote inbound") + } + } + + // successfully processed all events in [startBlock, toBlock] + return toBlock, nil +} + +// parseAndValidateDepositEvents collects and sorts events by block number, tx index, and log index +func (ob *Observer) parseAndValidateDepositEvents( + iterator *gatewayevm.GatewayEVMDepositedIterator, + gatewayAddr ethcommon.Address, +) []*gatewayevm.GatewayEVMDeposited { + // collect and sort events by block number, then tx index, then log index (ascending) + events := make([]*gatewayevm.GatewayEVMDeposited, 0) + for iterator.Next() { + events = append(events, iterator.Event) + err := evm.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", evm.TopicsGatewayDeposit) + if err == nil { + events = append(events, iterator.Event) + continue + } + ob.Logger().Inbound.Warn(). + Err(err). + Msgf("ObserveGateway: invalid Deposited event in tx %s on chain %d at height %d", + iterator.Event.Raw.TxHash.Hex(), ob.Chain().ChainId, iterator.Event.Raw.BlockNumber) + } + sort.SliceStable(events, func(i, j int) bool { + if events[i].Raw.BlockNumber == events[j].Raw.BlockNumber { + if events[i].Raw.TxIndex == events[j].Raw.TxIndex { + return events[i].Raw.Index < events[j].Raw.Index + } + return events[i].Raw.TxIndex < events[j].Raw.TxIndex + } + return events[i].Raw.BlockNumber < events[j].Raw.BlockNumber + }) + + // filter events from same tx + filtered := make([]*gatewayevm.GatewayEVMDeposited, 0) + guard := make(map[string]bool) + for _, event := range events { + // guard against multiple events in the same tx + if guard[event.Raw.TxHash.Hex()] { + ob.Logger().Inbound.Warn(). + Msgf("ObserveGateway: multiple remote call events detected in same tx %s", event.Raw.TxHash) + continue + } + guard[event.Raw.TxHash.Hex()] = true + filtered = append(filtered, event) + } + + return filtered +} + +// newDepositInboundVote creates a MsgVoteInbound message for a Deposit event +func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) types.MsgVoteInbound { + // if event.Asset is zero, it's a native token + coinType := coin.CoinType_ERC20 + if crypto.IsEmptyAddress(event.Asset) { + coinType = coin.CoinType_Gas + } + + return *types.NewMsgVoteInbound( + ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), + event.Sender.Hex(), + ob.Chain().ChainId, + "", + event.Receiver.Hex(), + ob.ZetacoreClient().Chain().ChainId, + sdkmath.NewUintFromBigInt(event.Amount), + hex.EncodeToString(event.Payload), + event.Raw.TxHash.Hex(), + event.Raw.BlockNumber, + 1_500_000, + coinType, + event.Asset.Hex(), + event.Raw.Index, + types.ProtocolContractVersion_V2, + types.WithEVMRevertOptions(event.RevertOptions), + ) +} + +// ObserveGatewayCall queries the gateway contract for call events +// returns the last block successfully scanned +// TODO: there are lot of similarities between this function and ObserveGatewayDeposit +// logic should be factorized using interfaces and generics +// https://github.com/zeta-chain/node/issues/2493 +func (ob *Observer) ObserveGatewayCall(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { + // filter ERC20CustodyDeposited logs + gatewayAddr, gatewayContract, err := ob.GetGatewayContract() + if err != nil { + // lastScanned is startBlock - 1 + return startBlock - 1, errors.Wrap(err, "can't get gateway contract") + } + + // get iterator for the events for the block range + eventIterator, err := gatewayContract.FilterCalled(&bind.FilterOpts{ + Start: startBlock, + End: &toBlock, + Context: ctx, + }, []ethcommon.Address{}, []ethcommon.Address{}) + if err != nil { + return startBlock - 1, errors.Wrapf( + err, + "error filtering calls from block %d to %d for chain %d", + startBlock, + toBlock, + ob.Chain().ChainId, + ) + } + + // parse and validate events + events := ob.parseAndValidateCallEvents(eventIterator, gatewayAddr) + + // increment prom counter + metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() + + // post to zetacore + lastScanned := uint64(0) + for _, event := range events { + // remember which block we are scanning (there could be multiple events in the same block) + if event.Raw.BlockNumber > lastScanned { + lastScanned = event.Raw.BlockNumber + } + + // check if the event is processable + if !ob.checkEventProcessability(event.Sender, event.Receiver, event.Raw.TxHash, event.Payload) { + continue + } + + msg := ob.newCallInboundVote(event) + + ob.Logger().Inbound.Info(). + Msgf("ObserveGateway: Call inbound detected on chain %d tx %s block %d from %s value message %s", + ob.Chain(). + ChainId, event.Raw.TxHash.Hex(), event.Raw.BlockNumber, event.Sender.Hex(), hex.EncodeToString(event.Payload)) + + _, err = ob.PostVoteInbound(ctx, &msg, zetacore.PostVoteInboundExecutionGasLimit) + if err != nil { + // decrement the last scanned block so we have to re-scan from this block next time + return lastScanned - 1, errors.Wrap(err, "error posting vote inbound") + } + } + + // successfully processed all events in [startBlock, toBlock] + return toBlock, nil +} + +// parseAndValidateCallEvents collects and sorts events by block number, tx index, and log index +func (ob *Observer) parseAndValidateCallEvents( + iterator *gatewayevm.GatewayEVMCalledIterator, + gatewayAddr ethcommon.Address, +) []*gatewayevm.GatewayEVMCalled { + // collect and sort events by block number, then tx index, then log index (ascending) + events := make([]*gatewayevm.GatewayEVMCalled, 0) + for iterator.Next() { + events = append(events, iterator.Event) + err := evm.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", evm.TopicsGatewayCall) + if err == nil { + events = append(events, iterator.Event) + continue + } + ob.Logger().Inbound.Warn(). + Err(err). + Msgf("ObserveGateway: invalid Call event in tx %s on chain %d at height %d", + iterator.Event.Raw.TxHash.Hex(), ob.Chain().ChainId, iterator.Event.Raw.BlockNumber) + } + sort.SliceStable(events, func(i, j int) bool { + if events[i].Raw.BlockNumber == events[j].Raw.BlockNumber { + if events[i].Raw.TxIndex == events[j].Raw.TxIndex { + return events[i].Raw.Index < events[j].Raw.Index + } + return events[i].Raw.TxIndex < events[j].Raw.TxIndex + } + return events[i].Raw.BlockNumber < events[j].Raw.BlockNumber + }) + + // filter events from same tx + filtered := make([]*gatewayevm.GatewayEVMCalled, 0) + guard := make(map[string]bool) + for _, event := range events { + // guard against multiple events in the same tx + if guard[event.Raw.TxHash.Hex()] { + ob.Logger().Inbound.Warn(). + Msgf("ObserveGateway: multiple remote call events detected in same tx %s", event.Raw.TxHash) + continue + } + guard[event.Raw.TxHash.Hex()] = true + filtered = append(filtered, event) + } + + return filtered +} + +// newCallInboundVote creates a MsgVoteInbound message for a Call event +func (ob *Observer) newCallInboundVote(event *gatewayevm.GatewayEVMCalled) types.MsgVoteInbound { + return *types.NewMsgVoteInbound( + ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), + event.Sender.Hex(), + ob.Chain().ChainId, + "", + event.Receiver.Hex(), + ob.ZetacoreClient().Chain().ChainId, + sdkmath.ZeroUint(), + hex.EncodeToString(event.Payload), + event.Raw.TxHash.Hex(), + event.Raw.BlockNumber, + 1_500_000, + coin.CoinType_NoAssetCall, + "", + event.Raw.Index, + types.ProtocolContractVersion_V2, + types.WithEVMRevertOptions(event.RevertOptions), + ) +} diff --git a/zetaclient/chains/evm/observer/v2_outbound.go b/zetaclient/chains/evm/observer/v2_outbound.go new file mode 100644 index 0000000000..3ad5a33601 --- /dev/null +++ b/zetaclient/chains/evm/observer/v2_outbound.go @@ -0,0 +1,303 @@ +package observer + +import ( + "bytes" + "encoding/hex" + "fmt" + "math/big" + "strings" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + + "github.com/zeta-chain/zetacore/pkg/chains" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/evm" +) + +// parseOutboundEventV2 parses an event from an outbound with protocol contract v2 +func parseOutboundEventV2( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + transaction *ethtypes.Transaction, + custodyAddr ethcommon.Address, + custody *erc20custody.ERC20Custody, + gatewayAddr ethcommon.Address, + gateway *gatewayevm.GatewayEVM, +) (*big.Int, chains.ReceiveStatus, error) { + // return failed status if receipt status is failed + if receipt.Status == ethtypes.ReceiptStatusFailed { + return big.NewInt(0), chains.ReceiveStatus_failed, nil + } + + outboundType := evm.ParseOutboundTypeFromCCTX(*cctx) + switch outboundType { + case evm.OutboundTypeGasWithdraw, evm.OutboundTypeGasWithdrawRevert: + // simple transfer, no need to parse event + return transaction.Value(), chains.ReceiveStatus_success, nil + case evm.OutboundTypeERC20Withdraw, evm.OutboundTypeERC20WithdrawRevert: + return parseAndCheckERC20CustodyWithdraw(cctx, receipt, custodyAddr, custody) + case evm.OutboundTypeERC20WithdrawAndCall: + return parseAndCheckERC20CustodyWithdrawAndCall(cctx, receipt, custodyAddr, custody) + case evm.OutboundTypeGasWithdrawAndCall, evm.OutboundTypeCall: + // both gas withdraw and call and no-asset call uses gateway execute + // no-asset call simply hash msg.value == 0 + return parseAndCheckGatewayExecuted(cctx, receipt, gatewayAddr, gateway) + case evm.OutboundTypeGasWithdrawRevertAndCallOnRevert, evm.OutboundTypeERC20WithdrawRevertAndCallOnRevert: + return parseAndCheckGatewayReverted(cctx, receipt, gatewayAddr, gateway) + } + return big.NewInt(0), chains.ReceiveStatus_failed, fmt.Errorf("unsupported outbound type %d", outboundType) +} + +// parseAndCheckGatewayExecuted parses and checks the gateway execute event +func parseAndCheckGatewayExecuted( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + gatewayAddr ethcommon.Address, + gateway *gatewayevm.GatewayEVM, +) (*big.Int, chains.ReceiveStatus, error) { + params := cctx.GetCurrentOutboundParam() + + for _, vLog := range receipt.Logs { + executed, err := gateway.GatewayEVMFilterer.ParseExecuted(*vLog) + if err != nil { + continue + } + // basic event check + if err := evm.ValidateEvmTxLog(vLog, gatewayAddr, receipt.TxHash.Hex(), evm.TopicsGatewayExecuted); err != nil { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, errors.Wrap( + err, + "failed to validate gateway executed event", + ) + } + // destination + if !strings.EqualFold(executed.Destination.Hex(), params.Receiver) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "receiver address mismatch in event, want %s got %s", + params.Receiver, + executed.Destination.Hex(), + ) + } + // amount + if executed.Value.Cmp(params.Amount.BigInt()) != 0 { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "amount mismatch in event, want %s got %s", + params.Amount.String(), + executed.Value.String(), + ) + } + // data + if err := checkCCTXMessage(executed.Data, cctx.RelayedMessage); err != nil { + return big.NewInt(0), chains.ReceiveStatus_failed, err + } + + return executed.Value, chains.ReceiveStatus_success, nil + } + + return big.NewInt(0), chains.ReceiveStatus_failed, errors.New("gateway execute event not found") +} + +// parseAndCheckGatewayReverted parses and checks the gateway reverted event +func parseAndCheckGatewayReverted( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + gatewayAddr ethcommon.Address, + gateway *gatewayevm.GatewayEVM, +) (*big.Int, chains.ReceiveStatus, error) { + params := cctx.GetCurrentOutboundParam() + + for _, vLog := range receipt.Logs { + reverted, err := gateway.GatewayEVMFilterer.ParseReverted(*vLog) + if err != nil { + continue + } + // basic event check + if err := evm.ValidateEvmTxLog(vLog, gatewayAddr, receipt.TxHash.Hex(), evm.TopicsGatewayReverted); err != nil { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, errors.Wrap( + err, + "failed to validate gateway reverte event", + ) + } + // destination + if !strings.EqualFold(reverted.To.Hex(), params.Receiver) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "receiver address mismatch in event, want %s got %s", + params.Receiver, + reverted.To.Hex(), + ) + } + // token + if !strings.EqualFold(reverted.Token.Hex(), cctx.InboundParams.Asset) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "asset address mismatch in event, want %s got %s", + cctx.InboundParams.Asset, + reverted.Token.Hex(), + ) + } + // amount + if reverted.Amount.Cmp(params.Amount.BigInt()) != 0 { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "amount mismatch in event, want %s got %s", + params.Amount.String(), + reverted.Amount.String(), + ) + } + + return reverted.Amount, chains.ReceiveStatus_success, nil + } + + return big.NewInt(0), chains.ReceiveStatus_failed, errors.New("erc20 custody withdraw event not found") +} + +// parseAndCheckERC20CustodyWithdraw parses and checks the ERC20 custody withdraw event +func parseAndCheckERC20CustodyWithdraw( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + custodyAddr ethcommon.Address, + custody *erc20custody.ERC20Custody, +) (*big.Int, chains.ReceiveStatus, error) { + params := cctx.GetCurrentOutboundParam() + + for _, vLog := range receipt.Logs { + withdrawn, err := custody.ERC20CustodyFilterer.ParseWithdrawn(*vLog) + if err != nil { + continue + } + // basic event check + if err := evm.ValidateEvmTxLog(vLog, custodyAddr, receipt.TxHash.Hex(), evm.TopicsERC20CustodyWithdraw); err != nil { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, errors.Wrap( + err, + "failed to validate erc20 custody withdrawn event", + ) + } + // destination + if !strings.EqualFold(withdrawn.To.Hex(), params.Receiver) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "receiver address mismatch in event, want %s got %s", + params.Receiver, + withdrawn.To.Hex(), + ) + } + // token + if !strings.EqualFold(withdrawn.Token.Hex(), cctx.InboundParams.Asset) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "asset address mismatch in event, want %s got %s", + cctx.InboundParams.Asset, + withdrawn.Token.Hex(), + ) + } + // amount + if withdrawn.Amount.Cmp(params.Amount.BigInt()) != 0 { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "amount mismatch in event, want %s got %s", + params.Amount.String(), + withdrawn.Amount.String(), + ) + } + + return withdrawn.Amount, chains.ReceiveStatus_success, nil + } + + return big.NewInt(0), chains.ReceiveStatus_failed, errors.New("erc20 custody withdraw event not found") +} + +// parseAndCheckERC20CustodyWithdrawAndCall parses and checks the ERC20 custody withdraw and call event +func parseAndCheckERC20CustodyWithdrawAndCall( + cctx *crosschaintypes.CrossChainTx, + receipt *ethtypes.Receipt, + custodyAddr ethcommon.Address, + custody *erc20custody.ERC20Custody, +) (*big.Int, chains.ReceiveStatus, error) { + params := cctx.GetCurrentOutboundParam() + + for _, vLog := range receipt.Logs { + withdrawn, err := custody.ERC20CustodyFilterer.ParseWithdrawnAndCalled(*vLog) + if err != nil { + continue + } + // basic event check + if err := evm.ValidateEvmTxLog(vLog, custodyAddr, receipt.TxHash.Hex(), evm.TopicsERC20CustodyWithdrawAndCall); err != nil { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, errors.Wrap( + err, + "failed to validate erc20 custody withdraw and call event", + ) + } + // destination + if !strings.EqualFold(withdrawn.To.Hex(), params.Receiver) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "receiver address mismatch in event, want %s got %s", + params.Receiver, + withdrawn.To.Hex(), + ) + } + // token + if !strings.EqualFold(withdrawn.Token.Hex(), cctx.InboundParams.Asset) { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "asset address mismatch in event, want %s got %s", + cctx.InboundParams.Asset, + withdrawn.Token.Hex(), + ) + } + // amount + if withdrawn.Amount.Cmp(params.Amount.BigInt()) != 0 { + return big.NewInt( + 0, + ), chains.ReceiveStatus_failed, fmt.Errorf( + "amount mismatch in event, want %s got %s", + params.Amount.String(), + withdrawn.Amount.String(), + ) + } + // data + if err := checkCCTXMessage(withdrawn.Data, cctx.RelayedMessage); err != nil { + return big.NewInt(0), chains.ReceiveStatus_failed, err + } + + return withdrawn.Amount, chains.ReceiveStatus_success, nil + } + + return big.NewInt(0), chains.ReceiveStatus_failed, errors.New("erc20 custody withdraw and call event not found") +} + +// checkCCTXMessage checks the message of cctx with the emitted data of the event +func checkCCTXMessage(emittedData []byte, message string) error { + messageBytes, err := hex.DecodeString(message) + if err != nil { + return errors.Wrap(err, "failed to decode message") + } + if !bytes.Equal(emittedData, messageBytes) { + return fmt.Errorf("message mismatch, want %s got %s", message, hex.EncodeToString(emittedData)) + } + return nil +} diff --git a/zetaclient/chains/evm/signer/outbound_data.go b/zetaclient/chains/evm/signer/outbound_data.go index dbeef3626d..3af1338b6b 100644 --- a/zetaclient/chains/evm/signer/outbound_data.go +++ b/zetaclient/chains/evm/signer/outbound_data.go @@ -39,6 +39,9 @@ type OutboundData struct { // outboundParams field contains data detailing the receiver chain and outbound transaction outboundParams *types.OutboundParams + + // revertOptions field contains data detailing the revert options + revertOptions types.RevertOptions } // NewOutboundData creates OutboundData from the given CCTX. @@ -63,10 +66,22 @@ func NewOutboundData( return nil, false, errors.Wrap(err, "unable to get app from context") } - // recipient + destination chain - to, toChainID, skip := getDestination(cctx, logger) - if skip { - return nil, true, nil + var ( + to ethcommon.Address + toChainID *big.Int + ) + + // in protocol contract v2, receiver is always set in the outbound + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 { + to = ethcommon.HexToAddress(cctx.GetCurrentOutboundParam().Receiver) + toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) + } else { + // recipient + destination chain + var skip bool + to, toChainID, skip = getDestination(cctx, logger) + if skip { + return nil, true, nil + } } // ensure that chain exists in app's context @@ -87,11 +102,20 @@ func NewOutboundData( // Base64 decode message var message []byte if cctx.InboundParams.CoinType != coin.CoinType_Cmd { - msg, errDecode := base64.StdEncoding.DecodeString(cctx.RelayedMessage) - if errDecode != nil { - logger.Err(err).Str("cctx.relayed_message", cctx.RelayedMessage).Msg("Unable to decode relayed message") + // protocol contract v2 uses hex encoding + if cctx.ProtocolContractVersion == types.ProtocolContractVersion_V2 { + message, err = hex.DecodeString(cctx.RelayedMessage) + if err != nil { + logger.Err(err).Msgf("decode CCTX.Message %s error", cctx.RelayedMessage) + message = []byte{} + } } else { - message = msg + msg, errDecode := base64.StdEncoding.DecodeString(cctx.RelayedMessage) + if errDecode != nil { + logger.Err(err).Str("cctx.relayed_message", cctx.RelayedMessage).Msg("Unable to decode relayed message") + } else { + message = msg + } } } @@ -114,6 +138,8 @@ func NewOutboundData( cctxIndex: cctxIndex, outboundParams: outboundParams, + + revertOptions: cctx.RevertOptions, }, false, nil } diff --git a/zetaclient/chains/evm/signer/sign.go b/zetaclient/chains/evm/signer/sign.go new file mode 100644 index 0000000000..b524849786 --- /dev/null +++ b/zetaclient/chains/evm/signer/sign.go @@ -0,0 +1,230 @@ +package signer + +import ( + "context" + "fmt" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + connectorevm "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.base.sol" + + "github.com/zeta-chain/zetacore/zetaclient/chains/evm" +) + +// SignConnectorOnReceive +// function onReceive( +// +// bytes calldata originSenderAddress, +// uint256 originChainId, +// address destinationAddress, +// uint zetaAmount, +// bytes calldata message, +// bytes32 internalSendHash +// +// ) external virtual {} +func (signer *Signer) SignConnectorOnReceive(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + var data []byte + var err error + + zetaConnectorABI, err := connectorevm.ZetaConnectorBaseMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ZetaConnectorZEVMMetaData ABI") + } + + data, err = zetaConnectorABI.Pack("onReceive", + txData.sender.Bytes(), + txData.srcChainID, + txData.to, + txData.amount, + txData.message, + txData.cctxIndex) + if err != nil { + return nil, errors.Wrap(err, "onReceive pack error") + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.zetaConnectorAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height) + if err != nil { + return nil, errors.Wrap(err, "sign onReceive error") + } + + return tx, nil +} + +// SignConnectorOnRevert +// function onRevert( +// address originSenderAddress, +// uint256 originChainId, +// bytes calldata destinationAddress, +// uint256 destinationChainId, +// uint256 zetaAmount, +// bytes calldata message, +// bytes32 internalSendHash +// ) external override whenNotPaused onlyTssAddress +func (signer *Signer) SignConnectorOnRevert(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + var data []byte + var err error + + zetaConnectorABI, err := connectorevm.ZetaConnectorBaseMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ZetaConnectorZEVMMetaData ABI") + } + + data, err = zetaConnectorABI.Pack("onRevert", + txData.sender, + txData.srcChainID, + txData.to.Bytes(), + txData.toChainID, + txData.amount, + txData.message, + txData.cctxIndex) + if err != nil { + return nil, errors.Wrap(err, "onRevert pack error") + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.zetaConnectorAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height) + if err != nil { + return nil, errors.Wrap(err, "sign onRevert error") + } + + return tx, nil +} + +// SignCancel signs a transaction from TSS address to itself with a zero amount in order to increment the nonce +func (signer *Signer) SignCancel(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + txData.gas.Limit = evm.EthTransferGasLimit + tx, _, _, err := signer.Sign( + ctx, + nil, + signer.TSS().EVMAddress(), + zeroValue, // zero out the amount to cancel the tx + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, errors.Wrap(err, "SignCancel error") + } + + return tx, nil +} + +// SignGasWithdraw signs a withdrawal transaction sent from the TSS address to the destination +func (signer *Signer) SignGasWithdraw(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + txData.gas.Limit = evm.EthTransferGasLimit + tx, _, _, err := signer.Sign( + ctx, + nil, + txData.to, + txData.amount, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, errors.Wrap(err, "SignGasWithdraw error") + } + + return tx, nil +} + +// SignERC20Withdraw +// function withdraw( +// address recipient, +// address asset, +// uint256 amount, +// ) external onlyTssAddress +func (signer *Signer) SignERC20Withdraw(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + var data []byte + var err error + + erc20CustodyV1ABI, err := erc20custody.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ERC20CustodyMetaData ABI") + } + + data, err = erc20CustodyV1ABI.Pack("withdraw", txData.to, txData.asset, txData.amount) + if err != nil { + return nil, fmt.Errorf("withdraw pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.er20CustodyAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign withdraw error: %w", err) + } + + return tx, nil +} + +// SignWhitelistERC20Cmd signs a whitelist command for ERC20 token +func (signer *Signer) SignWhitelistERC20Cmd( + ctx context.Context, + txData *OutboundData, + params string, +) (*ethtypes.Transaction, error) { + erc20 := ethcommon.HexToAddress(params) + if erc20 == (ethcommon.Address{}) { + return nil, fmt.Errorf("SignCommandTx: invalid erc20 address %s", params) + } + custodyAbi, err := erc20custody.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, err + } + data, err := custodyAbi.Pack("whitelist", erc20) + if err != nil { + return nil, fmt.Errorf("whitelist pack error: %w", err) + } + tx, _, _, err := signer.Sign( + ctx, + data, + txData.to, + zeroValue, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign whitelist error: %w", err) + } + return tx, nil +} + +// SignMigrateTssFundsCmd signs a migrate TSS funds command +func (signer *Signer) SignMigrateTssFundsCmd(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + tx, _, _, err := signer.Sign( + ctx, + nil, + txData.to, + txData.amount, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("SignMigrateTssFundsCmd error: %w", err) + } + return tx, nil +} diff --git a/zetaclient/chains/evm/signer/sign_test.go b/zetaclient/chains/evm/signer/sign_test.go new file mode 100644 index 0000000000..e8fdca3f24 --- /dev/null +++ b/zetaclient/chains/evm/signer/sign_test.go @@ -0,0 +1,252 @@ +package signer + +import ( + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" + "math/big" + "testing" +) + +func TestSigner_SignConnectorOnReceive(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignConnectorOnReceive - should successfully sign", func(t *testing.T) { + // Call SignConnectorOnReceive + tx, err := evmSigner.SignConnectorOnReceive(ctx, txData) + require.NoError(t, err) + + // Verify Signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + }) + t.Run("SignConnectorOnReceive - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignConnectorOnReceive + tx, err := evmSigner.SignConnectorOnReceive(ctx, txData) + require.ErrorContains(t, err, "sign onReceive error") + require.Nil(t, tx) + tss.Unpause() + }) + + t.Run("SignOutbound - should successfully sign LegacyTx", func(t *testing.T) { + // Call SignOutbound + tx, err := evmSigner.SignConnectorOnReceive(ctx, txData) + require.NoError(t, err) + + // Verify Signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // check that by default tx type is legacy tx + assert.Equal(t, ethtypes.LegacyTxType, int(tx.Type())) + }) + + t.Run("SignOutbound - should successfully sign DynamicFeeTx", func(t *testing.T) { + // ARRANGE + const ( + gwei = 1_000_000_000 + priorityFee = 1 * gwei + gasPrice = 3 * gwei + ) + + // Given a CCTX with gas price and priority fee + cctx := getCCTX(t) + cctx.OutboundParams[0].GasPrice = big.NewInt(gasPrice).String() + cctx.OutboundParams[0].GasPriorityFee = big.NewInt(priorityFee).String() + + // Given outbound data + txData, skip, err := NewOutboundData(ctx, cctx, 123, makeLogger(t)) + require.False(t, skip) + require.NoError(t, err) + + // Given a working TSS + tss.Unpause() + + // ACT + tx, err := evmSigner.SignConnectorOnReceive(ctx, txData) + require.NoError(t, err) + + // ASSERT + verifyTxSignature(t, tx, mocks.NewTSSMainnet().Pubkey(), evmSigner.EvmSigner()) + + // check that by default tx type is a dynamic fee tx + assert.Equal(t, ethtypes.DynamicFeeTxType, int(tx.Type())) + + // check that the gasPrice & priorityFee are set correctly + assert.Equal(t, int64(gasPrice), tx.GasFeeCap().Int64()) + assert.Equal(t, int64(priorityFee), tx.GasTipCap().Int64()) + }) +} + +func TestSigner_SignConnectorOnRevert(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignConnectorOnRevert - should successfully sign", func(t *testing.T) { + // Call SignConnectorOnRevert + tx, err := evmSigner.SignConnectorOnRevert(ctx, txData) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Revert tx calls connector contract with 0 gas token + verifyTxBodyBasics(t, tx, evmSigner.zetaConnectorAddress, txData.nonce, big.NewInt(0)) + }) + t.Run("SignConnectorOnRevert - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignConnectorOnRevert + tx, err := evmSigner.SignConnectorOnRevert(ctx, txData) + require.ErrorContains(t, err, "sign onRevert error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignCancel(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignCancel - should successfully sign", func(t *testing.T) { + // Call SignConnectorOnRevert + tx, err := evmSigner.SignCancel(ctx, txData) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Cancel tx sends 0 gas token to TSS self address + verifyTxBodyBasics(t, tx, evmSigner.TSS().EVMAddress(), txData.nonce, big.NewInt(0)) + }) + t.Run("SignCancel - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignCancel + tx, err := evmSigner.SignCancel(ctx, txData) + require.ErrorContains(t, err, "SignCancel error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignGasWithdraw(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignGasWithdraw - should successfully sign", func(t *testing.T) { + // Call SignGasWithdraw + tx, err := evmSigner.SignGasWithdraw(ctx, txData) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) + }) + t.Run("SignGasWithdraw - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call SignGasWithdraw + tx, err := evmSigner.SignGasWithdraw(ctx, txData) + require.ErrorContains(t, err, "SignGasWithdraw error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignERC20Withdraw(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignERC20WithdrawTx - should successfully sign", func(t *testing.T) { + // Call SignERC20WithdrawTx + tx, err := evmSigner.SignERC20Withdraw(ctx, txData) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Withdraw tx calls erc20 custody contract with 0 gas token + verifyTxBodyBasics(t, tx, evmSigner.er20CustodyAddress, txData.nonce, big.NewInt(0)) + }) + + t.Run("SignERC20WithdrawTx - should fail if keysign fails", func(t *testing.T) { + // pause tss to make keysign fail + tss.Pause() + + // Call SignERC20WithdrawTx + tx, err := evmSigner.SignERC20Withdraw(ctx, txData) + require.ErrorContains(t, err, "sign withdraw error") + require.Nil(t, tx) + }) +} diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 874c118e3e..c77d4e36e6 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -10,22 +10,20 @@ import ( "strings" "time" - "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/accounts/abi" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" ethrpc "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/x/crosschain/types" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" - "github.com/zeta-chain/zetacore/zetaclient/chains/evm" "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" "github.com/zeta-chain/zetacore/zetaclient/compliance" zctx "github.com/zeta-chain/zetacore/zetaclient/context" @@ -61,17 +59,14 @@ type Signer struct { // ethSigner encapsulates EVM transaction signature handling ethSigner ethtypes.Signer - // zetaConnectorABI is the ABI of the ZetaConnector contract - zetaConnectorABI abi.ABI - - // erc20CustodyABI is the ABI of the ERC20Custody contract - erc20CustodyABI abi.ABI - // zetaConnectorAddress is the address of the ZetaConnector contract zetaConnectorAddress ethcommon.Address // er20CustodyAddress is the address of the ERC20Custody contract er20CustodyAddress ethcommon.Address + + // gatewayAddress is the address of the Gateway contract + gatewayAddress ethcommon.Address } // NewSigner creates a new EVM signer @@ -82,10 +77,9 @@ func NewSigner( ts *metrics.TelemetryServer, logger base.Logger, endpoint string, - zetaConnectorABI string, - erc20CustodyABI string, zetaConnectorAddress ethcommon.Address, erc20CustodyAddress ethcommon.Address, + gatewayAddress ethcommon.Address, ) (*Signer, error) { // create base signer baseSigner := base.NewSigner(chain, tss, ts, logger) @@ -96,25 +90,13 @@ func NewSigner( return nil, errors.Wrap(err, "unable to create EVM client") } - // prepare ABIs - connectorABI, err := abi.JSON(strings.NewReader(zetaConnectorABI)) - if err != nil { - return nil, errors.Wrap(err, "unable to build ZetaConnector ABI") - } - - custodyABI, err := abi.JSON(strings.NewReader(erc20CustodyABI)) - if err != nil { - return nil, errors.Wrap(err, "unable to build ERC20Custody ABI") - } - return &Signer{ Signer: baseSigner, client: client, ethSigner: ethSigner, - zetaConnectorABI: connectorABI, - erc20CustodyABI: custodyABI, zetaConnectorAddress: zetaConnectorAddress, er20CustodyAddress: erc20CustodyAddress, + gatewayAddress: gatewayAddress, }, nil } @@ -154,9 +136,9 @@ func (signer *Signer) GetERC20CustodyAddress() ethcommon.Address { // GetGatewayAddress returns the gateway address func (signer *Signer) GetGatewayAddress() string { - // Note: return empty string for now - // gateway address will be needed in the future contract architecture - return "" + signer.Lock() + defer signer.Unlock() + return signer.gatewayAddress.String() } // Sign given data, and metadata (gas, nonce, etc) @@ -245,133 +227,13 @@ func (signer *Signer) broadcast(ctx context.Context, tx *ethtypes.Transaction) e return signer.client.SendTransaction(ctx, tx) } -// SignOutbound -// function onReceive( -// -// bytes calldata originSenderAddress, -// uint256 originChainId, -// address destinationAddress, -// uint zetaAmount, -// bytes calldata message, -// bytes32 internalSendHash -// -// ) external virtual {} -func (signer *Signer) SignOutbound(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - var data []byte - var err error - - data, err = signer.zetaConnectorABI.Pack("onReceive", - txData.sender.Bytes(), - txData.srcChainID, - txData.to, - txData.amount, - txData.message, - txData.cctxIndex) - if err != nil { - return nil, fmt.Errorf("onReceive pack error: %w", err) - } - - tx, _, _, err := signer.Sign( - ctx, - data, - signer.zetaConnectorAddress, - zeroValue, - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("sign onReceive error: %w", err) - } - - return tx, nil -} - -// SignRevertTx -// function onRevert( -// address originSenderAddress, -// uint256 originChainId, -// bytes calldata destinationAddress, -// uint256 destinationChainId, -// uint256 zetaAmount, -// bytes calldata message, -// bytes32 internalSendHash -// ) external override whenNotPaused onlyTssAddress -func (signer *Signer) SignRevertTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - data, err := signer.zetaConnectorABI.Pack("onRevert", - txData.sender, - txData.srcChainID, - txData.to.Bytes(), - txData.toChainID, - txData.amount, - txData.message, - txData.cctxIndex, - ) - if err != nil { - return nil, fmt.Errorf("onRevert pack error: %w", err) - } - - tx, _, _, err := signer.Sign( - ctx, - data, - signer.zetaConnectorAddress, - zeroValue, - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("sign onRevert error: %w", err) - } - - return tx, nil -} - -// SignCancelTx signs a transaction from TSS address to itself with a zero amount in order to increment the nonce -func (signer *Signer) SignCancelTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - txData.gas.Limit = evm.EthTransferGasLimit - tx, _, _, err := signer.Sign( - ctx, - nil, - signer.TSS().EVMAddress(), - zeroValue, // zero out the amount to cancel the tx - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("SignCancelTx error: %w", err) - } - - return tx, nil -} - -// SignWithdrawTx signs a withdrawal transaction sent from the TSS address to the destination -func (signer *Signer) SignWithdrawTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - txData.gas.Limit = evm.EthTransferGasLimit - tx, _, _, err := signer.Sign( - ctx, - nil, - txData.to, - txData.amount, - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("SignWithdrawTx error: %w", err) - } - - return tx, nil -} - // TryProcessOutbound - signer interface implementation // This function will attempt to build and sign an evm transaction using the TSS signer. // It will then broadcast the signed transaction to the outbound chain. // TODO(revamp): simplify function func (signer *Signer) TryProcessOutbound( ctx context.Context, - cctx *types.CrossChainTx, + cctx *crosschaintypes.CrossChainTx, outboundProc *outboundprocessor.Processor, outboundID string, _ interfaces.ChainObserver, @@ -430,10 +292,44 @@ func (signer *Signer) TryProcessOutbound( return } - // https://github.com/zeta-chain/node/issues/2050 - var tx *ethtypes.Transaction - // compliance check goes first + // sign outbound + tx, err := signer.SignOutboundFromCCTX( + ctx, + logger, + cctx, + txData, + zetacoreClient, + toChain, + ) + if err != nil { + logger.Err(err).Msg("error signing outbound") + return + } + + logger.Info().Msgf( + "Key-sign success: %d => %d, nonce %d", + cctx.InboundParams.SenderChainId, + toChain.ID(), + cctx.GetCurrentOutboundParam().TssNonce, + ) + + // Broadcast Signed Tx + signer.BroadcastOutbound(ctx, tx, cctx, logger, myID, zetacoreClient, txData) +} + +// SignOutboundFromCCTX signs an outbound transaction from a given cctx +// TODO: simplify logic with all if else +// https://github.com/zeta-chain/node/issues/2050 +func (signer *Signer) SignOutboundFromCCTX( + ctx context.Context, + logger zerolog.Logger, + cctx *crosschaintypes.CrossChainTx, + outboundData *OutboundData, + zetacoreClient interfaces.ZetacoreClient, + toChain zctx.Chain, +) (*ethtypes.Transaction, error) { if compliance.IsCctxRestricted(cctx) { + // restricted cctx compliance.PrintComplianceLog( logger, signer.Logger().Compliance, @@ -441,25 +337,20 @@ func (signer *Signer) TryProcessOutbound( signer.Chain().ChainId, cctx.Index, cctx.InboundParams.Sender, - txData.to.Hex(), + outboundData.to.Hex(), cctx.GetCurrentOutboundParam().CoinType.String(), ) - tx, err = signer.SignCancelTx(ctx, txData) // cancel the tx - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return - } - } else if cctx.InboundParams.CoinType == coin.CoinType_Cmd { // admin command + return signer.SignCancel(ctx, outboundData) + } else if cctx.InboundParams.CoinType == coin.CoinType_Cmd { + // admin command to := ethcommon.HexToAddress(cctx.GetCurrentOutboundParam().Receiver) if to == (ethcommon.Address{}) { - logger.Error().Msgf("invalid receiver %s", cctx.GetCurrentOutboundParam().Receiver) - return + return nil, fmt.Errorf("invalid receiver %s", cctx.GetCurrentOutboundParam().Receiver) } msg := strings.Split(cctx.RelayedMessage, ":") if len(msg) != 2 { - logger.Error().Msgf("invalid message %s", msg) - return + return nil, fmt.Errorf("invalid message %s", msg) } // cmd field is used to determine whether to execute ERC20 whitelist or migrate TSS funds given that the coin type // from the cctx is coin.CoinType_Cmd @@ -467,126 +358,100 @@ func (signer *Signer) TryProcessOutbound( // params field is used to pass input parameters for command requests, currently it is used to pass the ERC20 // contract address when a whitelist command is requested params := msg[1] - tx, err = signer.SignAdminTx(ctx, txData, cmd, params) - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return - } + return signer.SignAdminTx(ctx, outboundData, cmd, params) + } else if cctx.ProtocolContractVersion == crosschaintypes.ProtocolContractVersion_V2 { + // call sign outbound from cctx for v2 protocol contracts + return signer.SignOutboundFromCCTXV2(ctx, cctx, outboundData) } else if IsSenderZetaChain(cctx, zetacoreClient) { switch cctx.InboundParams.CoinType { case coin.CoinType_Gas: logger.Info().Msgf( - "SignWithdrawTx: %d => %d, nonce %d, gasPrice %d", + "SignGasWithdraw: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignWithdrawTx(ctx, txData) + return signer.SignGasWithdraw(ctx, outboundData) case coin.CoinType_ERC20: logger.Info().Msgf( - "SignERC20WithdrawTx: %d => %d, nonce %d, gasPrice %d", + "SignERC20Withdraw: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignERC20WithdrawTx(ctx, txData) + return signer.SignERC20Withdraw(ctx, outboundData) case coin.CoinType_Zeta: logger.Info().Msgf( - "SignOutbound: %d => %d, nonce %d, gasPrice %d", + "SignConnectorOnReceive: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignOutbound(ctx, txData) + return signer.SignConnectorOnReceive(ctx, outboundData) } - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return - } - } else if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert && cctx.OutboundParams[0].ReceiverChainId == zetacoreClient.Chain().ChainId { + } else if cctx.CctxStatus.Status == crosschaintypes.CctxStatus_PendingRevert && cctx.OutboundParams[0].ReceiverChainId == zetacoreClient.Chain().ChainId { switch cctx.InboundParams.CoinType { case coin.CoinType_Zeta: logger.Info().Msgf( - "SignRevertTx: %d => %d, nonce %d, gasPrice %d", + "SignConnectorOnRevert: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - txData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) - txData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) - tx, err = signer.SignRevertTx(ctx, txData) + outboundData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) + outboundData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) + return signer.SignConnectorOnRevert(ctx, outboundData) case coin.CoinType_Gas: logger.Info().Msgf( - "SignWithdrawTx: %d => %d, nonce %d, gasPrice %d", + "SignGasWithdraw: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignWithdrawTx(ctx, txData) + return signer.SignGasWithdraw(ctx, outboundData) case coin.CoinType_ERC20: - logger.Info().Msgf("SignERC20WithdrawTx: %d => %d, nonce %d, gasPrice %d", + logger.Info().Msgf("SignERC20Withdraw: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignERC20WithdrawTx(ctx, txData) - } - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return + return signer.SignERC20Withdraw(ctx, outboundData) } - } else if cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { + } else if cctx.CctxStatus.Status == crosschaintypes.CctxStatus_PendingRevert { logger.Info().Msgf( - "SignRevertTx: %d => %d, nonce %d, gasPrice %d", + "SignConnectorOnRevert: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - txData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) - txData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) - - tx, err = signer.SignRevertTx(ctx, txData) - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return - } - } else if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound { + outboundData.srcChainID = big.NewInt(cctx.OutboundParams[0].ReceiverChainId) + outboundData.toChainID = big.NewInt(cctx.GetCurrentOutboundParam().ReceiverChainId) + return signer.SignConnectorOnRevert(ctx, outboundData) + } else if cctx.CctxStatus.Status == crosschaintypes.CctxStatus_PendingOutbound { logger.Info().Msgf( - "SignOutbound: %d => %d, nonce %d, gasPrice %d", + "SignConnectorOnReceive: %d => %d, nonce %d, gasPrice %d", cctx.InboundParams.SenderChainId, toChain.ID(), cctx.GetCurrentOutboundParam().TssNonce, - txData.gas.Price, + outboundData.gas.Price, ) - tx, err = signer.SignOutbound(ctx, txData) - if err != nil { - logger.Warn().Err(err).Msg(ErrorMsg(cctx)) - return - } + return signer.SignConnectorOnReceive(ctx, outboundData) } - logger.Info().Msgf( - "Key-sign success: %d => %d, nonce %d", - cctx.InboundParams.SenderChainId, - toChain.ID(), - cctx.GetCurrentOutboundParam().TssNonce, - ) - - // Broadcast Signed Tx - signer.BroadcastOutbound(ctx, tx, cctx, logger, myID, zetacoreClient, txData) + return nil, fmt.Errorf("SignOutboundFromCCTX: can't determine how to sign outbound from cctx %s", cctx.String()) } // BroadcastOutbound signed transaction through evm rpc client func (signer *Signer) BroadcastOutbound( ctx context.Context, tx *ethtypes.Transaction, - cctx *types.CrossChainTx, + cctx *crosschaintypes.CrossChainTx, logger zerolog.Logger, myID sdk.AccAddress, zetacoreClient interfaces.ZetacoreClient, @@ -647,34 +512,6 @@ func (signer *Signer) BroadcastOutbound( } } -// SignERC20WithdrawTx -// function withdraw( -// address recipient, -// address asset, -// uint256 amount, -// ) external onlyTssAddress -func (signer *Signer) SignERC20WithdrawTx(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - data, err := signer.erc20CustodyABI.Pack("withdraw", txData.to, txData.asset, txData.amount) - if err != nil { - return nil, fmt.Errorf("withdraw pack error: %w", err) - } - - tx, _, _, err := signer.Sign( - ctx, - data, - signer.er20CustodyAddress, - zeroValue, - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("sign withdraw error: %w", err) - } - - return tx, nil -} - // EvmClient returns the EVM RPC client func (signer *Signer) EvmClient() interfaces.EVMRPCClient { return signer.client @@ -689,17 +526,17 @@ func (signer *Signer) EvmSigner() ethtypes.Signer { // IsSenderZetaChain checks if the sender chain is ZetaChain // TODO(revamp): move to another package more general for cctx functions func IsSenderZetaChain( - cctx *types.CrossChainTx, + cctx *crosschaintypes.CrossChainTx, zetacoreClient interfaces.ZetacoreClient, ) bool { return cctx.InboundParams.SenderChainId == zetacoreClient.Chain().ChainId && - cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound + cctx.CctxStatus.Status == crosschaintypes.CctxStatus_PendingOutbound } -// ErrorMsg returns a error message for SignOutbound failure with cctx data -func ErrorMsg(cctx *types.CrossChainTx) string { +// ErrorMsg returns a error message for SignConnectorOnReceive failure with cctx data +func ErrorMsg(cctx *crosschaintypes.CrossChainTx) string { return fmt.Sprintf( - "signer SignOutbound error: nonce %d chain %d", + "signer SignConnectorOnReceive error: nonce %d chain %d", cctx.GetCurrentOutboundParam().TssNonce, cctx.GetCurrentOutboundParam().ReceiverChainId, ) diff --git a/zetaclient/chains/evm/signer/signer_admin.go b/zetaclient/chains/evm/signer/signer_admin.go index 09bf1aa378..6c6d8db2d2 100644 --- a/zetaclient/chains/evm/signer/signer_admin.go +++ b/zetaclient/chains/evm/signer/signer_admin.go @@ -8,7 +8,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/zetacore/pkg/constant" ) @@ -58,7 +58,7 @@ func (signer *Signer) signWhitelistERC20Cmd( txData.to, zeroValue, txData.gas, - txData.outboundParams.TssNonce, + txData.nonce, txData.height, ) if err != nil { @@ -99,7 +99,7 @@ func (signer *Signer) signMigrateERC20CustodyFundsCmd( txData.to, zeroValue, txData.gas, - txData.outboundParams.TssNonce, + txData.nonce, txData.height, ) if err != nil { @@ -143,7 +143,7 @@ func (signer *Signer) signUpdateERC20CustodyPauseStatusCmd( txData.to, zeroValue, txData.gas, - txData.outboundParams.TssNonce, + txData.nonce, txData.height, ) if err != nil { diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index 23c7bc2fcf..3297a301f3 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -10,7 +10,6 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" observertypes "github.com/zeta-chain/zetacore/x/observer/types" zctx "github.com/zeta-chain/zetacore/zetaclient/context" @@ -45,7 +44,7 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { tss = mocks.NewTSSMainnet() } - mpiAddress := ConnectorAddress + connectorAddress := ConnectorAddress erc20CustodyAddress := ERC20CustodyAddress logger := base.Logger{} @@ -56,10 +55,9 @@ func getNewEvmSigner(tss interfaces.TSSSigner) (*Signer, error) { nil, logger, mocks.EVMRPCEnabled, - config.GetConnectorABI(), - config.GetERC20CustodyABI(), - mpiAddress, + connectorAddress, erc20CustodyAddress, + sample.EthAddress(), ) } @@ -184,232 +182,6 @@ func TestSigner_TryProcessOutbound(t *testing.T) { require.Len(t, *list, 1) } -func TestSigner_SignOutbound(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignOutbound - should successfully sign LegacyTx", func(t *testing.T) { - // Call SignOutbound - tx, err := evmSigner.SignOutbound(ctx, txData) - require.NoError(t, err) - - // Verify Signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // check that by default tx type is legacy tx - assert.Equal(t, ethtypes.LegacyTxType, int(tx.Type())) - }) - t.Run("SignOutbound - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignOutbound - tx, err := evmSigner.SignOutbound(ctx, txData) - require.ErrorContains(t, err, "sign onReceive error") - require.Nil(t, tx) - }) - - t.Run("SignOutbound - should successfully sign DynamicFeeTx", func(t *testing.T) { - // ARRANGE - const ( - gwei = 1_000_000_000 - priorityFee = 1 * gwei - gasPrice = 3 * gwei - ) - - // Given a CCTX with gas price and priority fee - cctx := getCCTX(t) - cctx.OutboundParams[0].GasPrice = big.NewInt(gasPrice).String() - cctx.OutboundParams[0].GasPriorityFee = big.NewInt(priorityFee).String() - - // Given outbound data - txData, skip, err := NewOutboundData(ctx, cctx, 123, makeLogger(t)) - require.False(t, skip) - require.NoError(t, err) - - // Given a working TSS - tss.Unpause() - - // ACT - tx, err := evmSigner.SignOutbound(ctx, txData) - require.NoError(t, err) - - // ASSERT - verifyTxSignature(t, tx, mocks.NewTSSMainnet().Pubkey(), evmSigner.EvmSigner()) - - // check that by default tx type is a dynamic fee tx - assert.Equal(t, ethtypes.DynamicFeeTxType, int(tx.Type())) - - // check that the gasPrice & priorityFee are set correctly - assert.Equal(t, int64(gasPrice), tx.GasFeeCap().Int64()) - assert.Equal(t, int64(priorityFee), tx.GasTipCap().Int64()) - }) -} - -func TestSigner_SignRevertTx(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignRevertTx - should successfully sign", func(t *testing.T) { - // Call SignRevertTx - tx, err := evmSigner.SignRevertTx(ctx, txData) - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - // Note: Revert tx calls connector contract with 0 gas token - verifyTxBodyBasics(t, tx, evmSigner.zetaConnectorAddress, txData.nonce, big.NewInt(0)) - }) - t.Run("SignRevertTx - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignRevertTx - tx, err := evmSigner.SignRevertTx(ctx, txData) - require.ErrorContains(t, err, "sign onRevert error") - require.Nil(t, tx) - }) -} - -func TestSigner_SignCancelTx(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignCancelTx - should successfully sign", func(t *testing.T) { - // Call SignRevertTx - tx, err := evmSigner.SignCancelTx(ctx, txData) - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - // Note: Cancel tx sends 0 gas token to TSS self address - verifyTxBodyBasics(t, tx, evmSigner.TSS().EVMAddress(), txData.nonce, big.NewInt(0)) - }) - t.Run("SignCancelTx - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignCancelTx - tx, err := evmSigner.SignCancelTx(ctx, txData) - require.ErrorContains(t, err, "SignCancelTx error") - require.Nil(t, tx) - }) -} - -func TestSigner_SignWithdrawTx(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignWithdrawTx - should successfully sign", func(t *testing.T) { - // Call SignWithdrawTx - tx, err := evmSigner.SignWithdrawTx(ctx, txData) - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) - }) - t.Run("SignWithdrawTx - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignWithdrawTx - tx, err := evmSigner.SignWithdrawTx(ctx, txData) - require.ErrorContains(t, err, "SignWithdrawTx error") - require.Nil(t, tx) - }) -} - -func TestSigner_SignERC20WithdrawTx(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignERC20WithdrawTx - should successfully sign", func(t *testing.T) { - // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - // Note: Withdraw tx calls erc20 custody contract with 0 gas token - verifyTxBodyBasics(t, tx, evmSigner.er20CustodyAddress, txData.nonce, big.NewInt(0)) - }) - - t.Run("SignERC20WithdrawTx - should fail if keysign fails", func(t *testing.T) { - // pause tss to make keysign fail - tss.Pause() - - // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) - require.ErrorContains(t, err, "sign withdraw error") - require.Nil(t, tx) - }) -} - func TestSigner_BroadcastOutbound(t *testing.T) { ctx := makeCtx(t) @@ -424,8 +196,8 @@ func TestSigner_BroadcastOutbound(t *testing.T) { require.False(t, skip) t.Run("BroadcastOutbound - should successfully broadcast", func(t *testing.T) { - // Call SignERC20WithdrawTx - tx, err := evmSigner.SignERC20WithdrawTx(ctx, txData) + // Call SignERC20Withdraw + tx, err := evmSigner.SignERC20Withdraw(ctx, txData) require.NoError(t, err) evmSigner.BroadcastOutbound( diff --git a/zetaclient/chains/evm/signer/v2_sign.go b/zetaclient/chains/evm/signer/v2_sign.go new file mode 100644 index 0000000000..9a3bf3ba89 --- /dev/null +++ b/zetaclient/chains/evm/signer/v2_sign.go @@ -0,0 +1,205 @@ +package signer + +import ( + "context" + "fmt" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + erc20custodyv2 "github.com/zeta-chain/protocol-contracts/v2/pkg/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayevm.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/revert.sol" +) + +// signGatewayExecute signs a gateway execute +// used for gas withdrawal and call transaction +// function execute +// address destination, +// bytes calldata data +func (signer *Signer) signGatewayExecute(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + gatewayABI, err := gatewayevm.GatewayEVMMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get GatewayEVMMetaData ABI") + } + + data, err := gatewayABI.Pack("execute", txData.to, txData.message) + if err != nil { + return nil, fmt.Errorf("execute pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.gatewayAddress, + txData.amount, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign execute error: %w", err) + } + + return tx, nil +} + +// signGatewayExecuteRevert signs a gateway execute revert +// function executeRevert +// address destination, +// bytes calldata data +func (signer *Signer) signGatewayExecuteRevert( + ctx context.Context, + txData *OutboundData, +) (*ethtypes.Transaction, error) { + gatewayABI, err := gatewayevm.GatewayEVMMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get GatewayEVMMetaData ABI") + } + + data, err := gatewayABI.Pack( + "executeRevert", + txData.to, + txData.message, + revert.RevertContext{ + Asset: txData.asset, + Amount: txData.amount.Uint64(), + RevertMessage: txData.revertOptions.RevertMessage, + }, + ) + if err != nil { + return nil, fmt.Errorf("executeRevert pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.gatewayAddress, + txData.amount, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign executeRevert error: %w", err) + } + + return tx, nil +} + +// signERC20CustodyWithdraw signs a erc20 withdrawal transaction +// function withdrawAndCall +// address to, +// address token, +// uint256 amount, +func (signer *Signer) signERC20CustodyWithdraw( + ctx context.Context, + txData *OutboundData, +) (*ethtypes.Transaction, error) { + erc20CustodyV2ABI, err := erc20custodyv2.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ERC20CustodyMetaData ABI") + } + + data, err := erc20CustodyV2ABI.Pack("withdraw", txData.to, txData.asset, txData.amount) + if err != nil { + return nil, fmt.Errorf("withdraw pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.er20CustodyAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign withdraw error: %w", err) + } + + return tx, nil +} + +// signERC20CustodyWithdrawAndCall signs a erc20 withdrawal and call transaction +// function withdrawAndCall +// address token, +// address to, +// uint256 amount, +// bytes calldata data +func (signer *Signer) signERC20CustodyWithdrawAndCall( + ctx context.Context, + txData *OutboundData, +) (*ethtypes.Transaction, error) { + erc20CustodyV2ABI, err := erc20custodyv2.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ERC20CustodyMetaData ABI") + } + + data, err := erc20CustodyV2ABI.Pack("withdrawAndCall", txData.to, txData.asset, txData.amount, txData.message) + if err != nil { + return nil, fmt.Errorf("withdraw pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.er20CustodyAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign withdrawAndCall error: %w", err) + } + + return tx, nil +} + +// signERC20CustodyWithdrawRevert signs a erc20 withdrawal revert transaction +// function withdrawAndRevert +// address token, +// address to, +// uint256 amount, +// bytes calldata data +func (signer *Signer) signERC20CustodyWithdrawRevert( + ctx context.Context, + txData *OutboundData, +) (*ethtypes.Transaction, error) { + erc20CustodyV2ABI, err := erc20custodyv2.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, errors.Wrap(err, "unable to get ERC20CustodyMetaData ABI") + } + + data, err := erc20CustodyV2ABI.Pack( + "withdrawAndRevert", + txData.to, + txData.asset, + txData.amount, + txData.message, + revert.RevertContext{ + Asset: txData.asset, + Amount: txData.amount.Uint64(), + RevertMessage: txData.revertOptions.RevertMessage, + }, + ) + if err != nil { + return nil, fmt.Errorf("withdraw pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + signer.er20CustodyAddress, + zeroValue, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign withdrawAndRevert error: %w", err) + } + + return tx, nil +} diff --git a/zetaclient/chains/evm/signer/v2_signer.go b/zetaclient/chains/evm/signer/v2_signer.go new file mode 100644 index 0000000000..00f9c2197d --- /dev/null +++ b/zetaclient/chains/evm/signer/v2_signer.go @@ -0,0 +1,37 @@ +package signer + +import ( + "context" + "fmt" + + ethtypes "github.com/ethereum/go-ethereum/core/types" + + "github.com/zeta-chain/zetacore/x/crosschain/types" + "github.com/zeta-chain/zetacore/zetaclient/chains/evm" +) + +// SignOutboundFromCCTXV2 signs an outbound transaction from a CCTX with protocol contract v2 +func (signer *Signer) SignOutboundFromCCTXV2( + ctx context.Context, + cctx *types.CrossChainTx, + outboundData *OutboundData, +) (*ethtypes.Transaction, error) { + outboundType := evm.ParseOutboundTypeFromCCTX(*cctx) + switch outboundType { + case evm.OutboundTypeGasWithdraw, evm.OutboundTypeGasWithdrawRevert: + return signer.SignGasWithdraw(ctx, outboundData) + case evm.OutboundTypeERC20Withdraw, evm.OutboundTypeERC20WithdrawRevert: + return signer.signERC20CustodyWithdraw(ctx, outboundData) + case evm.OutboundTypeERC20WithdrawAndCall: + return signer.signERC20CustodyWithdrawAndCall(ctx, outboundData) + case evm.OutboundTypeGasWithdrawAndCall, evm.OutboundTypeCall: + // both gas withdraw and call and no-asset call uses gateway execute + // no-asset call simply hash msg.value == 0 + return signer.signGatewayExecute(ctx, outboundData) + case evm.OutboundTypeGasWithdrawRevertAndCallOnRevert: + return signer.signGatewayExecuteRevert(ctx, outboundData) + case evm.OutboundTypeERC20WithdrawRevertAndCallOnRevert: + return signer.signERC20CustodyWithdrawRevert(ctx, outboundData) + } + return nil, fmt.Errorf("unsupported outbound type %d", outboundType) +} diff --git a/zetaclient/chains/evm/validation.go b/zetaclient/chains/evm/validation.go index 8c7f9249c5..0f043c9966 100644 --- a/zetaclient/chains/evm/validation.go +++ b/zetaclient/chains/evm/validation.go @@ -14,7 +14,7 @@ import ( // ValidateEvmTxLog checks the basics of an EVM tx log func ValidateEvmTxLog(vLog *ethtypes.Log, wantAddress ethcommon.Address, wantHash string, wantTopics int) error { if vLog.Removed { - return fmt.Errorf("log is removed, chain reorg?") + return fmt.Errorf("log is removed, it might be related to a chain reorganization") } if vLog.Address != wantAddress { return fmt.Errorf("log emitter address mismatch: want %s got %s", wantAddress.Hex(), vLog.Address.Hex()) diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 7fa989a071..41c4c3efdd 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -144,7 +144,7 @@ func (signer *Signer) TryProcessOutbound( // sign the withdraw transaction by relayer key tx, err := signer.SignWithdrawTx(ctx, *msg) if err != nil { - logger.Error().Err(err).Msgf("TryProcessOutbound: SignWithdrawTx error for chain %d nonce %d", chainID, nonce) + logger.Error().Err(err).Msgf("TryProcessOutbound: SignGasWithdraw error for chain %d nonce %d", chainID, nonce) return } diff --git a/zetaclient/config/config_chain.go b/zetaclient/config/config_chain.go index 342574764f..826253d71e 100644 --- a/zetaclient/config/config_chain.go +++ b/zetaclient/config/config_chain.go @@ -10,28 +10,6 @@ const ( MaxBlocksPerPeriod = 100 ) -const ( - // connectorAbiString is the ABI of the connector contract - // TODO(revamp): we should be able to use info from Go binding - connectorAbiString = ` -[{"inputs":[{"internalType":"address","name":"_zetaTokenAddress","type":"address"},{"internalType":"address","name":"_tssAddress","type":"address"},{"internalType":"address","name":"_tssAddressUpdater","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"originSenderAddress","type":"bytes"},{"indexed":true,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":true,"internalType":"address","name":"destinationAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":true,"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"ZetaReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"originSenderAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"originChainId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":true,"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":true,"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"ZetaReverted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"originSenderAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gasLimit","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"zetaParams","type":"bytes"}],"name":"ZetaSent","type":"event"},{"inputs":[],"name":"getLockedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"originSenderAddress","type":"bytes"},{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"address","name":"destinationAddress","type":"address"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"onReceive","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"originSenderAddress","type":"address"},{"internalType":"uint256","name":"originChainId","type":"uint256"},{"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"bytes32","name":"internalSendHash","type":"bytes32"}],"name":"onRevert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceTssAddressUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"bytes","name":"destinationAddress","type":"bytes"},{"internalType":"uint256","name":"gasLimit","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"uint256","name":"zetaAmount","type":"uint256"},{"internalType":"bytes","name":"zetaParams","type":"bytes"}],"internalType":"struct ZetaInterfaces.SendInput","name":"input","type":"tuple"}],"name":"send","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tssAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tssAddressUpdater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tssAddress","type":"address"}],"name":"updateTssAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zetaToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]` - - // erc20CustodyAbiString is the ABI of the erc20 custodu contract - // TODO(revamp): we should be able to use info from Go binding - erc20CustodyAbiString = ` -[{"inputs":[{"internalType":"address","name":"_TSSAddress","type":"address"},{"internalType":"address","name":"_TSSAddressUpdater","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidSender","type":"error"},{"inputs":[],"name":"InvalidTSSUpdater","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"NotPaused","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"recipient","type":"bytes"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"message","type":"bytes"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"Unwhitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"Whitelisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"TSSAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TSSAddressUpdater","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"recipient","type":"bytes"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceTSSAddressUpdater","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"unwhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"updateTSSAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"whitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]` -) - -// GetConnectorABI returns the ABI of the connector contract -func GetConnectorABI() string { - return connectorAbiString -} - -// GetERC20CustodyABI returns the ABI of the erc20 custody contract -func GetERC20CustodyABI() string { - return erc20CustodyAbiString -} - // New constructs Config optionally with default values. func New(setDefaults bool) Config { cfg := Config{ diff --git a/zetaclient/orchestrator/bootstrap.go b/zetaclient/orchestrator/bootstrap.go index 61d2960468..238652f931 100644 --- a/zetaclient/orchestrator/bootstrap.go +++ b/zetaclient/orchestrator/bootstrap.go @@ -18,7 +18,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/chains/interfaces" solbserver "github.com/zeta-chain/zetacore/zetaclient/chains/solana/observer" solanasigner "github.com/zeta-chain/zetacore/zetaclient/chains/solana/signer" - "github.com/zeta-chain/zetacore/zetaclient/config" zctx "github.com/zeta-chain/zetacore/zetaclient/context" "github.com/zeta-chain/zetacore/zetaclient/db" "github.com/zeta-chain/zetacore/zetaclient/keys" @@ -108,8 +107,9 @@ func syncSignerMap( switch { case chain.IsEVM(): var ( - mpiAddress = ethcommon.HexToAddress(chain.Params().ConnectorContractAddress) - erc20CustodyAddress = ethcommon.HexToAddress(chain.Params().Erc20CustodyContractAddress) + zetaConnectorAddress = ethcommon.HexToAddress(chain.Params().ConnectorContractAddress) + erc20CustodyAddress = ethcommon.HexToAddress(chain.Params().Erc20CustodyContractAddress) + gatewayAddress = ethcommon.HexToAddress(chain.Params().GatewayAddress) ) cfg, found := app.Config().GetEVMConfig(chainID) @@ -125,10 +125,9 @@ func syncSignerMap( ts, logger, cfg.Endpoint, - config.GetConnectorABI(), - config.GetERC20CustodyABI(), - mpiAddress, + zetaConnectorAddress, erc20CustodyAddress, + gatewayAddress, ) if err != nil { logger.Std.Error().Err(err).Msgf("Unable to construct signer for EVM chain %d", chainID) diff --git a/zetaclient/orchestrator/orchestrator.go b/zetaclient/orchestrator/orchestrator.go index a7f8feccfb..d8a8aeb6a0 100644 --- a/zetaclient/orchestrator/orchestrator.go +++ b/zetaclient/orchestrator/orchestrator.go @@ -180,7 +180,7 @@ func (oc *Orchestrator) resolveSigner(app *zctx.AppContext, chainID int64) (inte case chain.IsSolana(): params := chain.Params() - // update solana gateway address + // update gateway address if params.GatewayAddress != signer.GetGatewayAddress() { signer.SetGatewayAddress(params.GatewayAddress) oc.logger.Info(). diff --git a/zetaclient/testdata/cctx/chain_1_cctx_inbound_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go b/zetaclient/testdata/cctx/chain_1_cctx_inbound_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go index f629b000e9..fefbc9cad9 100644 --- a/zetaclient/testdata/cctx/chain_1_cctx_inbound_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go +++ b/zetaclient/testdata/cctx/chain_1_cctx_inbound_ERC20_0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da.go @@ -28,7 +28,7 @@ var chain_1_cctx_inbound_ERC20_0x4ea69a0 = &crosschaintypes.CrossChainTx{ Amount: sdkmath.NewUintFromString("9992000000"), ObservedHash: "0x4ea69a0e2ff36f7548ab75791c3b990e076e2a4bffeb616035b239b7d33843da", ObservedExternalHeight: 19320188, - BallotIndex: "0xaf8af6853ead0a6f7c6348ab91b3631e9527aa30da4b22eec199fb8c99060920", + BallotIndex: "0xc3d0dab7b2a34e3bfa2430963ff776ef2357c41f3164a28ab5d6380d7a438938", FinalizedZetaHeight: 1944675, TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, }, diff --git a/zetaclient/testdata/cctx/chain_1_cctx_inbound_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go b/zetaclient/testdata/cctx/chain_1_cctx_inbound_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go index 91fc0beaf8..ef095d996d 100644 --- a/zetaclient/testdata/cctx/chain_1_cctx_inbound_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go +++ b/zetaclient/testdata/cctx/chain_1_cctx_inbound_Gas_0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532.go @@ -28,7 +28,7 @@ var chain_1_cctx_inbound_Gas_0xeaec67d = &crosschaintypes.CrossChainTx{ Amount: sdkmath.NewUintFromString("4000000000000000"), ObservedHash: "0xeaec67d5dd5d85f27b21bef83e01cbdf59154fd793ea7a22c297f7c3a722c532", ObservedExternalHeight: 19330473, - BallotIndex: "0x639adb850b522874ddd2c4e5eb7ae7ad26d86814fc5b18d8a9e85f638bb94594", + BallotIndex: "0x79f6a3da92d085b2f3c682a2eb1606ef89e53a7db4fdbbb397b3e0f54884cfb0", FinalizedZetaHeight: 1965579, TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, }, diff --git a/zetaclient/testdata/cctx/chain_1_cctx_inbound_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go b/zetaclient/testdata/cctx/chain_1_cctx_inbound_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go index 6e3f5d855d..f655dcc508 100644 --- a/zetaclient/testdata/cctx/chain_1_cctx_inbound_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go +++ b/zetaclient/testdata/cctx/chain_1_cctx_inbound_Zeta_0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76.go @@ -28,7 +28,7 @@ var chain_1_cctx_inbound_Zeta_0xf393520 = &crosschaintypes.CrossChainTx{ Amount: sdkmath.NewUintFromString("20000000000000000000"), ObservedHash: "0xf3935200c80f98502d5edc7e871ffc40ca898e134525c42c2ae3cbc5725f9d76", ObservedExternalHeight: 19273702, - BallotIndex: "0x9039e8b0493d58ee4db876ffd0785970019d7eb515e2f71b00fe670bb6a8e6ce", + BallotIndex: "0xa4efd2d7293e1c2c557eb527c7ad7f2f8754cce413d3f72929a17e02b537b0af", FinalizedZetaHeight: 1851403, TxFinalizationStatus: crosschaintypes.TxFinalizationStatus_Executed, }, diff --git a/zetaclient/testutils/evm.go b/zetaclient/testutils/evm.go index 3007fa6035..7a0218fecf 100644 --- a/zetaclient/testutils/evm.go +++ b/zetaclient/testutils/evm.go @@ -2,8 +2,8 @@ package testutils import ( ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" ) // ParseReceiptZetaSent parses a ZetaSent event from a receipt diff --git a/zetaclient/testutils/mocks/chain_params.go b/zetaclient/testutils/mocks/chain_params.go index 19603eda34..a23010372a 100644 --- a/zetaclient/testutils/mocks/chain_params.go +++ b/zetaclient/testutils/mocks/chain_params.go @@ -5,22 +5,21 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/stretchr/testify/require" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/erc20custody.sol" + "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/evm/zetaconnector.non-eth.sol" + "github.com/zeta-chain/zetacore/pkg/constant" observertypes "github.com/zeta-chain/zetacore/x/observer/types" "github.com/zeta-chain/zetacore/zetaclient/testutils" ) func MockChainParams(chainID int64, confirmation uint64) observertypes.ChainParams { - const zeroAddress = "0x0000000000000000000000000000000000000000" - - connectorAddr := zeroAddress + connectorAddr := constant.EVMZeroAddress if a, ok := testutils.ConnectorAddresses[chainID]; ok { connectorAddr = a.Hex() } - erc20CustodyAddr := zeroAddress + erc20CustodyAddr := constant.EVMZeroAddress if a, ok := testutils.CustodyAddresses[chainID]; ok { erc20CustodyAddr = a.Hex() } @@ -28,7 +27,7 @@ func MockChainParams(chainID int64, confirmation uint64) observertypes.ChainPara return observertypes.ChainParams{ ChainId: chainID, ConfirmationCount: confirmation, - ZetaTokenContractAddress: zeroAddress, + ZetaTokenContractAddress: constant.EVMZeroAddress, ConnectorContractAddress: connectorAddr, Erc20CustodyContractAddress: erc20CustodyAddr, InboundTicker: 12, diff --git a/zetaclient/zetacore/client_monitor.go b/zetaclient/zetacore/client_monitor.go index b505af90a1..51d4f17168 100644 --- a/zetaclient/zetacore/client_monitor.go +++ b/zetaclient/zetacore/client_monitor.go @@ -150,10 +150,11 @@ func (c *Client) monitorVoteInboundResult( if retryGasLimit > 0 { // new retryGasLimit set to 0 to prevent reentering this function - if _, _, err := c.PostVoteInbound(ctx, retryGasLimit, 0, msg); err != nil { + if resentTxHash, _, err := c.PostVoteInbound(ctx, retryGasLimit, 0, msg); err != nil { c.logger.Error().Err(err).Fields(logFields).Msg("monitorVoteInboundResult: failed to resend tx") } else { - c.logger.Info().Fields(logFields).Msg("monitorVoteInboundResult: successfully resent tx") + logFields["inbound.resent_hash"] = resentTxHash + c.logger.Info().Fields(logFields).Msgf("monitorVoteInboundResult: successfully resent tx") } } diff --git a/zetaclient/zetacore/constant.go b/zetaclient/zetacore/constant.go index a5a4e16829..e7343206ee 100644 --- a/zetaclient/zetacore/constant.go +++ b/zetaclient/zetacore/constant.go @@ -16,7 +16,7 @@ const ( PostVoteInboundGasLimit = 500_000 // PostVoteInboundExecutionGasLimit is the gas limit for voting on observed inbound tx and executing it - PostVoteInboundExecutionGasLimit = 4_000_000 + PostVoteInboundExecutionGasLimit = 6_500_000 // PostVoteInboundMessagePassingExecutionGasLimit is the gas limit for voting on, and executing ,observed inbound tx related to message passing (coin_type == zeta) PostVoteInboundMessagePassingExecutionGasLimit = 4_000_000 @@ -41,7 +41,7 @@ const ( // PostVoteOutboundRevertGasLimit is the gas limit for voting on observed outbound tx for revert (when outbound fails) // The value needs to be higher because reverting implies interacting with the EVM to perform swaps for the gas token - PostVoteOutboundRevertGasLimit = 1_500_000 + PostVoteOutboundRevertGasLimit = 4_000_000 ) // constants for monitoring tx results diff --git a/zetaclient/zetacore/tx.go b/zetaclient/zetacore/tx.go index fb57cd449b..2d63f4a3fd 100644 --- a/zetaclient/zetacore/tx.go +++ b/zetaclient/zetacore/tx.go @@ -50,6 +50,7 @@ func GetInboundVoteMessage( coinType, asset, eventIndex, + types.ProtocolContractVersion_V1, ) return msg } diff --git a/zetaclient/zetacore/tx_test.go b/zetaclient/zetacore/tx_test.go index 05cdff6417..a87ed8404e 100644 --- a/zetaclient/zetacore/tx_test.go +++ b/zetaclient/zetacore/tx_test.go @@ -426,7 +426,7 @@ func TestZetacore_PostVoteInbound(t *testing.T) { expectedOutput := observertypes.QueryHasVotedResponse{HasVoted: false} input := observertypes.QueryHasVotedRequest{ - BallotIdentifier: "0x2d10e9b7ce7921fa6b61ada3020d1c797d5ec52424cdcf86ef31cbbbcd45db58", + BallotIdentifier: "0xd204175fc8500bcea563049cce918fa55134bd2d415d3fe137144f55e572b5ff", VoterAddress: address.String(), } method := "/zetachain.zetacore.observer.Query/HasVoted"