diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index e80b399c61..35524dc29f 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- + + **Describe the Bug** A clear and concise description of what the bug is. @@ -21,4 +23,4 @@ If applicable, add screenshots to help explain your problem. **Environment (please complete the following information):** - OS -- Version used +- Version used \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 6010eb826e..ea0804fb7c 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,6 +7,8 @@ assignees: '' --- + + **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. diff --git a/.github/ISSUE_TEMPLATE/syncing.yaml b/.github/ISSUE_TEMPLATE/syncing.yaml index 6519c730f9..0cc504b910 100644 --- a/.github/ISSUE_TEMPLATE/syncing.yaml +++ b/.github/ISSUE_TEMPLATE/syncing.yaml @@ -1,6 +1,6 @@ name: Having difficulties setting up or syncing a node description: Please fill out the following form to help us understand and resolve your issue with setting up or syncing a node. Provide as much detail as possible to ensure a quick and accurate resolution. -labels: ["infra"] +labels: ["infra", "triage"] body: - type: dropdown attributes: diff --git a/.github/workflows/zetachain-sec-checks.yml b/.github/workflows/zetachain-sec-checks.yml new file mode 100644 index 0000000000..47aba1fe2e --- /dev/null +++ b/.github/workflows/zetachain-sec-checks.yml @@ -0,0 +1,26 @@ +name: "CodeQL - ZetaChain Custom checks " + +on: [push, pull_request] + +jobs: + analyze: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + packs: zeta-chain/protocol-security-codeql + - name: Analyze + uses: github/codeql-action/analyze@v3 diff --git a/Dockerfile-localnet b/Dockerfile-localnet index dd329a712d..2f8c90bf47 100644 --- a/Dockerfile-localnet +++ b/Dockerfile-localnet @@ -28,7 +28,7 @@ RUN --mount=type=cache,target="/root/.cache/go-build" \ make install install-zetae2e FROM ghcr.io/zeta-chain/golang:1.23.3-bookworm AS cosmovisor-build -RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.7.0 +RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.7.1 FROM ghcr.io/zeta-chain/golang:1.23.3-bookworm AS base-runtime diff --git a/changelog.md b/changelog.md index e416fb47d8..651d15e942 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ ## Unreleased +### Breaking Changes + +* The CCTX List RPC (`/zeta-chain/crosschain/cctx`) will now return CCTXs ordered by creation time. CCTXs from before the upgrade will not be displayed. Use the `?unordered=true` parameter to revert to the old behavior. + ### Features * [3414](https://github.com/zeta-chain/node/pull/3414) - support advanced abort workflow (onAbort) @@ -10,13 +14,17 @@ * [3455](https://github.com/zeta-chain/node/pull/3455) - add `track-cctx` command to zetatools * [3506](https://github.com/zeta-chain/node/pull/3506) - define `ConfirmationMode` enum and add it to `InboundParams`, `OutboundParams`, `MsgVoteInbound` and `MsgVoteOutbound` * [3469](https://github.com/zeta-chain/node/pull/3469) - add `MsgRemoveInboundTracker` to remove inbound trackers. This message can be triggered by the emergency policy. -* [3450](https://github.com/zeta-chain/node/pull/3450) - SOL withdraw and call integration +* [3450](https://github.com/zeta-chain/node/pull/3450) - integrate SOL withdraw and call * [3538](https://github.com/zeta-chain/node/pull/3538) - implement `MsgUpdateOperationalChainParams` for updating operational-related chain params with operational policy -* [3534] (https://github.com/zeta-chain/node/pull/3534) - Add Sui deposit & depositAndCall +* [3534](https://github.com/zeta-chain/node/pull/3534) - Add Sui deposit & depositAndCall * [3541](https://github.com/zeta-chain/node/pull/3541) - implement `MsgUpdateZRC20Name` to update the name or symbol of a ZRC20 token -* [3520](https://github.com/zeta-chain/node/pull/3520) - SPL withdraw and call integration * [3439](https://github.com/zeta-chain/node/pull/3439) - use protocol contracts V2 with TON deposits +* [3520](https://github.com/zeta-chain/node/pull/3520) - integrate SPL withdraw and call +* [3527](https://github.com/zeta-chain/node/pull/3527) - integrate SOL/SPL withdraw and call revert * [3522](https://github.com/zeta-chain/node/pull/3522) - add `MsgDisableFastConfirmation` to disable fast confirmation. This message can be triggered by the emergency policy. +* [3548](https://github.com/zeta-chain/node/pull/3548) - ensure cctx list is sorted by creation time +* [3562](https://github.com/zeta-chain/node/pull/3562) - add Sui withdrawals +* [3600](https://github.com/zeta-chain/node/pull/3600) - add dedicated zetaclient restricted addresses config. This file will be automatically reloaded when it changes without needing to restart zetaclient. ### Refactor @@ -28,11 +36,14 @@ * [3501](https://github.com/zeta-chain/node/pull/3501) - fix E2E test failure caused by nil `ConfirmationParams` for Solana and TON * [3509](https://github.com/zeta-chain/node/pull/3509) - schedule Bitcoin TSS keysign on interval to avoid TSS keysign spam * [3517](https://github.com/zeta-chain/node/pull/3517) - remove duplicate gateway event appending to fix false positive on multiple events in same tx +* [3602](https://github.com/zeta-chain/node/pull/3602) - hardcode gas limits to avoid estimate gas calls ### Tests * [3430](https://github.com/zeta-chain/node/pull/3430) - add simulation test for MsgWithDrawEmission * [3503](https://github.com/zeta-chain/node/pull/3503) - add check in e2e test to ensure deletion of stale ballots +* [3536](https://github.com/zeta-chain/node/pull/3536) - add e2e test for upgrading solana gateway program +* [3560](https://github.com/zeta-chain/node/pull/3560) - initialize Sui E2E deposit tests * [3591](https://github.com/zeta-chain/node/pull/3591) - add a runner for gov proposals in the e2e test. ## v28.0.0 diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 80dbf283d4..d98a198e72 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -10,6 +10,7 @@ import ( "github.com/rs/zerolog/log" "github.com/spf13/cobra" + "github.com/zeta-chain/node/pkg/bg" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/constant" "github.com/zeta-chain/node/pkg/graceful" @@ -58,6 +59,15 @@ func Start(_ *cobra.Command, _ []string) error { appContext := zctx.New(cfg, passes.relayerKeys(), logger.Std) ctx := zctx.WithAppContext(context.Background(), appContext) + err = config.LoadRestrictedAddressesConfig(cfg, globalOpts.ZetacoreHome) + if err != nil { + logger.Std.Err(err).Msg("loading restricted addresses config") + } else { + bg.Work(ctx, func(ctx context.Context) error { + return config.WatchRestrictedAddressesConfig(ctx, cfg, globalOpts.ZetacoreHome, logger.Std) + }, bg.WithName("watch_restricted_addresses_config"), bg.WithLogger(logger.Std)) + } + telemetry, err := startTelemetry(ctx, cfg) if err != nil { return errors.Wrap(err, "unable to start telemetry") @@ -77,7 +87,7 @@ func Start(_ *cobra.Command, _ []string) error { return errors.Wrap(err, "unable to update app context") } - log.Info().Msgf("Config is updated from zetacore\n %s", cfg.StringMasked()) + log.Debug().Msgf("Config is updated from zetacore\n %s", cfg.StringMasked()) granteePubKeyBech32, err := resolveObserverPubKeyBech32(cfg, passes.hotkey) if err != nil { @@ -133,7 +143,7 @@ func Start(_ *cobra.Command, _ []string) error { // Orchestrator wraps the zetacore client and adds the observers and signer maps to it. // This is the high level object used for CCTX interactions // It also handles background configuration updates from zetacore - taskScheduler := scheduler.New(logger.Std) + taskScheduler := scheduler.New(logger.Std, 0) maestroDeps := &orchestrator.Dependencies{ Zetacore: zetacoreClient, TSS: tss, diff --git a/cmd/zetae2e/config/config.go b/cmd/zetae2e/config/config.go index 9cc11d815c..96ba899544 100644 --- a/cmd/zetae2e/config/config.go +++ b/cmd/zetae2e/config/config.go @@ -58,6 +58,14 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C conf.Contracts.Solana.GatewayProgramID = config.DoubleQuotedString(r.GatewayProgram.String()) conf.Contracts.Solana.SPLAddr = config.DoubleQuotedString(r.SPLAddr.String()) + if r.SuiGateway != nil { + conf.Contracts.Sui.GatewayPackageID = config.DoubleQuotedString(r.SuiGateway.PackageID()) + conf.Contracts.Sui.GatewayObjectID = config.DoubleQuotedString(r.SuiGateway.ObjectID()) + } + + conf.Contracts.Sui.FungibleTokenCoinType = config.DoubleQuotedString(r.SuiTokenCoinType) + conf.Contracts.Sui.FungibleTokenTreasuryCap = config.DoubleQuotedString(r.SuiTokenTreasuryCap) + conf.Contracts.EVM.ZetaEthAddr = config.DoubleQuotedString(r.ZetaEthAddr.Hex()) conf.Contracts.EVM.ConnectorEthAddr = config.DoubleQuotedString(r.ConnectorEthAddr.Hex()) conf.Contracts.EVM.CustodyAddr = config.DoubleQuotedString(r.ERC20CustodyAddr.Hex()) @@ -73,6 +81,9 @@ func ExportContractsFromRunner(r *runner.E2ERunner, conf config.Config) config.C conf.Contracts.ZEVM.SOLZRC20Addr = config.DoubleQuotedString(r.SOLZRC20Addr.Hex()) conf.Contracts.ZEVM.SPLZRC20Addr = config.DoubleQuotedString(r.SPLZRC20Addr.Hex()) conf.Contracts.ZEVM.TONZRC20Addr = config.DoubleQuotedString(r.TONZRC20Addr.Hex()) + conf.Contracts.ZEVM.SUIZRC20Addr = config.DoubleQuotedString(r.SUIZRC20Addr.Hex()) + conf.Contracts.ZEVM.SuiTokenZRC20Addr = config.DoubleQuotedString(r.SuiTokenZRC20Addr.Hex()) + conf.Contracts.ZEVM.UniswapFactoryAddr = config.DoubleQuotedString(r.UniswapV2FactoryAddr.Hex()) conf.Contracts.ZEVM.UniswapRouterAddr = config.DoubleQuotedString(r.UniswapV2RouterAddr.Hex()) conf.Contracts.ZEVM.ConnectorZEVMAddr = config.DoubleQuotedString(r.ConnectorZEVMAddr.Hex()) diff --git a/cmd/zetae2e/config/contracts.go b/cmd/zetae2e/config/contracts.go index 1c3f871d38..798e21f547 100644 --- a/cmd/zetae2e/config/contracts.go +++ b/cmd/zetae2e/config/contracts.go @@ -19,11 +19,12 @@ import ( "github.com/zeta-chain/node/e2e/config" "github.com/zeta-chain/node/e2e/contracts/contextapp" "github.com/zeta-chain/node/e2e/contracts/erc20" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" "github.com/zeta-chain/node/e2e/contracts/zevmswap" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" + "github.com/zeta-chain/node/pkg/contracts/sui" "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-core/contracts/uniswapv2factory.sol" uniswapv2router "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-periphery/contracts/uniswapv2router02.sol" fungibletypes "github.com/zeta-chain/node/x/fungible/types" @@ -117,6 +118,21 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { r.SPLAddr = solana.MustPublicKeyFromBase58(c.String()) } + // set Sui contracts + suiPackageID := conf.Contracts.Sui.GatewayPackageID + suiGatewayID := conf.Contracts.Sui.GatewayObjectID + + if suiPackageID != "" && suiGatewayID != "" { + r.SuiGateway = sui.NewGateway(suiPackageID.String(), suiGatewayID.String()) + } + + if c := conf.Contracts.Sui.FungibleTokenCoinType; c != "" { + r.SuiTokenCoinType = c.String() + } + if c := conf.Contracts.Sui.FungibleTokenTreasuryCap; c != "" { + r.SuiTokenTreasuryCap = c.String() + } + evmChainID, err := r.EVMClient.ChainID(r.Ctx) require.NoError(r, err, "get evm chain ID") evmChainParams := chainParamsByChainID(chainParams, evmChainID.Int64()) @@ -224,6 +240,28 @@ func setContractsFromConfig(r *runner.E2ERunner, conf config.Config) error { } } + if c := conf.Contracts.ZEVM.SUIZRC20Addr; c != "" { + r.SUIZRC20Addr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid SUIZRC20Addr: %w", err) + } + r.SUIZRC20, err = zrc20.NewZRC20(r.SUIZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + } + + if c := conf.Contracts.ZEVM.SuiTokenZRC20Addr; c != "" { + r.SuiTokenZRC20Addr, err = c.AsEVMAddress() + if err != nil { + return fmt.Errorf("invalid SuiTokenZRC20Addr: %w", err) + } + r.SuiTokenZRC20, err = zrc20.NewZRC20(r.SuiTokenZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + } + if c := conf.Contracts.ZEVM.UniswapFactoryAddr; c != "" { r.UniswapV2FactoryAddr, err = c.AsEVMAddress() if err != nil { diff --git a/cmd/zetae2e/config/local.yml b/cmd/zetae2e/config/local.yml index af9dc0039c..70ce5ad8be 100644 --- a/cmd/zetae2e/config/local.yml +++ b/cmd/zetae2e/config/local.yml @@ -36,6 +36,10 @@ additional_accounts: evm_address: "0xf67deecc3B15F9CEeF5eba3468ed601f3e0B9de2" private_key: "2b3306a8ac43dbf0e350b87876c131e7e12bd49563a16de9ce8aeb664b94d559" solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" + user_sui: + bech32_address: "zeta1qluk7yfyqfejwss64lqmn7de6svudcedz94zs2" + evm_address: "0x07F96F1124027327421AAFc1B9F9B9D419C6e32d" + private_key: "e9f0e48e37fb2c2357b4fbc2b675fce434889c46052d92e8f4cabd74507a65ad" user_legacy_ether: bech32_address: "zeta134rakuus43xn63yucgxhn88ywj8ewcv6ezn2ga" evm_address: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A" diff --git a/cmd/zetae2e/config/localnet.yml b/cmd/zetae2e/config/localnet.yml index 6abf3b2e18..25fbaa4444 100644 --- a/cmd/zetae2e/config/localnet.yml +++ b/cmd/zetae2e/config/localnet.yml @@ -34,6 +34,10 @@ additional_accounts: evm_address: "0xf67deecc3B15F9CEeF5eba3468ed601f3e0B9de2" private_key: "2b3306a8ac43dbf0e350b87876c131e7e12bd49563a16de9ce8aeb664b94d559" solana_private_key: "4yqSQxDeTBvn86BuxcN5jmZb2gaobFXrBqu8kiE9rZxNkVMe3LfXmFigRsU4sRp7vk4vVP1ZCFiejDKiXBNWvs2C" + user_sui: + bech32_address: "zeta1qluk7yfyqfejwss64lqmn7de6svudcedz94zs2" + evm_address: "0x07F96F1124027327421AAFc1B9F9B9D419C6e32d" + private_key: "e9f0e48e37fb2c2357b4fbc2b675fce434889c46052d92e8f4cabd74507a65ad" user_legacy_ether: bech32_address: "zeta134rakuus43xn63yucgxhn88ywj8ewcv6ezn2ga" evm_address: "0x8D47Db7390AC4D3D449Cc20D799ce4748F97619A" diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index f8064c7fd6..d915b7ce3f 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -12,6 +12,7 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" + "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" zetae2econfig "github.com/zeta-chain/node/cmd/zetae2e/config" @@ -53,10 +54,9 @@ const ( var ( TestTimeout = 20 * time.Minute ErrTopLevelTimeout = errors.New("top level test timeout") + noError = testutil.NoError ) -var noError = testutil.NoError - // NewLocalCmd returns the local command // which runs the E2E tests locally on the machine with localnet for each blockchain func NewLocalCmd() *cobra.Command { @@ -84,7 +84,7 @@ func NewLocalCmd() *cobra.Command { cmd.Flags().Bool(flagTestTSSMigration, false, "set to true to include a migration test at the end") cmd.Flags().Bool(flagTestLegacy, false, "set to true to run legacy EVM tests") cmd.Flags().Bool(flagSkipTrackerCheck, false, "set to true to skip tracker check at the end of the tests") - cmd.Flags().Bool(flagSkipPrecompiles, false, "set to true to skip stateful precompiled contracts test") + cmd.Flags().Bool(flagSkipPrecompiles, true, "set to true to skip stateful precompiled contracts test") cmd.Flags(). Bool(flagUpgradeContracts, false, "set to true to upgrade Gateways and ERC20Custody contracts during setup for ZEVM and EVM") @@ -197,10 +197,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { // monitor block production to ensure we fail fast if there are consensus failures go monitorBlockProductionCancel(ctx, cancel, conf) - if testSui && !skipSetup { - deployerRunner.SetupSui(conf.RPCs.SuiFaucet) - } - // set the authority client to the zeta tx server to be able to query message permissions deployerRunner.ZetaTxServer.SetAuthorityClient(deployerRunner.AuthorityClient) @@ -261,6 +257,10 @@ func localE2ETest(cmd *cobra.Command, _ []string) { // Update the chain params to contains protocol contract addresses deployerRunner.UpdateProtocolContractsInChainParams() + if testSui { + deployerRunner.SetupSui(conf.RPCs.SuiFaucet) + } + logger.Print("✅ setup completed in %s", time.Since(startTime)) } @@ -288,7 +288,6 @@ func localE2ETest(cmd *cobra.Command, _ []string) { if upgradeContracts { deployerRunner.UpgradeGatewaysAndERC20Custody() } - // always mint ERC20 before every test execution deployerRunner.MintERC20OnEVM(1e10) @@ -410,6 +409,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSolanaDepositName, e2etests.TestSolanaWithdrawName, e2etests.TestSolanaWithdrawAndCallName, + e2etests.TestSolanaWithdrawAndCallRevertWithCallName, e2etests.TestSolanaDepositAndCallName, e2etests.TestSolanaDepositAndCallRevertName, e2etests.TestSolanaDepositAndCallRevertWithDustName, @@ -421,12 +421,23 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestSPLDepositAndCallName, e2etests.TestSPLWithdrawName, e2etests.TestSPLWithdrawAndCallName, + e2etests.TestSPLWithdrawAndCallRevertName, e2etests.TestSPLWithdrawAndCreateReceiverAtaName, e2etests.TestSolanaWhitelistSPLName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } + if testSui { + suiTests := []string{ + e2etests.TestSuiDepositName, + e2etests.TestSuiDepositAndCallName, + e2etests.TestSuiTokenDepositName, + e2etests.TestSuiTokenDepositAndCallName, + } + eg.Go(suiTestRoutine(conf, deployerRunner, verbose, suiTests...)) + } + if testTON { if deployerRunner.Clients.TON == nil { logger.Print("❌ TON client is nil, maybe TON lite-server config is not set") @@ -501,6 +512,13 @@ func localE2ETest(cmd *cobra.Command, _ []string) { logger.Print("✅ e2e tests completed in %s", time.Since(testStartTime).String()) + if testSolana { + require.True( + deployerRunner, + deployerRunner.VerifySolanaContractsUpgrade(conf.AdditionalAccounts.UserSolana.SolanaPrivateKey.String()), + ) + } + if testTSSMigration { TSSMigration(deployerRunner, logger, verbose, conf) } diff --git a/cmd/zetae2e/local/sui.go b/cmd/zetae2e/local/sui.go new file mode 100644 index 0000000000..bbfca0b94a --- /dev/null +++ b/cmd/zetae2e/local/sui.go @@ -0,0 +1,68 @@ +package local + +import ( + "fmt" + "time" + + "github.com/fatih/color" + + "github.com/zeta-chain/node/e2e/config" + "github.com/zeta-chain/node/e2e/e2etests" + "github.com/zeta-chain/node/e2e/runner" +) + +// suiTestRoutine runs Sui related e2e tests +func suiTestRoutine( + conf config.Config, + deployerRunner *runner.E2ERunner, + verbose bool, + testNames ...string, +) func() error { + return func() (err error) { + // initialize runner for sui test + suiRunner, err := initTestRunner( + "sui", + conf, + deployerRunner, + conf.AdditionalAccounts.UserSui, + runner.NewLogger(verbose, color.FgHiCyan, "sui"), + runner.WithZetaTxServer(deployerRunner.ZetaTxServer), + ) + if err != nil { + return err + } + + suiRunner.Logger.Print("🏃 starting Sui tests") + startTime := time.Now() + + suiRunnerSigner, err := suiRunner.Account.SuiSigner() + if err != nil { + return err + } + + // get tokens for the account + suiRunner.RequestSuiFromFaucet(conf.RPCs.SuiFaucet, suiRunnerSigner.Address()) + + // mint fungible tokens to the account + txRes := deployerRunner.SuiMintUSDC("10000000", suiRunnerSigner.Address()) + + deployerRunner.Logger.Info("Sui USDC mint tx: %s", txRes.Digest) + + // run sui test + testsToRun, err := suiRunner.GetE2ETestsToRunByName( + e2etests.AllE2ETests, + testNames..., + ) + if err != nil { + return fmt.Errorf("sui tests failed: %v", err) + } + + if err := suiRunner.RunE2ETests(testsToRun); err != nil { + return fmt.Errorf("sui tests failed: %v", err) + } + + suiRunner.Logger.Print("🍾 sui tests completed in %s", time.Since(startTime).String()) + + return err + } +} diff --git a/cmd/zetae2e/local/tss_migration.go b/cmd/zetae2e/local/tss_migration.go index 30358dc7b4..629c973f7b 100644 --- a/cmd/zetae2e/local/tss_migration.go +++ b/cmd/zetae2e/local/tss_migration.go @@ -90,7 +90,12 @@ func TSSMigration(deployerRunner *runner.E2ERunner, logger *runner.Logger, verbo logger.Print("❌ tss migration failed") os.Exit(1) } + + // Update TSS address for contracts in connected chains + // TODO : Update TSS address for other chains if necessary + // https://github.com/zeta-chain/node/issues/3599 deployerRunner.UpdateTSSAddressForConnector() deployerRunner.UpdateTSSAddressForERC20custody() + deployerRunner.UpdateTSSAddressForGateway() logger.Print("✅ migration completed in %s ", time.Since(migrationStartTime).String()) } diff --git a/contrib/localnet/docker-compose.yml b/contrib/localnet/docker-compose.yml index 41b22d4c7e..346aa3f420 100644 --- a/contrib/localnet/docker-compose.yml +++ b/contrib/localnet/docker-compose.yml @@ -230,6 +230,8 @@ services: mynetwork: ipv4_address: 172.20.0.103 entrypoint: ["/usr/bin/start-solana.sh"] + volumes: + - ssh:/root/.ssh ton: # figure out why E2E fail with MyLocalTon v124 @ deposit: deployer.CreateWallet(..) diff --git a/contrib/localnet/orchestrator/start-zetae2e.sh b/contrib/localnet/orchestrator/start-zetae2e.sh index ef73e05db6..aa78cee7cd 100644 --- a/contrib/localnet/orchestrator/start-zetae2e.sh +++ b/contrib/localnet/orchestrator/start-zetae2e.sh @@ -10,6 +10,8 @@ # Trap signals and forward to children trap 'kill -- -$$' SIGINT SIGTERM +/usr/sbin/sshd + get_zetacored_version() { retries=10 node_info="" @@ -113,6 +115,9 @@ fund_eth_from_config '.additional_accounts.user_bitcoin_withdraw.evm_address' 10 # unlock solana tester accounts fund_eth_from_config '.additional_accounts.user_solana.evm_address' 10000 "solana tester" +# unlock sui tester accounts +fund_eth_from_config '.additional_accounts.user_sui.evm_address' 10000 "sui tester" + # unlock miscellaneous tests accounts fund_eth_from_config '.additional_accounts.user_misc.evm_address' 10000 "misc tester" diff --git a/contrib/localnet/scripts/gateway-upgrade.so b/contrib/localnet/scripts/gateway-upgrade.so new file mode 100755 index 0000000000..8d48973fca Binary files /dev/null and b/contrib/localnet/scripts/gateway-upgrade.so differ diff --git a/contrib/localnet/scripts/start-zetaclientd.sh b/contrib/localnet/scripts/start-zetaclientd.sh index 1627c1cd10..b4a71b0e18 100755 --- a/contrib/localnet/scripts/start-zetaclientd.sh +++ b/contrib/localnet/scripts/start-zetaclientd.sh @@ -69,7 +69,10 @@ RELAYER_KEY_PATH="$HOME/.zetacored/relayer-keys" mkdir -p "${RELAYER_KEY_PATH}" mkdir -p "$HOME/.tss/" -zetae2e local get-zetaclient-bootstrap > "$HOME/.tss/address_book.seed" +address_book_path="$HOME/.tss/address_book.seed" +if [[ ! -f "$address_book_path" ]]; then + zetae2e local get-zetaclient-bootstrap > $address_book_path +fi MYIP=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1) @@ -113,4 +116,7 @@ if [[ -f /root/zetaclient-config-overlay.json ]]; then mv /tmp/merged_config.json /root/.zetacored/config/zetaclient_config.json fi +# ensure restricted addresses config is initialized to avoid log spam +echo "[]" > ~/.zetacored/config/zetaclient_restricted_addresses.json + zetaclientd-supervisor start < /root/password.file \ No newline at end of file diff --git a/contrib/localnet/scripts/start-zetacored.sh b/contrib/localnet/scripts/start-zetacored.sh index 6618eaffe2..043ebec06e 100755 --- a/contrib/localnet/scripts/start-zetacored.sh +++ b/contrib/localnet/scripts/start-zetacored.sh @@ -246,7 +246,9 @@ then # solana tester address=$(yq -r '.additional_accounts.user_solana.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta -# migration tester +# sui tester + address=$(yq -r '.additional_accounts.user_sui.bech32_address' /root/config.yml) + zetacored add-genesis-account "$address" 100000000000000000000000000azeta# migration tester address=$(yq -r '.additional_accounts.user_migration.bech32_address' /root/config.yml) zetacored add-genesis-account "$address" 100000000000000000000000000azeta # precompile tester diff --git a/contrib/localnet/solana/Dockerfile b/contrib/localnet/solana/Dockerfile index 21713112d4..dc1b9a6b8f 100644 --- a/contrib/localnet/solana/Dockerfile +++ b/contrib/localnet/solana/Dockerfile @@ -1,14 +1,28 @@ FROM ghcr.io/zeta-chain/solana-docker:2.0.24 +# Set working directory for the container WORKDIR /data + +# Setup SSH server +# Expose port 22 for SSH access +EXPOSE 22 +# Install and configure SSH server +RUN apt update && apt install -y openssh-server + +# Copy and make executable the main Solana startup script COPY ./start-solana.sh /usr/bin/start-solana.sh RUN chmod +x /usr/bin/start-solana.sh + +# Copy Solana program files and their keypairs COPY ./gateway.so . COPY ./gateway-keypair.json . COPY ./connected.so . COPY ./connected-keypair.json . COPY ./connected_spl.so . COPY ./connected_spl-keypair.json . +COPY ./gateway_upgrade.so . + +RUN mkdir /run/sshd ENTRYPOINT [ "bash" ] CMD [ "/usr/bin/start-solana.sh" ] \ No newline at end of file diff --git a/contrib/localnet/solana/connected.so b/contrib/localnet/solana/connected.so index 5363b358ad..a755b0d90e 100755 Binary files a/contrib/localnet/solana/connected.so and b/contrib/localnet/solana/connected.so differ diff --git a/contrib/localnet/solana/connected_spl.so b/contrib/localnet/solana/connected_spl.so index 821a6e5aa9..7f89b6d221 100755 Binary files a/contrib/localnet/solana/connected_spl.so and b/contrib/localnet/solana/connected_spl.so differ diff --git a/contrib/localnet/solana/gateway.so b/contrib/localnet/solana/gateway.so index 82d484b91c..2f24b7b624 100755 Binary files a/contrib/localnet/solana/gateway.so and b/contrib/localnet/solana/gateway.so differ diff --git a/contrib/localnet/solana/gateway_upgrade.so b/contrib/localnet/solana/gateway_upgrade.so new file mode 100755 index 0000000000..c9a597ceb1 Binary files /dev/null and b/contrib/localnet/solana/gateway_upgrade.so differ diff --git a/contrib/localnet/solana/start-solana.sh b/contrib/localnet/solana/start-solana.sh index 4768e692f2..9433cd8be0 100644 --- a/contrib/localnet/solana/start-solana.sh +++ b/contrib/localnet/solana/start-solana.sh @@ -1,5 +1,6 @@ #!/bin/bash +service ssh start echo "making an id" solana-keygen new -o /root/.config/solana/id.json --no-bip39-passphrase @@ -11,9 +12,29 @@ sleep 5 # airdrop to e2e sol account solana airdrop 1000 solana airdrop 1000 37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ + +# Deploy initial programs solana program deploy gateway.so solana program deploy connected.so solana program deploy connected_spl.so +# Get program ID from gateway keypair +GATEWAY_PROGRAM_ID=$(solana-keygen pubkey gateway-keypair.json) + + +echo "Gateway program ID: $GATEWAY_PROGRAM_ID" +echo "Starting upgrade loop" +# Execute upgrade when execute-update file is found. +# This file is created by the orchestrator when trying to upgrade the program +while true; do + if [ -f "/data/execute-update" ]; then + echo "Found execute-update file, performing upgrade" + solana program deploy gateway_upgrade.so --program-id "$GATEWAY_PROGRAM_ID" + rm /data/execute-update + echo "Upgrade completed and execute-update file removed" + fi + sleep 2 +done + # leave some time for debug if validator exits due to errors sleep 1000 \ No newline at end of file diff --git a/docs/openapi/openapi.html b/docs/openapi/openapi.html index 20f878256f..550722c00d 100644 --- a/docs/openapi/openapi.html +++ b/docs/openapi/openapi.html @@ -24,6 +24,9 @@ dom_id: "#swagger-ui", deepLinking: true, layout: "BaseLayout", + showExtensions: true, + showCommonExtensions: true, + defaultModelExpandDepth: 5 }); } diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 9d91f00409..dd56e3b4e7 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -9155,74 +9155,20 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/params/{params_type}: + /cosmos/gov/v1/constitution: get: - summary: Params queries all parameters of the gov module. - operationId: GovParams + summary: Constitution queries the chain's constitution. + operationId: Constitution responses: '200': description: A successful response. schema: type: object properties: - voting_params: - description: voting_params defines the parameters related to voting. - type: object - properties: - voting_period: - type: string - description: Duration of the voting period. - deposit_params: - description: deposit_params defines the parameters related to deposit. - type: object - properties: - min_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: Minimum deposit for a proposal to enter voting period. - max_deposit_period: - type: string - description: >- - Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - - months. - tally_params: - description: tally_params defines the parameters related to tally. - type: object - properties: - quorum: - type: string - format: byte - description: >- - Minimum percentage of total stake needed to vote for a result to be - - considered valid. - threshold: - type: string - format: byte - description: >- - Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. - veto_threshold: - type: string - format: byte - description: >- - Minimum value of Veto votes to Total votes ratio for proposal to be - - vetoed. Default value: 1/3. - description: >- - QueryParamsResponse is the response type for the Query/Params RPC method. + constitution: + type: string + title: >- + QueryConstitutionResponse is the response type for the Query/Constitution RPC method default: description: An unexpected error response. schema: @@ -9386,277 +9332,193 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: params_type - description: >- - params_type defines which parameters to query for, can be one of "voting", - - "tallying" or "deposit". - in: path - required: true - type: string tags: - Query - /cosmos/gov/v1beta1/proposals: + /cosmos/gov/v1/params/{params_type}: get: - summary: Proposals queries all proposals based on given status. - operationId: Proposals + summary: Params queries all parameters of the gov module. + operationId: GovV1Params responses: '200': description: A successful response. schema: type: object properties: - proposals: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - content: + voting_params: + description: |- + Deprecated: Prefer to use `params` instead. + voting_params defines the parameters related to voting. + type: object + properties: + voting_period: + type: string + description: Duration of the voting period. + deposit_params: + description: |- + Deprecated: Prefer to use `params` instead. + deposit_params defines the parameters related to deposit. + type: object + properties: + min_deposit: + type: array + items: type: object properties: - type_url: + denom: type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: + amount: type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack + Coin defines a token with a denomination and an amount. - methods only use the fully qualified type name after the last '/' + NOTE: The amount field is an Int which implements the custom method - in the type URL, for example "foo.bar.com/x/y.z" will yield type + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - name "y.z". + months. + tally_params: + description: |- + Deprecated: Prefer to use `params` instead. + tally_params defines the parameters related to tally. + type: object + properties: + quorum: + type: string + description: >- + Minimum percentage of total stake needed to vote for a result to be - JSON + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + veto_threshold: + type: string + description: >- + Minimum value of Veto votes to Total votes ratio for proposal to be - The JSON representation of an `Any` value uses the regular + vetoed. Default value: 1/3. + params: + description: |- + params defines all the paramaters of x/gov module. - representation of the deserialized, embedded message, with an + Since: cosmos-sdk 0.47 + type: object + properties: + min_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - additional field `@type` which contains the type URL. Example: + NOTE: The amount field is an Int which implements the custom method - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + signatures required by gogoproto. + description: Minimum deposit for a proposal to enter voting period. + max_deposit_period: + type: string + description: >- + Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + months. + voting_period: + type: string + description: Duration of the voting period. + quorum: + type: string + description: >- + Minimum percentage of total stake needed to vote for a result to be - If the embedded message type is well-known and has a custom JSON + considered valid. + threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. + veto_threshold: + type: string + description: >- + Minimum value of Veto votes to Total votes ratio for proposal to be - representation, that representation will be embedded adding a field + vetoed. Default value: 1/3. + min_initial_deposit_ratio: + type: string + description: >- + The ratio representing the proportion of the deposit value that must be paid at proposal submission. + proposal_cancel_ratio: + type: string + description: >- + The cancel ratio which will not be returned back to the depositors when a proposal is cancelled. - `value` which holds the custom JSON in addition to the `@type` + Since: cosmos-sdk 0.50 + proposal_cancel_dest: + type: string + description: >- + The address which will receive (proposal_cancel_ratio * deposit) proposal deposits. - field. Example (for message [google.protobuf.Duration][]): + If empty, the (proposal_cancel_ratio * deposit) proposal deposits will be burned. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: - description: status defines the proposal status. - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - final_tally_result: - description: >- - final_tally_result is the final tally result of the proposal. When + Since: cosmos-sdk 0.50 + expedited_voting_period: + type: string + description: |- + Duration of the voting period of an expedited proposal. - querying a proposal via gRPC, this field is not populated until the + Since: cosmos-sdk 0.50 + expedited_threshold: + type: string + description: >- + Minimum proportion of Yes votes for proposal to pass. Default value: 0.67. - proposal's voting period has ended. + Since: cosmos-sdk 0.50 + expedited_min_deposit: + type: array + items: type: object properties: - 'yes': - type: string - description: yes is the number of yes votes on a proposal. - abstain: - type: string - description: >- - abstain is the number of abstain votes on a proposal. - 'no': + denom: type: string - description: no is the number of no votes on a proposal. - no_with_veto: + amount: type: string - description: >- - no_with_veto is the number of no with veto votes on a proposal. - submit_time: - type: string - format: date-time - description: submit_time is the time of proposal submission. - deposit_end_time: - type: string - format: date-time - description: deposit_end_time is the end time for deposition. - total_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + description: >- + Coin defines a token with a denomination and an amount. - NOTE: The amount field is an Int which implements the custom method + NOTE: The amount field is an Int which implements the custom method - signatures required by gogoproto. - description: total_deposit is the total deposit on the proposal. - voting_start_time: - type: string - format: date-time - description: >- - voting_start_time is the starting time to vote on a proposal. - voting_end_time: - type: string - format: date-time - description: voting_end_time is the end time of voting on a proposal. - description: >- - Proposal defines the core field members of a governance proposal. - description: proposals defines all the requested governance proposals. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + signatures required by gogoproto. + description: >- + Minimum expedited deposit for a proposal to enter voting period. + burn_vote_quorum: + type: boolean + title: burn deposits if a proposal does not meet quorum + burn_proposal_deposit_prevote: + type: boolean + title: burn deposits if the proposal does not enter voting period + burn_vote_veto: + type: boolean + title: burn deposits if quorum with vote type no_veto is met + min_deposit_ratio: type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + description: >- + The ratio representing the proportion of the deposit value minimum that must be met when making a deposit. - was set, its value is undefined otherwise - description: >- - QueryProposalsResponse is the response type for the Query/Proposals RPC + Default value: 0.01. Meaning that for a chain with a min_deposit of 100stake, a deposit of 1stake would be - method. + required. + + Since: cosmos-sdk 0.50 + description: >- + QueryParamsResponse is the response type for the Query/Params RPC method. default: description: An unexpected error response. schema: @@ -9821,329 +9683,308 @@ paths: "value": "1.212s" } parameters: - - name: proposal_status - description: |- - proposal_status defines the status of the proposals. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - in: query - required: false - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - - name: voter - description: voter defines the voter address for the proposals. - in: query - required: false - type: string - - name: depositor - description: depositor defines the deposit addresses from the proposals. - in: query - required: false - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit + - name: params_type description: >- - limit is the total number of results to be returned in the result page. + params_type defines which parameters to query for, can be one of "voting", - If left empty it will default to a value to be set by each app. - in: query - required: false + "tallying" or "deposit". + in: path + required: true type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}: + /cosmos/gov/v1/proposals: get: - summary: Proposal queries proposal details based on ProposalID. - operationId: Proposal + summary: Proposals queries all proposals based on given status. + operationId: GovV1Proposal responses: '200': description: A successful response. schema: type: object properties: - proposal: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - content: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + proposals: + type: array + items: + type: object + properties: + id: + type: string + format: uint64 + description: id defines the unique id of the proposal. + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - protocol buffer message. This string must contain at least + protocol buffer message. This string must contain at least - one "/" character. The last segment of the URL's path must represent + one "/" character. The last segment of the URL's path must represent - the fully qualified name of the type (as in + the fully qualified name of the type (as in - `path/google.protobuf.Duration`). The name should be in a canonical form + `path/google.protobuf.Duration`). The name should be in a canonical form - (e.g., leading "." is not accepted). + (e.g., leading "." is not accepted). - In practice, teams usually precompile into the binary all types that they + In practice, teams usually precompile into the binary all types that they - expect it to use in the context of Any. However, for URLs which use the + expect it to use in the context of Any. However, for URLs which use the - scheme `http`, `https`, or no scheme, one can optionally set up a type + scheme `http`, `https`, or no scheme, one can optionally set up a type - server that maps type URLs to message definitions as follows: + server that maps type URLs to message definitions as follows: - * If no scheme is provided, `https` is assumed. + * If no scheme is provided, `https` is assumed. - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Note: this functionality is not currently available in the official + Note: this functionality is not currently available in the official - protobuf release, and it is not used for type URLs beginning with + protobuf release, and it is not used for type URLs beginning with - type.googleapis.com. + type.googleapis.com. - Schemes other than `http`, `https` (or the empty scheme) might be + Schemes other than `http`, `https` (or the empty scheme) might be - used with implementation specific semantics. - value: - type: string - format: byte + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + `Any` contains an arbitrary serialized protocol buffer message along with a - URL that describes the type of the serialized message. + URL that describes the type of the serialized message. - Protobuf library provides support to pack/unpack Any values in the form + Protobuf library provides support to pack/unpack Any values in the form - of utility functions or additional generated methods of the Any type. + of utility functions or additional generated methods of the Any type. - Example 1: Pack and unpack a message in C++. + Example 1: Pack and unpack a message in C++. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - Example 2: Pack and unpack a message in Java. + Example 2: Pack and unpack a message in Java. - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - Example 3: Pack and unpack a message in Python. + Example 3: Pack and unpack a message in Python. - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - Example 4: Pack and unpack a message in Go + Example 4: Pack and unpack a message in Go - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The pack methods provided by protobuf library will by default use + The pack methods provided by protobuf library will by default use - 'type.googleapis.com/full.type.name' as the type URL and the unpack + 'type.googleapis.com/full.type.name' as the type URL and the unpack - methods only use the fully qualified type name after the last '/' + methods only use the fully qualified type name after the last '/' - in the type URL, for example "foo.bar.com/x/y.z" will yield type + in the type URL, for example "foo.bar.com/x/y.z" will yield type - name "y.z". + name "y.z". - JSON + JSON - The JSON representation of an `Any` value uses the regular + The JSON representation of an `Any` value uses the regular - representation of the deserialized, embedded message, with an + representation of the deserialized, embedded message, with an - additional field `@type` which contains the type URL. Example: + additional field `@type` which contains the type URL. Example: - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - If the embedded message type is well-known and has a custom JSON + If the embedded message type is well-known and has a custom JSON - representation, that representation will be embedded adding a field + representation, that representation will be embedded adding a field - `value` which holds the custom JSON in addition to the `@type` + `value` which holds the custom JSON in addition to the `@type` - field. Example (for message [google.protobuf.Duration][]): + field. Example (for message [google.protobuf.Duration][]): - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - status: - description: status defines the proposal status. - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - final_tally_result: - description: >- - final_tally_result is the final tally result of the proposal. When + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages are the arbitrary messages to be executed if the proposal passes. + status: + description: status defines the proposal status. + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: + description: >- + final_tally_result is the final tally result of the proposal. When - querying a proposal via gRPC, this field is not populated until the + querying a proposal via gRPC, this field is not populated until the - proposal's voting period has ended. - type: object - properties: - 'yes': - type: string - description: yes is the number of yes votes on a proposal. - abstain: - type: string - description: abstain is the number of abstain votes on a proposal. - 'no': - type: string - description: no is the number of no votes on a proposal. - no_with_veto: - type: string - description: >- - no_with_veto is the number of no with veto votes on a proposal. - submit_time: - type: string - format: date-time - description: submit_time is the time of proposal submission. - deposit_end_time: - type: string - format: date-time - description: deposit_end_time is the end time for deposition. - total_deposit: - type: array - items: + proposal's voting period has ended. type: object properties: - denom: + yes_count: type: string - amount: + description: yes_count is the number of yes votes on a proposal. + abstain_count: + type: string + description: >- + abstain_count is the number of abstain votes on a proposal. + no_count: + type: string + description: no_count is the number of no votes on a proposal. + no_with_veto_count: type: string + description: >- + no_with_veto_count is the number of no with veto votes on a proposal. + submit_time: + type: string + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + + signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. + voting_start_time: + type: string + format: date-time description: >- - Coin defines a token with a denomination and an amount. + voting_start_time is the starting time to vote on a proposal. + voting_end_time: + type: string + format: date-time + description: voting_end_time is the end time of voting on a proposal. + metadata: + type: string + title: >- + metadata is any arbitrary metadata attached to the proposal. - NOTE: The amount field is an Int which implements the custom method + the recommended format of the metadata is to be found here: - signatures required by gogoproto. - description: total_deposit is the total deposit on the proposal. - voting_start_time: + https://docs.cosmos.network/v0.47/modules/gov#proposal-3 + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: proposer is the address of the proposal sumbitter + expedited: + type: boolean + description: 'Since: cosmos-sdk 0.50' + title: expedited defines if the proposal is expedited + failed_reason: + type: string + description: 'Since: cosmos-sdk 0.50' + title: failed_reason defines the reason why the proposal failed + description: >- + Proposal defines the core field members of a governance proposal. + description: proposals defines all the requested governance proposals. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: type: string - format: date-time - description: >- - voting_start_time is the starting time to vote on a proposal. - voting_end_time: + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: type: string - format: date-time - description: voting_end_time is the end time of voting on a proposal. - description: >- - Proposal defines the core field members of a governance proposal. + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise description: >- - QueryProposalResponse is the response type for the Query/Proposal RPC method. + QueryProposalsResponse is the response type for the Query/Proposals RPC + + method. default: description: An unexpected error response. schema: @@ -10308,51 +10149,571 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true + - name: proposal_status + description: |- + proposal_status defines the status of the proposals. + + - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. + - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit + period. + - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting + period. + - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has + passed. + - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has + been rejected. + - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has + failed. + in: query + required: false + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + - name: voter + description: voter defines the voter address for the proposals. + in: query + required: false + type: string + - name: depositor + description: depositor defines the deposit addresses from the proposals. + in: query + required: false + type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false type: string format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits: + /cosmos/gov/v1/proposals/{proposal_id}: get: - summary: Deposits queries all deposits of a single proposal. - operationId: Deposits + summary: Proposal queries proposal details based on ProposalID. + operationId: GovV1Proposal responses: '200': description: A successful response. schema: type: object properties: - deposits: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - depositor: - type: string - description: >- - depositor defines the deposit addresses from the proposals. - amount: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - + proposal: + type: object + properties: + id: + type: string + format: uint64 + description: id defines the unique id of the proposal. + messages: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + messages are the arbitrary messages to be executed if the proposal passes. + status: + description: status defines the proposal status. + type: string + enum: + - PROPOSAL_STATUS_UNSPECIFIED + - PROPOSAL_STATUS_DEPOSIT_PERIOD + - PROPOSAL_STATUS_VOTING_PERIOD + - PROPOSAL_STATUS_PASSED + - PROPOSAL_STATUS_REJECTED + - PROPOSAL_STATUS_FAILED + default: PROPOSAL_STATUS_UNSPECIFIED + final_tally_result: + description: >- + final_tally_result is the final tally result of the proposal. When + + querying a proposal via gRPC, this field is not populated until the + + proposal's voting period has ended. + type: object + properties: + yes_count: + type: string + description: yes_count is the number of yes votes on a proposal. + abstain_count: + type: string + description: >- + abstain_count is the number of abstain votes on a proposal. + no_count: + type: string + description: no_count is the number of no votes on a proposal. + no_with_veto_count: + type: string + description: >- + no_with_veto_count is the number of no with veto votes on a proposal. + submit_time: + type: string + format: date-time + description: submit_time is the time of proposal submission. + deposit_end_time: + type: string + format: date-time + description: deposit_end_time is the end time for deposition. + total_deposit: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + + signatures required by gogoproto. + description: total_deposit is the total deposit on the proposal. + voting_start_time: + type: string + format: date-time + description: >- + voting_start_time is the starting time to vote on a proposal. + voting_end_time: + type: string + format: date-time + description: voting_end_time is the end time of voting on a proposal. + metadata: + type: string + title: >- + metadata is any arbitrary metadata attached to the proposal. + + the recommended format of the metadata is to be found here: + + https://docs.cosmos.network/v0.47/modules/gov#proposal-3 + title: + type: string + description: 'Since: cosmos-sdk 0.47' + title: title is the title of the proposal + summary: + type: string + description: 'Since: cosmos-sdk 0.47' + title: summary is a short summary of the proposal + proposer: + type: string + description: 'Since: cosmos-sdk 0.47' + title: proposer is the address of the proposal sumbitter + expedited: + type: boolean + description: 'Since: cosmos-sdk 0.50' + title: expedited defines if the proposal is expedited + failed_reason: + type: string + description: 'Since: cosmos-sdk 0.50' + title: failed_reason defines the reason why the proposal failed + description: >- + Proposal defines the core field members of a governance proposal. + description: >- + QueryProposalResponse is the response type for the Query/Proposal RPC method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: proposal_id + description: proposal_id defines the unique id of the proposal. + in: path + required: true + type: string + format: uint64 + tags: + - Query + /cosmos/gov/v1/proposals/{proposal_id}/deposits: + get: + summary: Deposits queries all deposits of a single proposal. + operationId: GovV1Deposit + responses: + '200': + description: A successful response. + schema: + type: object + properties: + deposits: + type: array + items: + type: object + properties: + proposal_id: + type: string + format: uint64 + description: proposal_id defines the unique id of the proposal. + depositor: + type: string + description: >- + depositor defines the deposit addresses from the proposals. + amount: + type: array + items: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + signatures required by gogoproto. description: amount to be deposited by depositor. description: >- @@ -10601,11 +10962,11 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/deposits/{depositor}: + /cosmos/gov/v1/proposals/{proposal_id}/deposits/{depositor}: get: summary: >- - Deposit queries single deposit information based on proposalID, depositor address. - operationId: Deposit + Deposit queries single deposit information based on proposalID, depositAddr. + operationId: GovV1Deposit responses: '200': description: A successful response. @@ -10822,10 +11183,10 @@ paths: type: string tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/tally: + /cosmos/gov/v1/proposals/{proposal_id}/tally: get: summary: TallyResult queries the tally of a proposal vote. - operationId: TallyResult + operationId: GovV1TallyResult responses: '200': description: A successful response. @@ -10836,19 +11197,20 @@ paths: description: tally defines the requested tally. type: object properties: - 'yes': + yes_count: type: string - description: yes is the number of yes votes on a proposal. - abstain: + description: yes_count is the number of yes votes on a proposal. + abstain_count: type: string - description: abstain is the number of abstain votes on a proposal. - 'no': + description: >- + abstain_count is the number of abstain votes on a proposal. + no_count: type: string - description: no is the number of no votes on a proposal. - no_with_veto: + description: no_count is the number of no votes on a proposal. + no_with_veto_count: type: string description: >- - no_with_veto is the number of no with veto votes on a proposal. + no_with_veto_count is the number of no with veto votes on a proposal. description: >- QueryTallyResultResponse is the response type for the Query/Tally RPC method. default: @@ -11023,10 +11385,10 @@ paths: format: uint64 tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/votes: + /cosmos/gov/v1/proposals/{proposal_id}/votes: get: summary: Votes queries votes of a given proposal. - operationId: Votes + operationId: GovV1Votes responses: '200': description: A successful response. @@ -11045,21 +11407,6 @@ paths: voter: type: string description: voter is the voter address of the proposal. - option: - description: >- - Deprecated: Prefer to use `options` instead. This field is set in queries - - if and only if `len(options) == 1` and that option has weight 1. In all - - other cases, this field will default to VOTE_OPTION_UNSPECIFIED. - type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED options: type: array items: @@ -11082,12 +11429,13 @@ paths: weight is the vote weight associated with the vote option. description: >- WeightedVoteOption defines a unit of vote for vote split. + description: options is the weighted vote options. + metadata: + type: string + title: >- + metadata is any arbitrary metadata attached to the vote. - Since: cosmos-sdk 0.43 - description: |- - options is the weighted vote options. - - Since: cosmos-sdk 0.43 + the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#vote-5 description: >- Vote defines a vote on a governance proposal. @@ -11334,10 +11682,10 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1beta1/proposals/{proposal_id}/votes/{voter}: + /cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}: get: summary: Vote queries voted information based on proposalID, voterAddr. - operationId: Vote + operationId: GovV1Vote responses: '200': description: A successful response. @@ -11354,21 +11702,6 @@ paths: voter: type: string description: voter is the voter address of the proposal. - option: - description: >- - Deprecated: Prefer to use `options` instead. This field is set in queries - - if and only if `len(options) == 1` and that option has weight 1. In all - - other cases, this field will default to VOTE_OPTION_UNSPECIFIED. - type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED options: type: array items: @@ -11391,12 +11724,13 @@ paths: weight is the vote weight associated with the vote option. description: >- WeightedVoteOption defines a unit of vote for vote split. + description: options is the weighted vote options. + metadata: + type: string + title: >- + metadata is any arbitrary metadata attached to the vote. - Since: cosmos-sdk 0.43 - description: |- - options is the weighted vote options. - - Since: cosmos-sdk 0.43 + the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#vote-5 description: >- Vote defines a vote on a governance proposal. @@ -11580,20 +11914,30 @@ paths: type: string tags: - Query - /cosmos/gov/v1/constitution: + /cosmos/params/v1beta1/params: get: - summary: Constitution queries the chain's constitution. - operationId: Constitution + summary: |- + Params queries a specific parameter of a module, given its subspace and + key. + operationId: Params responses: '200': description: A successful response. schema: type: object properties: - constitution: - type: string - title: >- - QueryConstitutionResponse is the response type for the Query/Constitution RPC method + param: + description: param defines the queried parameter. + type: object + properties: + subspace: + type: string + key: + type: string + value: + type: string + description: >- + QueryParamsResponse is response type for the Query/Params RPC method. default: description: An unexpected error response. schema: @@ -11613,337 +11957,465 @@ paths: properties: type_url: type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. value: type: string format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. + parameters: + - name: subspace + description: subspace defines the module to query the parameter for. + in: query + required: false + type: string + - name: key + description: key defines the key of the parameter in the subspace. + in: query + required: false + type: string + tags: + - Query + /cosmos/params/v1beta1/subspaces: + get: + summary: >- + Subspaces queries for all registered subspaces and all keys for a subspace. + description: 'Since: cosmos-sdk 0.46' + operationId: Subspaces + responses: + '200': + description: A successful response. + schema: + type: object + properties: + subspaces: + type: array + items: + type: object + properties: + subspace: + type: string + keys: + type: array + items: + type: string description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON + Subspace defines a parameter subspace name and all the keys that exist for - representation, that representation will be embedded adding a field + the subspace. - `value` which holds the custom JSON in addition to the `@type` + Since: cosmos-sdk 0.46 + description: >- + QuerySubspacesResponse defines the response types for querying for all - field. Example (for message [google.protobuf.Duration][]): + registered subspaces and all keys for a subspace. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } + Since: cosmos-sdk 0.46 + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte tags: - Query - /cosmos/gov/v1/params/{params_type}: + /cosmos/slashing/v1beta1/params: get: - summary: Params queries all parameters of the gov module. - operationId: GovV1Params + summary: Params queries the parameters of slashing module + operationId: SlashingParams responses: '200': description: A successful response. schema: type: object properties: - voting_params: - description: |- - Deprecated: Prefer to use `params` instead. - voting_params defines the parameters related to voting. + params: type: object properties: - voting_period: + signed_blocks_window: type: string - description: Duration of the voting period. - deposit_params: - description: |- - Deprecated: Prefer to use `params` instead. - deposit_params defines the parameters related to deposit. - type: object - properties: - min_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: Minimum deposit for a proposal to enter voting period. - max_deposit_period: + format: int64 + min_signed_per_window: type: string - description: >- - Maximum period for Atom holders to deposit on a proposal. Initial value: 2 - - months. - tally_params: - description: |- - Deprecated: Prefer to use `params` instead. - tally_params defines the parameters related to tally. - type: object - properties: - quorum: + format: byte + downtime_jail_duration: type: string - description: >- - Minimum percentage of total stake needed to vote for a result to be - - considered valid. - threshold: + slash_fraction_double_sign: type: string - description: >- - Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. - veto_threshold: + format: byte + slash_fraction_downtime: type: string - description: >- - Minimum value of Veto votes to Total votes ratio for proposal to be - - vetoed. Default value: 1/3. - params: - description: |- - params defines all the paramaters of x/gov module. - - Since: cosmos-sdk 0.47 - type: object - properties: - min_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string + format: byte + description: >- + Params represents the parameters used for by the slashing module. + title: >- + QueryParamsResponse is the response type for the Query/Params RPC method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query + /cosmos/slashing/v1beta1/signing_infos: + get: + summary: SigningInfos queries signing info of all validators + operationId: SigningInfos + responses: + '200': + description: A successful response. + schema: + type: object + properties: + info: + type: array + items: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was un-jailed + index_offset: + type: string + format: int64 description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: Minimum deposit for a proposal to enter voting period. - max_deposit_period: - type: string - description: >- - Maximum period for Atom holders to deposit on a proposal. Initial value: 2 + Index which is incremented every time a validator is bonded in a block and - months. - voting_period: - type: string - description: Duration of the voting period. - quorum: - type: string - description: >- - Minimum percentage of total stake needed to vote for a result to be + _may_ have signed a pre-commit or not. This in conjunction with the - considered valid. - threshold: - type: string - description: >- - Minimum proportion of Yes votes for proposal to pass. Default value: 0.5. - veto_threshold: - type: string - description: >- - Minimum value of Veto votes to Total votes ratio for proposal to be + signed_blocks_window param determines the index in the missed block bitmap. + jailed_until: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to liveness downtime. + tombstoned: + type: boolean + description: >- + Whether or not a validator has been tombstoned (killed out of validator - vetoed. Default value: 1/3. - min_initial_deposit_ratio: - type: string - description: >- - The ratio representing the proportion of the deposit value that must be paid at proposal submission. - proposal_cancel_ratio: - type: string - description: >- - The cancel ratio which will not be returned back to the depositors when a proposal is cancelled. + set). It is set once the validator commits an equivocation or for any other - Since: cosmos-sdk 0.50 - proposal_cancel_dest: - type: string - description: >- - The address which will receive (proposal_cancel_ratio * deposit) proposal deposits. + configured misbehavior. + missed_blocks_counter: + type: string + format: int64 + description: >- + A counter of missed (unsigned) blocks. It is used to avoid unnecessary - If empty, the (proposal_cancel_ratio * deposit) proposal deposits will be burned. + reads in the missed block bitmap. + description: >- + ValidatorSigningInfo defines a validator's signing info for monitoring their - Since: cosmos-sdk 0.50 - expedited_voting_period: + liveness activity. + title: info is the signing info of all validators + pagination: + type: object + properties: + next_key: type: string + format: byte description: |- - Duration of the voting period of an expedited proposal. - - Since: cosmos-sdk 0.50 - expedited_threshold: + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: type: string - description: >- - Minimum proportion of Yes votes for proposal to pass. Default value: 0.67. - - Since: cosmos-sdk 0.50 - expedited_min_deposit: - type: array - items: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total - signatures required by gogoproto. - description: >- - Minimum expedited deposit for a proposal to enter voting period. - burn_vote_quorum: - type: boolean - title: burn deposits if a proposal does not meet quorum - burn_proposal_deposit_prevote: - type: boolean - title: burn deposits if the proposal does not enter voting period - burn_vote_veto: - type: boolean - title: burn deposits if quorum with vote type no_veto is met - min_deposit_ratio: - type: string - description: >- - The ratio representing the proportion of the deposit value minimum that must be met when making a deposit. + was set, its value is undefined otherwise + description: >- + PageResponse is to be embedded in gRPC response messages where the - Default value: 0.01. Meaning that for a chain with a min_deposit of 100stake, a deposit of 1stake would be + corresponding request message has used PageRequest. - required. + message SomeResponse { + repeated Bar results = 1; + PageResponse page = 2; + } + title: >- + QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC - Since: cosmos-sdk 0.50 - description: >- - QueryParamsResponse is the response type for the Query/Params RPC method. + method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/slashing/v1beta1/signing_infos/{cons_address}: + get: + summary: SigningInfo queries the signing info of given cons address + operationId: SigningInfo + responses: + '200': + description: A successful response. + schema: + type: object + properties: + val_signing_info: + type: object + properties: + address: + type: string + start_height: + type: string + format: int64 + title: >- + Height at which validator was first a candidate OR was un-jailed + index_offset: + type: string + format: int64 + description: >- + Index which is incremented every time a validator is bonded in a block and + + _may_ have signed a pre-commit or not. This in conjunction with the + + signed_blocks_window param determines the index in the missed block bitmap. + jailed_until: + type: string + format: date-time + description: >- + Timestamp until which the validator is jailed due to liveness downtime. + tombstoned: + type: boolean + description: >- + Whether or not a validator has been tombstoned (killed out of validator + + set). It is set once the validator commits an equivocation or for any other + + configured misbehavior. + missed_blocks_counter: + type: string + format: int64 + description: >- + A counter of missed (unsigned) blocks. It is used to avoid unnecessary + + reads in the missed block bitmap. + description: >- + ValidatorSigningInfo defines a validator's signing info for monitoring their + + liveness activity. + title: >- + val_signing_info is the signing info of requested val cons address + title: >- + QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC + + method + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + parameters: + - name: cons_address + description: cons_address is the address to query signing info of + in: path + required: true + type: string + tags: + - Query + /cosmos/staking/v1beta1/delegations/{delegator_addr}: + get: + summary: >- + DelegatorDelegations queries all delegations of a given delegator address. + description: >- + When called from another module, this query might consume a high amount of + + gas if the pagination field is incorrectly set. + operationId: DelegatorDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + delegation_responses: + type: array + items: + type: object + properties: + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the encoded address of the delegator. + validator_address: + type: string + description: >- + validator_address is the encoded address of the validator. + shares: + type: string + description: shares define the delegation shares received. + description: >- + Delegation represents the bond with tokens held by an account. It is + + owned by one delegator, and is associated with the voting power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it contains a + + balance in addition to shares which is more suitable for client responses. + description: >- + delegation_responses defines all the delegations' info of a delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorDelegationsResponse is response type for the + Query/DelegatorDelegations RPC method. default: description: An unexpected error response. schema: @@ -12108,286 +12580,510 @@ paths: "value": "1.212s" } parameters: - - name: params_type - description: >- - params_type defines which parameters to query for, can be one of "voting", - - "tallying" or "deposit". + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. in: path required: true type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/gov/v1/proposals: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations: get: - summary: Proposals queries all proposals based on given status. - operationId: GovV1Proposal + summary: Redelegations queries redelegations of given address. + description: >- + When called from another module, this query might consume a high amount of + + gas if the pagination field is incorrectly set. + operationId: Redelegations responses: '200': description: A successful response. schema: type: object properties: - proposals: + redelegation_responses: type: array items: type: object properties: - id: - type: string - format: uint64 - description: id defines the unique id of the proposal. - messages: + redelegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the bech32-encoded address of the delegator. + validator_src_address: + type: string + description: >- + validator_src_address is the validator redelegation source operator address. + validator_dst_address: + type: string + description: >- + validator_dst_address is the validator redelegation destination operator address. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by external modules + description: >- + RedelegationEntry defines a redelegation object with relevant metadata. + description: entries are the redelegation entries. + description: >- + Redelegation contains the list of a particular delegator's redelegating bonds + + from a particular source validator to a particular destination validator. + entries: type: array items: type: object properties: - type_url: - type: string + redelegation_entry: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height defines the height which the redelegation took place. + completion_time: + type: string + format: date-time + description: >- + completion_time defines the unix time for redelegation completion. + initial_balance: + type: string + description: >- + initial_balance defines the initial balance when redelegation started. + shares_dst: + type: string + description: >- + shares_dst is the amount of destination-validator shares created by redelegation. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by external modules description: >- - A URL/resource name that uniquely identifies the type of the serialized + RedelegationEntry defines a redelegation object with relevant metadata. + balance: + type: string + description: >- + RedelegationEntryResponse is equivalent to a RedelegationEntry except that it - protocol buffer message. This string must contain at least + contains a balance in addition to shares which is more suitable for client - one "/" character. The last segment of the URL's path must represent + responses. + description: >- + RedelegationResponse is equivalent to a Redelegation except that its entries - the fully qualified name of the type (as in + contain a balance in addition to shares which is more suitable for client - `path/google.protobuf.Duration`). The name should be in a canonical form + responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total - (e.g., leading "." is not accepted). + was set, its value is undefined otherwise + description: >- + QueryRedelegationsResponse is response type for the Query/Redelegations RPC - In practice, teams usually precompile into the binary all types that they + method. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - expect it to use in the context of Any. However, for URLs which use the + protocol buffer message. This string must contain at least - scheme `http`, `https`, or no scheme, one can optionally set up a type + one "/" character. The last segment of the URL's path must represent - server that maps type URLs to message definitions as follows: + the fully qualified name of the type (as in - * If no scheme is provided, `https` is assumed. + `path/google.protobuf.Duration`). The name should be in a canonical form - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + (e.g., leading "." is not accepted). - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + In practice, teams usually precompile into the binary all types that they - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + expect it to use in the context of Any. However, for URLs which use the - Note: this functionality is not currently available in the official + scheme `http`, `https`, or no scheme, one can optionally set up a type - protobuf release, and it is not used for type URLs beginning with + server that maps type URLs to message definitions as follows: - type.googleapis.com. + * If no scheme is provided, `https` is assumed. - Schemes other than `http`, `https` (or the empty scheme) might be + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - URL that describes the type of the serialized message. + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - Protobuf library provides support to pack/unpack Any values in the form + Note: this functionality is not currently available in the official - of utility functions or additional generated methods of the Any type. + protobuf release, and it is not used for type URLs beginning with - Example 1: Pack and unpack a message in C++. + type.googleapis.com. - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + Schemes other than `http`, `https` (or the empty scheme) might be - Example 2: Pack and unpack a message in Java. + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + URL that describes the type of the serialized message. - Example 3: Pack and unpack a message in Python. + Protobuf library provides support to pack/unpack Any values in the form - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + of utility functions or additional generated methods of the Any type. - Example 4: Pack and unpack a message in Go + Example 1: Pack and unpack a message in C++. - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - The pack methods provided by protobuf library will by default use + Example 2: Pack and unpack a message in Java. - 'type.googleapis.com/full.type.name' as the type URL and the unpack + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - methods only use the fully qualified type name after the last '/' + Example 3: Pack and unpack a message in Python. - in the type URL, for example "foo.bar.com/x/y.z" will yield type + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - name "y.z". + Example 4: Pack and unpack a message in Go - JSON + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - The JSON representation of an `Any` value uses the regular + The pack methods provided by protobuf library will by default use - representation of the deserialized, embedded message, with an + 'type.googleapis.com/full.type.name' as the type URL and the unpack - additional field `@type` which contains the type URL. Example: + methods only use the fully qualified type name after the last '/' - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + in the type URL, for example "foo.bar.com/x/y.z" will yield type - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + name "y.z". - If the embedded message type is well-known and has a custom JSON + JSON - representation, that representation will be embedded adding a field + The JSON representation of an `Any` value uses the regular - `value` which holds the custom JSON in addition to the `@type` + representation of the deserialized, embedded message, with an - field. Example (for message [google.protobuf.Duration][]): + additional field `@type` which contains the type URL. Example: - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - description: >- - messages are the arbitrary messages to be executed if the proposal passes. - status: - description: status defines the proposal status. - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - final_tally_result: - description: >- - final_tally_result is the final tally result of the proposal. When + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - querying a proposal via gRPC, this field is not populated until the + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - proposal's voting period has ended. - type: object - properties: - yes_count: - type: string - description: yes_count is the number of yes votes on a proposal. - abstain_count: - type: string - description: >- - abstain_count is the number of abstain votes on a proposal. - no_count: - type: string - description: no_count is the number of no votes on a proposal. - no_with_veto_count: - type: string - description: >- - no_with_veto_count is the number of no with veto votes on a proposal. - submit_time: + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string + - name: src_validator_addr + description: src_validator_addr defines the validator address to redelegate from. + in: query + required: false + type: string + - name: dst_validator_addr + description: dst_validator_addr defines the validator address to redelegate to. + in: query + required: false + type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + tags: + - Query + /cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations: + get: + summary: >- + DelegatorUnbondingDelegations queries all unbonding delegations of a given + + delegator address. + description: >- + When called from another module, this query might consume a high amount of + + gas if the pagination field is incorrectly set. + operationId: DelegatorUnbondingDelegations + responses: + '200': + description: A successful response. + schema: + type: object + properties: + unbonding_responses: + type: array + items: + type: object + properties: + delegator_address: type: string - format: date-time - description: submit_time is the time of proposal submission. - deposit_end_time: + description: >- + delegator_address is the encoded address of the delegator. + validator_address: type: string - format: date-time - description: deposit_end_time is the end time for deposition. - total_deposit: + description: >- + validator_address is the encoded address of the validator. + entries: type: array items: type: object properties: - denom: + creation_height: type: string - amount: + format: int64 + description: >- + creation_height is the height which the unbonding took place. + completion_time: type: string + format: date-time + description: >- + completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by external modules description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: total_deposit is the total deposit on the proposal. - voting_start_time: - type: string - format: date-time - description: >- - voting_start_time is the starting time to vote on a proposal. - voting_end_time: - type: string - format: date-time - description: voting_end_time is the end time of voting on a proposal. - metadata: - type: string - title: >- - metadata is any arbitrary metadata attached to the proposal. - - the recommended format of the metadata is to be found here: - - https://docs.cosmos.network/v0.47/modules/gov#proposal-3 - title: - type: string - description: 'Since: cosmos-sdk 0.47' - title: title is the title of the proposal - summary: - type: string - description: 'Since: cosmos-sdk 0.47' - title: summary is a short summary of the proposal - proposer: - type: string - description: 'Since: cosmos-sdk 0.47' - title: proposer is the address of the proposal sumbitter - expedited: - type: boolean - description: 'Since: cosmos-sdk 0.50' - title: expedited defines if the proposal is expedited - failed_reason: - type: string - description: 'Since: cosmos-sdk 0.50' - title: failed_reason defines the reason why the proposal failed + UnbondingDelegationEntry defines an unbonding object with relevant metadata. + description: entries are the unbonding delegation entries. description: >- - Proposal defines the core field members of a governance proposal. - description: proposals defines all the requested governance proposals. + UnbondingDelegation stores all of a single delegator's unbonding bonds + + for a single validator in an time-ordered list. pagination: description: pagination defines the pagination in the response. type: object @@ -12407,9 +13103,9 @@ paths: was set, its value is undefined otherwise description: >- - QueryProposalsResponse is the response type for the Query/Proposals RPC + QueryUnbondingDelegatorDelegationsResponse is response type for the - method. + Query/UnbondingDelegatorDelegations RPC method. default: description: An unexpected error response. schema: @@ -12574,41 +13270,10 @@ paths: "value": "1.212s" } parameters: - - name: proposal_status - description: |- - proposal_status defines the status of the proposals. - - - PROPOSAL_STATUS_UNSPECIFIED: PROPOSAL_STATUS_UNSPECIFIED defines the default proposal status. - - PROPOSAL_STATUS_DEPOSIT_PERIOD: PROPOSAL_STATUS_DEPOSIT_PERIOD defines a proposal status during the deposit - period. - - PROPOSAL_STATUS_VOTING_PERIOD: PROPOSAL_STATUS_VOTING_PERIOD defines a proposal status during the voting - period. - - PROPOSAL_STATUS_PASSED: PROPOSAL_STATUS_PASSED defines a proposal status of a proposal that has - passed. - - PROPOSAL_STATUS_REJECTED: PROPOSAL_STATUS_REJECTED defines a proposal status of a proposal that has - been rejected. - - PROPOSAL_STATUS_FAILED: PROPOSAL_STATUS_FAILED defines a proposal status of a proposal that has - failed. - in: query - required: false - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - - name: voter - description: voter defines the voter address for the proposals. - in: query - required: false - type: string - - name: depositor - description: depositor defines the deposit addresses from the proposals. - in: query - required: false + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true type: string - name: pagination.key description: |- @@ -12661,26 +13326,32 @@ paths: type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators: get: - summary: Proposal queries proposal details based on ProposalID. - operationId: GovV1Proposal + summary: |- + DelegatorValidators queries all validators info for given delegator + address. + description: >- + When called from another module, this query might consume a high amount of + + gas if the pagination field is incorrectly set. + operationId: StakingDelegatorValidators responses: '200': description: A successful response. schema: type: object properties: - proposal: - type: object - properties: - id: - type: string - format: uint64 - description: id defines the unique id of the proposal. - messages: - type: array - items: + validators: + type: array + items: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's operator; bech encoded in JSON. + consensus_pubkey: type: object properties: type_url: @@ -12829,107 +13500,143 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - description: >- - messages are the arbitrary messages to be executed if the proposal passes. - status: - description: status defines the proposal status. - type: string - enum: - - PROPOSAL_STATUS_UNSPECIFIED - - PROPOSAL_STATUS_DEPOSIT_PERIOD - - PROPOSAL_STATUS_VOTING_PERIOD - - PROPOSAL_STATUS_PASSED - - PROPOSAL_STATUS_REJECTED - - PROPOSAL_STATUS_FAILED - default: PROPOSAL_STATUS_UNSPECIFIED - final_tally_result: - description: >- - final_tally_result is the final tally result of the proposal. When - - querying a proposal via gRPC, this field is not populated until the - - proposal's voting period has ended. - type: object - properties: - yes_count: - type: string - description: yes_count is the number of yes votes on a proposal. - abstain_count: - type: string - description: >- - abstain_count is the number of abstain votes on a proposal. - no_count: - type: string - description: no_count is the number of no votes on a proposal. - no_with_veto_count: - type: string - description: >- - no_with_veto_count is the number of no with veto votes on a proposal. - submit_time: - type: string - format: date-time - description: submit_time is the time of proposal submission. - deposit_end_time: - type: string - format: date-time - description: deposit_end_time is the end time for deposition. - total_deposit: - type: array - items: + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed from bonded status or not. + status: + description: >- + status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's delegators. + description: + description: >- + description defines the description terms for the validator. type: object properties: - denom: + moniker: type: string - amount: + description: >- + moniker defines a human-readable name for the validator. + identity: type: string + description: >- + identity defines an optional identity signature (ex. UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 description: >- - Coin defines a token with a denomination and an amount. + unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was changed. + min_self_delegation: + type: string + description: >- + min_self_delegation is the validator's self declared minimum self delegation. - NOTE: The amount field is an Int which implements the custom method + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of this validator + description: >- + Validator defines a validator, together with the total amount of the - signatures required by gogoproto. - description: total_deposit is the total deposit on the proposal. - voting_start_time: - type: string - format: date-time - description: >- - voting_start_time is the starting time to vote on a proposal. - voting_end_time: - type: string - format: date-time - description: voting_end_time is the end time of voting on a proposal. - metadata: - type: string - title: >- - metadata is any arbitrary metadata attached to the proposal. + Validator's bond shares and their exchange rate to coins. Slashing results in - the recommended format of the metadata is to be found here: + a decrease in the exchange rate, allowing correct calculation of future - https://docs.cosmos.network/v0.47/modules/gov#proposal-3 - title: - type: string - description: 'Since: cosmos-sdk 0.47' - title: title is the title of the proposal - summary: - type: string - description: 'Since: cosmos-sdk 0.47' - title: summary is a short summary of the proposal - proposer: + undelegations without iterating over delegators. When coins are delegated to + + this validator, the validator is credited with a delegation whose number of + + bond shares is based on the amount of coins delegated divided by the current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: validators defines the validators' info of a delegator. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: type: string - description: 'Since: cosmos-sdk 0.47' - title: proposer is the address of the proposal sumbitter - expedited: - type: boolean - description: 'Since: cosmos-sdk 0.50' - title: expedited defines if the proposal is expedited - failed_reason: + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: type: string - description: 'Since: cosmos-sdk 0.50' - title: failed_reason defines the reason why the proposal failed - description: >- - Proposal defines the core field members of a governance proposal. - description: >- - QueryProposalResponse is the response type for the Query/Proposal RPC method. + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total + + was set, its value is undefined otherwise + description: |- + QueryDelegatorValidatorsResponse is response type for the + Query/DelegatorValidators RPC method. default: description: An unexpected error response. schema: @@ -13094,78 +13801,348 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. in: path required: true type: string + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/deposits: + /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}: get: - summary: Deposits queries all deposits of a single proposal. - operationId: GovV1Deposit + summary: |- + DelegatorValidator queries validator info for given delegator validator + pair. + operationId: DelegatorValidator responses: '200': description: A successful response. schema: type: object properties: - deposits: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - depositor: - type: string - description: >- - depositor defines the deposit addresses from the proposals. - amount: - type: array - items: + validator: + type: object + properties: + operator_address: + type: string + description: >- + operator_address defines the address of the validator's operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean + description: >- + jailed defined whether the validator has been jailed from bonded status or not. + status: + description: >- + status is the validator status (bonded/unbonding/unbonded). + type: string + enum: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: + type: string + description: >- + tokens define the delegated tokens (incl. self-delegation). + delegator_shares: + type: string + description: >- + delegator_shares defines total shares issued to a validator's delegators. + description: + description: >- + description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be used for creating a validator. type: object properties: - denom: + rate: type: string - amount: + description: >- + rate is the commission rate charged to delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator can ever charge, as a fraction. + max_change_rate: type: string + description: >- + max_change_rate defines the maximum daily increase of the validator commission, as a fraction. + update_time: + type: string + format: date-time description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: amount to be deposited by depositor. - description: >- - Deposit defines an amount deposited by an account address to an active - - proposal. - description: deposits defines the requested deposits. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: + update_time is the last time the commission rate was changed. + min_self_delegation: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + description: >- + min_self_delegation is the validator's self declared minimum self delegation. + + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: type: string - format: uint64 + format: int64 title: >- - total is total number of results available if PageRequest.count_total + strictly positive if this validator's unbonding has been stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of this validator + description: >- + Validator defines a validator, together with the total amount of the - was set, its value is undefined otherwise - description: >- - QueryDepositsResponse is the response type for the Query/Deposits RPC method. + Validator's bond shares and their exchange rate to coins. Slashing results in + + a decrease in the exchange rate, allowing correct calculation of future + + undelegations without iterating over delegators. When coins are delegated to + + this validator, the validator is credited with a delegation whose number of + + bond shares is based on the amount of coins delegated divided by the current + + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: |- + QueryDelegatorValidatorResponse response type for the + Query/DelegatorValidator RPC method. default: description: An unexpected error response. schema: @@ -13330,1517 +14307,384 @@ paths: "value": "1.212s" } parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. in: path required: true type: string - format: uint64 - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/gov/v1/proposals/{proposal_id}/deposits/{depositor}: + /cosmos/staking/v1beta1/historical_info/{height}: get: - summary: >- - Deposit queries single deposit information based on proposalID, depositAddr. - operationId: GovV1Deposit + summary: HistoricalInfo queries the historical info for given height. + operationId: HistoricalInfo responses: '200': description: A successful response. schema: type: object properties: - deposit: + hist: + description: hist defines the historical info at the given height. type: object properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - depositor: - type: string - description: >- - depositor defines the deposit addresses from the proposals. - amount: + header: + type: object + properties: + version: + title: basic block info + type: object + properties: + block: + type: string + format: uint64 + app: + type: string + format: uint64 + description: >- + Consensus captures the consensus rules for processing a block in the blockchain, + + including all blockchain data structures and the rules of the application's + + state transition machine. + chain_id: + type: string + height: + type: string + format: int64 + time: + type: string + format: date-time + last_block_id: + title: prev block info + type: object + properties: + hash: + type: string + format: byte + part_set_header: + type: object + properties: + total: + type: integer + format: int64 + hash: + type: string + format: byte + title: PartsetHeader + last_commit_hash: + type: string + format: byte + title: hashes of block data + data_hash: + type: string + format: byte + validators_hash: + type: string + format: byte + title: hashes from the app output from the prev block + next_validators_hash: + type: string + format: byte + consensus_hash: + type: string + format: byte + app_hash: + type: string + format: byte + last_results_hash: + type: string + format: byte + evidence_hash: + type: string + format: byte + title: consensus info + proposer_address: + type: string + format: byte + description: Header defines the structure of a block header. + valset: type: array items: type: object properties: - denom: - type: string - amount: + operator_address: type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: amount to be deposited by depositor. - description: >- - Deposit defines an amount deposited by an account address to an active - - proposal. - description: >- - QueryDepositResponse is the response type for the Query/Deposit RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } + description: >- + operator_address defines the address of the validator's operator; bech encoded in JSON. + consensus_pubkey: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized - Example 2: Pack and unpack a message in Java. + protocol buffer message. This string must contain at least - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } + one "/" character. The last segment of the URL's path must represent - Example 3: Pack and unpack a message in Python. + the fully qualified name of the type (as in - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... + `path/google.protobuf.Duration`). The name should be in a canonical form - Example 4: Pack and unpack a message in Go + (e.g., leading "." is not accepted). - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } + In practice, teams usually precompile into the binary all types that they - The pack methods provided by protobuf library will by default use + expect it to use in the context of Any. However, for URLs which use the - 'type.googleapis.com/full.type.name' as the type URL and the unpack + scheme `http`, `https`, or no scheme, one can optionally set up a type - methods only use the fully qualified type name after the last '/' + server that maps type URLs to message definitions as follows: - in the type URL, for example "foo.bar.com/x/y.z" will yield type + * If no scheme is provided, `https` is assumed. - name "y.z". + * An HTTP GET on the URL must yield a [google.protobuf.Type][] - JSON + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the - The JSON representation of an `Any` value uses the regular + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) - representation of the deserialized, embedded message, with an + Note: this functionality is not currently available in the official - additional field `@type` which contains the type URL. Example: + protobuf release, and it is not used for type URLs beginning with - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } + type.googleapis.com. - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } + Schemes other than `http`, `https` (or the empty scheme) might be - If the embedded message type is well-known and has a custom JSON + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a - representation, that representation will be embedded adding a field + URL that describes the type of the serialized message. - `value` which holds the custom JSON in addition to the `@type` + Protobuf library provides support to pack/unpack Any values in the form - field. Example (for message [google.protobuf.Duration][]): + of utility functions or additional generated methods of the Any type. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true - type: string - format: uint64 - - name: depositor - description: depositor defines the deposit addresses from the proposals. - in: path - required: true - type: string - tags: - - Query - /cosmos/gov/v1/proposals/{proposal_id}/tally: - get: - summary: TallyResult queries the tally of a proposal vote. - operationId: GovV1TallyResult - responses: - '200': - description: A successful response. - schema: - type: object - properties: - tally: - description: tally defines the requested tally. - type: object - properties: - yes_count: - type: string - description: yes_count is the number of yes votes on a proposal. - abstain_count: - type: string - description: >- - abstain_count is the number of abstain votes on a proposal. - no_count: - type: string - description: no_count is the number of no votes on a proposal. - no_with_veto_count: - type: string - description: >- - no_with_veto_count is the number of no with veto votes on a proposal. - description: >- - QueryTallyResultResponse is the response type for the Query/Tally RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized + Example 1: Pack and unpack a message in C++. - protocol buffer message. This string must contain at least + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } - one "/" character. The last segment of the URL's path must represent + Example 2: Pack and unpack a message in Java. - the fully qualified name of the type (as in + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } - `path/google.protobuf.Duration`). The name should be in a canonical form + Example 3: Pack and unpack a message in Python. - (e.g., leading "." is not accepted). + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... - In practice, teams usually precompile into the binary all types that they + Example 4: Pack and unpack a message in Go - expect it to use in the context of Any. However, for URLs which use the + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } - scheme `http`, `https`, or no scheme, one can optionally set up a type + The pack methods provided by protobuf library will by default use - server that maps type URLs to message definitions as follows: + 'type.googleapis.com/full.type.name' as the type URL and the unpack - * If no scheme is provided, `https` is assumed. + methods only use the fully qualified type name after the last '/' - * An HTTP GET on the URL must yield a [google.protobuf.Type][] + in the type URL, for example "foo.bar.com/x/y.z" will yield type - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the + name "y.z". - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) + JSON - Note: this functionality is not currently available in the official + The JSON representation of an `Any` value uses the regular - protobuf release, and it is not used for type URLs beginning with + representation of the deserialized, embedded message, with an - type.googleapis.com. + additional field `@type` which contains the type URL. Example: - Schemes other than `http`, `https` (or the empty scheme) might be + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } - URL that describes the type of the serialized message. + If the embedded message type is well-known and has a custom JSON - Protobuf library provides support to pack/unpack Any values in the form + representation, that representation will be embedded adding a field - of utility functions or additional generated methods of the Any type. + `value` which holds the custom JSON in addition to the `@type` - Example 1: Pack and unpack a message in C++. + field. Example (for message [google.protobuf.Duration][]): - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true - type: string - format: uint64 - tags: - - Query - /cosmos/gov/v1/proposals/{proposal_id}/votes: - get: - summary: Votes queries votes of a given proposal. - operationId: GovV1Votes - responses: - '200': - description: A successful response. - schema: - type: object - properties: - votes: - type: array - items: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - voter: - type: string - description: voter is the voter address of the proposal. - options: - type: array - items: - type: object - properties: - option: - description: >- - option defines the valid vote options, it must not contain duplicate vote options. - type: string - enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - weight: - type: string - description: >- - weight is the vote weight associated with the vote option. - description: >- - WeightedVoteOption defines a unit of vote for vote split. - description: options is the weighted vote options. - metadata: - type: string - title: >- - metadata is any arbitrary metadata attached to the vote. - - the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#vote-5 - description: >- - Vote defines a vote on a governance proposal. - - A Vote consists of a proposal ID, the voter, and the vote option. - description: votes defines the queried votes. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - QueryVotesResponse is the response type for the Query/Votes RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true - type: string - format: uint64 - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Query - /cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}: - get: - summary: Vote queries voted information based on proposalID, voterAddr. - operationId: GovV1Vote - responses: - '200': - description: A successful response. - schema: - type: object - properties: - vote: - type: object - properties: - proposal_id: - type: string - format: uint64 - description: proposal_id defines the unique id of the proposal. - voter: - type: string - description: voter is the voter address of the proposal. - options: - type: array - items: - type: object - properties: - option: + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + jailed: + type: boolean description: >- - option defines the valid vote options, it must not contain duplicate vote options. + jailed defined whether the validator has been jailed from bonded status or not. + status: + description: >- + status is the validator status (bonded/unbonding/unbonded). type: string enum: - - VOTE_OPTION_UNSPECIFIED - - VOTE_OPTION_YES - - VOTE_OPTION_ABSTAIN - - VOTE_OPTION_NO - - VOTE_OPTION_NO_WITH_VETO - default: VOTE_OPTION_UNSPECIFIED - weight: + - BOND_STATUS_UNSPECIFIED + - BOND_STATUS_UNBONDED + - BOND_STATUS_UNBONDING + - BOND_STATUS_BONDED + default: BOND_STATUS_UNSPECIFIED + tokens: type: string description: >- - weight is the vote weight associated with the vote option. - description: >- - WeightedVoteOption defines a unit of vote for vote split. - description: options is the weighted vote options. - metadata: - type: string - title: >- - metadata is any arbitrary metadata attached to the vote. - - the recommended format of the metadata is to be found here: https://docs.cosmos.network/v0.47/modules/gov#vote-5 - description: >- - Vote defines a vote on a governance proposal. - - A Vote consists of a proposal ID, the voter, and the vote option. - description: >- - QueryVoteResponse is the response type for the Query/Vote RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: proposal_id - description: proposal_id defines the unique id of the proposal. - in: path - required: true - type: string - format: uint64 - - name: voter - description: voter defines the voter address for the proposals. - in: path - required: true - type: string - tags: - - Query - /cosmos/params/v1beta1/params: - get: - summary: |- - Params queries a specific parameter of a module, given its subspace and - key. - operationId: Params - responses: - '200': - description: A successful response. - schema: - type: object - properties: - param: - description: param defines the queried parameter. - type: object - properties: - subspace: - type: string - key: - type: string - value: - type: string - description: >- - QueryParamsResponse is response type for the Query/Params RPC method. - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: subspace - description: subspace defines the module to query the parameter for. - in: query - required: false - type: string - - name: key - description: key defines the key of the parameter in the subspace. - in: query - required: false - type: string - tags: - - Query - /cosmos/params/v1beta1/subspaces: - get: - summary: >- - Subspaces queries for all registered subspaces and all keys for a subspace. - description: 'Since: cosmos-sdk 0.46' - operationId: Subspaces - responses: - '200': - description: A successful response. - schema: - type: object - properties: - subspaces: - type: array - items: - type: object - properties: - subspace: - type: string - keys: - type: array - items: - type: string - description: >- - Subspace defines a parameter subspace name and all the keys that exist for - - the subspace. - - Since: cosmos-sdk 0.46 - description: >- - QuerySubspacesResponse defines the response types for querying for all - - registered subspaces and all keys for a subspace. - - Since: cosmos-sdk 0.46 - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - tags: - - Query - /cosmos/slashing/v1beta1/params: - get: - summary: Params queries the parameters of slashing module - operationId: SlashingParams - responses: - '200': - description: A successful response. - schema: - type: object - properties: - params: - type: object - properties: - signed_blocks_window: - type: string - format: int64 - min_signed_per_window: - type: string - format: byte - downtime_jail_duration: - type: string - slash_fraction_double_sign: - type: string - format: byte - slash_fraction_downtime: - type: string - format: byte - description: >- - Params represents the parameters used for by the slashing module. - title: >- - QueryParamsResponse is the response type for the Query/Params RPC method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - tags: - - Query - /cosmos/slashing/v1beta1/signing_infos: - get: - summary: SigningInfos queries signing info of all validators - operationId: SigningInfos - responses: - '200': - description: A successful response. - schema: - type: object - properties: - info: - type: array - items: - type: object - properties: - address: - type: string - start_height: - type: string - format: int64 - title: >- - Height at which validator was first a candidate OR was un-jailed - index_offset: - type: string - format: int64 - description: >- - Index which is incremented every time a validator is bonded in a block and - - _may_ have signed a pre-commit or not. This in conjunction with the - - signed_blocks_window param determines the index in the missed block bitmap. - jailed_until: - type: string - format: date-time - description: >- - Timestamp until which the validator is jailed due to liveness downtime. - tombstoned: - type: boolean - description: >- - Whether or not a validator has been tombstoned (killed out of validator - - set). It is set once the validator commits an equivocation or for any other - - configured misbehavior. - missed_blocks_counter: - type: string - format: int64 - description: >- - A counter of missed (unsigned) blocks. It is used to avoid unnecessary - - reads in the missed block bitmap. - description: >- - ValidatorSigningInfo defines a validator's signing info for monitoring their - - liveness activity. - title: info is the signing info of all validators - pagination: - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - PageResponse is to be embedded in gRPC response messages where the - - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - title: >- - QuerySigningInfosResponse is the response type for the Query/SigningInfos RPC - - method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Query - /cosmos/slashing/v1beta1/signing_infos/{cons_address}: - get: - summary: SigningInfo queries the signing info of given cons address - operationId: SigningInfo - responses: - '200': - description: A successful response. - schema: - type: object - properties: - val_signing_info: - type: object - properties: - address: - type: string - start_height: - type: string - format: int64 - title: >- - Height at which validator was first a candidate OR was un-jailed - index_offset: - type: string - format: int64 - description: >- - Index which is incremented every time a validator is bonded in a block and - - _may_ have signed a pre-commit or not. This in conjunction with the - - signed_blocks_window param determines the index in the missed block bitmap. - jailed_until: - type: string - format: date-time - description: >- - Timestamp until which the validator is jailed due to liveness downtime. - tombstoned: - type: boolean - description: >- - Whether or not a validator has been tombstoned (killed out of validator - - set). It is set once the validator commits an equivocation or for any other - - configured misbehavior. - missed_blocks_counter: - type: string - format: int64 - description: >- - A counter of missed (unsigned) blocks. It is used to avoid unnecessary - - reads in the missed block bitmap. - description: >- - ValidatorSigningInfo defines a validator's signing info for monitoring their - - liveness activity. - title: >- - val_signing_info is the signing info of requested val cons address - title: >- - QuerySigningInfoResponse is the response type for the Query/SigningInfo RPC - - method - default: - description: An unexpected error response. - schema: - type: object - properties: - error: - type: string - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - properties: - type_url: - type: string - value: - type: string - format: byte - parameters: - - name: cons_address - description: cons_address is the address to query signing info of - in: path - required: true - type: string - tags: - - Query - /cosmos/staking/v1beta1/delegations/{delegator_addr}: - get: - summary: >- - DelegatorDelegations queries all delegations of a given delegator address. - description: >- - When called from another module, this query might consume a high amount of - - gas if the pagination field is incorrectly set. - operationId: DelegatorDelegations - responses: - '200': - description: A successful response. - schema: - type: object - properties: - delegation_responses: - type: array - items: - type: object - properties: - delegation: - type: object - properties: - delegator_address: + tokens define the delegated tokens (incl. self-delegation). + delegator_shares: type: string description: >- - delegator_address is the encoded address of the delegator. - validator_address: + delegator_shares defines total shares issued to a validator's delegators. + description: + description: >- + description defines the description terms for the validator. + type: object + properties: + moniker: + type: string + description: >- + moniker defines a human-readable name for the validator. + identity: + type: string + description: >- + identity defines an optional identity signature (ex. UPort or Keybase). + website: + type: string + description: website defines an optional website link. + security_contact: + type: string + description: >- + security_contact defines an optional email for security contact. + details: + type: string + description: details define other optional details. + unbonding_height: + type: string + format: int64 + description: >- + unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. + unbonding_time: + type: string + format: date-time + description: >- + unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. + commission: + description: commission defines the commission parameters. + type: object + properties: + commission_rates: + description: >- + commission_rates defines the initial commission rates to be used for creating a validator. + type: object + properties: + rate: + type: string + description: >- + rate is the commission rate charged to delegators, as a fraction. + max_rate: + type: string + description: >- + max_rate defines the maximum commission rate which validator can ever charge, as a fraction. + max_change_rate: + type: string + description: >- + max_change_rate defines the maximum daily increase of the validator commission, as a fraction. + update_time: + type: string + format: date-time + description: >- + update_time is the last time the commission rate was changed. + min_self_delegation: type: string description: >- - validator_address is the encoded address of the validator. - shares: + min_self_delegation is the validator's self declared minimum self delegation. + + Since: cosmos-sdk 0.46 + unbonding_on_hold_ref_count: type: string - description: shares define the delegation shares received. + format: int64 + title: >- + strictly positive if this validator's unbonding has been stopped by external modules + unbonding_ids: + type: array + items: + type: string + format: uint64 + title: >- + list of unbonding ids, each uniquely identifing an unbonding of this validator description: >- - Delegation represents the bond with tokens held by an account. It is + Validator defines a validator, together with the total amount of the - owned by one delegator, and is associated with the voting power of one + Validator's bond shares and their exchange rate to coins. Slashing results in - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. + a decrease in the exchange rate, allowing correct calculation of future - NOTE: The amount field is an Int which implements the custom method + undelegations without iterating over delegators. When coins are delegated to - signatures required by gogoproto. - description: >- - DelegationResponse is equivalent to Delegation except that it contains a + this validator, the validator is credited with a delegation whose number of - balance in addition to shares which is more suitable for client responses. - description: >- - delegation_responses defines all the delegations' info of a delegator. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + bond shares is based on the amount of coins delegated divided by the current - was set, its value is undefined otherwise - description: |- - QueryDelegatorDelegationsResponse is response type for the - Query/DelegatorDelegations RPC method. + exchange rate. Voting power can be calculated as total bonded shares + + multiplied by exchange rate. + description: >- + QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC + + method. default: description: An unexpected error response. schema: @@ -15005,210 +14849,54 @@ paths: "value": "1.212s" } parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. + - name: height + description: height defines at which height to query the historical info. in: path required: true type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean + format: int64 tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/redelegations: + /cosmos/staking/v1beta1/params: get: - summary: Redelegations queries redelegations of given address. - description: >- - When called from another module, this query might consume a high amount of - - gas if the pagination field is incorrectly set. - operationId: Redelegations + summary: Parameters queries the staking parameters. + operationId: StakingParams responses: '200': description: A successful response. schema: type: object properties: - redelegation_responses: - type: array - items: - type: object - properties: - redelegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the bech32-encoded address of the delegator. - validator_src_address: - type: string - description: >- - validator_src_address is the validator redelegation source operator address. - validator_dst_address: - type: string - description: >- - validator_dst_address is the validator redelegation destination operator address. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height defines the height which the redelegation took place. - completion_time: - type: string - format: date-time - description: >- - completion_time defines the unix time for redelegation completion. - initial_balance: - type: string - description: >- - initial_balance defines the initial balance when redelegation started. - shares_dst: - type: string - description: >- - shares_dst is the amount of destination-validator shares created by redelegation. - unbonding_id: - type: string - format: uint64 - title: >- - Incrementing id that uniquely identifies this entry - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - Strictly positive if this entry's unbonding has been stopped by external modules - description: >- - RedelegationEntry defines a redelegation object with relevant metadata. - description: entries are the redelegation entries. - description: >- - Redelegation contains the list of a particular delegator's redelegating bonds - - from a particular source validator to a particular destination validator. - entries: - type: array - items: - type: object - properties: - redelegation_entry: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height defines the height which the redelegation took place. - completion_time: - type: string - format: date-time - description: >- - completion_time defines the unix time for redelegation completion. - initial_balance: - type: string - description: >- - initial_balance defines the initial balance when redelegation started. - shares_dst: - type: string - description: >- - shares_dst is the amount of destination-validator shares created by redelegation. - unbonding_id: - type: string - format: uint64 - title: >- - Incrementing id that uniquely identifies this entry - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - Strictly positive if this entry's unbonding has been stopped by external modules - description: >- - RedelegationEntry defines a redelegation object with relevant metadata. - balance: - type: string - description: >- - RedelegationEntryResponse is equivalent to a RedelegationEntry except that it - - contains a balance in addition to shares which is more suitable for client - - responses. - description: >- - RedelegationResponse is equivalent to a Redelegation except that its entries - - contain a balance in addition to shares which is more suitable for client - - responses. - pagination: - description: pagination defines the pagination in the response. + params: + description: params holds all the parameters of this module. type: object properties: - next_key: + unbonding_time: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + description: unbonding_time is the time duration of unbonding. + max_validators: + type: integer + format: int64 + description: max_validators is the maximum number of validators. + max_entries: + type: integer + format: int64 + description: >- + max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). + historical_entries: + type: integer + format: int64 + description: >- + historical_entries is the number of historical entries to persist. + bond_denom: + type: string + description: bond_denom defines the bondable coin denomination. + min_commission_rate: type: string - format: uint64 title: >- - total is total number of results available if PageRequest.count_total - - was set, its value is undefined otherwise + min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators description: >- - QueryRedelegationsResponse is response type for the Query/Redelegations RPC - - method. + QueryParamsResponse is response type for the Query/Params RPC method. default: description: An unexpected error response. schema: @@ -15372,165 +15060,27 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path - required: true - type: string - - name: src_validator_addr - description: src_validator_addr defines the validator address to redelegate from. - in: query - required: false - type: string - - name: dst_validator_addr - description: dst_validator_addr defines the validator address to redelegate to. - in: query - required: false - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/unbonding_delegations: + /cosmos/staking/v1beta1/pool: get: - summary: >- - DelegatorUnbondingDelegations queries all unbonding delegations of a given - - delegator address. - description: >- - When called from another module, this query might consume a high amount of - - gas if the pagination field is incorrectly set. - operationId: DelegatorUnbondingDelegations + summary: Pool queries the pool info. + operationId: Pool responses: '200': description: A successful response. schema: type: object properties: - unbonding_responses: - type: array - items: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the encoded address of the delegator. - validator_address: - type: string - description: >- - validator_address is the encoded address of the validator. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding took place. - completion_time: - type: string - format: date-time - description: >- - completion_time is the unix time for unbonding completion. - initial_balance: - type: string - description: >- - initial_balance defines the tokens initially scheduled to receive at completion. - balance: - type: string - description: >- - balance defines the tokens to receive at completion. - unbonding_id: - type: string - format: uint64 - title: >- - Incrementing id that uniquely identifies this entry - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - Strictly positive if this entry's unbonding has been stopped by external modules - description: >- - UnbondingDelegationEntry defines an unbonding object with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's unbonding bonds - - for a single validator in an time-ordered list. - pagination: - description: pagination defines the pagination in the response. + pool: + description: pool defines the pool info. type: object properties: - next_key: + not_bonded_tokens: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + bonded_tokens: type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total - - was set, its value is undefined otherwise - description: >- - QueryUnbondingDelegatorDelegationsResponse is response type for the - - Query/UnbondingDelegatorDelegations RPC method. + description: QueryPoolResponse is response type for the Query/Pool RPC method. default: description: An unexpected error response. schema: @@ -15694,73 +15244,16 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path - required: true - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators: + /cosmos/staking/v1beta1/validators: get: - summary: |- - DelegatorValidators queries all validators info for given delegator - address. + summary: Validators queries all validators that match the given status. description: >- When called from another module, this query might consume a high amount of gas if the pagination field is incorrectly set. - operationId: StakingDelegatorValidators + operationId: Validators responses: '200': description: A successful response. @@ -16040,7 +15533,7 @@ paths: exchange rate. Voting power can be calculated as total bonded shares multiplied by exchange rate. - description: validators defines the validators' info of a delegator. + description: validators contains all the queried validators. pagination: description: pagination defines the pagination in the response. type: object @@ -16059,9 +15552,8 @@ paths: total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise - description: |- - QueryDelegatorValidatorsResponse is response type for the - Query/DelegatorValidators RPC method. + title: >- + QueryValidatorsResponse is response type for the Query/Validators RPC method default: description: An unexpected error response. schema: @@ -16226,10 +15718,10 @@ paths: "value": "1.212s" } parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path - required: true + - name: status + description: status enables to query for validators matching a given status. + in: query + required: false type: string - name: pagination.key description: |- @@ -16282,12 +15774,10 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/delegators/{delegator_addr}/validators/{validator_addr}: + /cosmos/staking/v1beta1/validators/{validator_addr}: get: - summary: |- - DelegatorValidator queries validator info for given delegator validator - pair. - operationId: DelegatorValidator + summary: Validator queries validator info for given validator address. + operationId: Validator responses: '200': description: A successful response. @@ -16565,9 +16055,8 @@ paths: exchange rate. Voting power can be calculated as total bonded shares multiplied by exchange rate. - description: |- - QueryDelegatorValidatorResponse response type for the - Query/DelegatorValidator RPC method. + title: >- + QueryValidatorResponse is response type for the Query/Validator RPC method default: description: An unexpected error response. schema: @@ -16732,11 +16221,6 @@ paths: "value": "1.212s" } parameters: - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path - required: true - type: string - name: validator_addr description: validator_addr defines the validator address to query for. in: path @@ -16744,372 +16228,83 @@ paths: type: string tags: - Query - /cosmos/staking/v1beta1/historical_info/{height}: + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations: get: - summary: HistoricalInfo queries the historical info for given height. - operationId: HistoricalInfo + summary: ValidatorDelegations queries delegate info for given validator. + description: >- + When called from another module, this query might consume a high amount of + + gas if the pagination field is incorrectly set. + operationId: ValidatorDelegations responses: '200': description: A successful response. schema: type: object properties: - hist: - description: hist defines the historical info at the given height. - type: object - properties: - header: - type: object - properties: - version: - title: basic block info - type: object - properties: - block: - type: string - format: uint64 - app: - type: string - format: uint64 - description: >- - Consensus captures the consensus rules for processing a block in the blockchain, - - including all blockchain data structures and the rules of the application's - - state transition machine. - chain_id: - type: string - height: - type: string - format: int64 - time: - type: string - format: date-time - last_block_id: - title: prev block info - type: object - properties: - hash: - type: string - format: byte - part_set_header: - type: object - properties: - total: - type: integer - format: int64 - hash: - type: string - format: byte - title: PartsetHeader - last_commit_hash: - type: string - format: byte - title: hashes of block data - data_hash: - type: string - format: byte - validators_hash: - type: string - format: byte - title: hashes from the app output from the prev block - next_validators_hash: - type: string - format: byte - consensus_hash: - type: string - format: byte - app_hash: - type: string - format: byte - last_results_hash: - type: string - format: byte - evidence_hash: - type: string - format: byte - title: consensus info - proposer_address: - type: string - format: byte - description: Header defines the structure of a block header. - valset: - type: array - items: + delegation_responses: + type: array + items: + type: object + properties: + delegation: type: object properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed from bonded status or not. - status: - description: >- - status is the validator status (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a validator's delegators. - description: - description: >- - description defines the description terms for the validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. - unbonding_time: + delegator_address: type: string - format: date-time description: >- - unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission rates to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to delegators, as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate which validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily increase of the validator commission, as a fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate was changed. - min_self_delegation: + delegator_address is the encoded address of the delegator. + validator_address: type: string description: >- - min_self_delegation is the validator's self declared minimum self delegation. - - Since: cosmos-sdk 0.46 - unbonding_on_hold_ref_count: + validator_address is the encoded address of the validator. + shares: type: string - format: int64 - title: >- - strictly positive if this validator's unbonding has been stopped by external modules - unbonding_ids: - type: array - items: - type: string - format: uint64 - title: >- - list of unbonding ids, each uniquely identifing an unbonding of this validator + description: shares define the delegation shares received. description: >- - Validator defines a validator, together with the total amount of the - - Validator's bond shares and their exchange rate to coins. Slashing results in - - a decrease in the exchange rate, allowing correct calculation of future + Delegation represents the bond with tokens held by an account. It is - undelegations without iterating over delegators. When coins are delegated to + owned by one delegator, and is associated with the voting power of one - this validator, the validator is credited with a delegation whose number of + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string + description: >- + Coin defines a token with a denomination and an amount. - bond shares is based on the amount of coins delegated divided by the current + NOTE: The amount field is an Int which implements the custom method - exchange rate. Voting power can be calculated as total bonded shares + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it contains a - multiplied by exchange rate. - description: >- - QueryHistoricalInfoResponse is response type for the Query/HistoricalInfo RPC + balance in addition to shares which is more suitable for client responses. + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. + total: + type: string + format: uint64 + title: >- + total is total number of results available if PageRequest.count_total - method. + was set, its value is undefined otherwise + title: |- + QueryValidatorDelegationsResponse is response type for the + Query/ValidatorDelegations RPC method default: description: An unexpected error response. schema: @@ -17274,54 +16469,114 @@ paths: "value": "1.212s" } parameters: - - name: height - description: height defines at which height to query the historical info. + - name: validator_addr + description: validator_addr defines the validator address to query for. in: path required: true type: string - format: int64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Query - /cosmos/staking/v1beta1/params: + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}: get: - summary: Parameters queries the staking parameters. - operationId: StakingParams + summary: Delegation queries delegate info for given validator delegator pair. + operationId: Delegation responses: '200': description: A successful response. schema: type: object properties: - params: - description: params holds all the parameters of this module. + delegation_response: type: object properties: - unbonding_time: - type: string - description: unbonding_time is the time duration of unbonding. - max_validators: - type: integer - format: int64 - description: max_validators is the maximum number of validators. - max_entries: - type: integer - format: int64 + delegation: + type: object + properties: + delegator_address: + type: string + description: >- + delegator_address is the encoded address of the delegator. + validator_address: + type: string + description: >- + validator_address is the encoded address of the validator. + shares: + type: string + description: shares define the delegation shares received. description: >- - max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). - historical_entries: - type: integer - format: int64 + Delegation represents the bond with tokens held by an account. It is + + owned by one delegator, and is associated with the voting power of one + + validator. + balance: + type: object + properties: + denom: + type: string + amount: + type: string description: >- - historical_entries is the number of historical entries to persist. - bond_denom: - type: string - description: bond_denom defines the bondable coin denomination. - min_commission_rate: - type: string - title: >- - min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators + Coin defines a token with a denomination and an amount. + + NOTE: The amount field is an Int which implements the custom method + + signatures required by gogoproto. + description: >- + DelegationResponse is equivalent to Delegation except that it contains a + + balance in addition to shares which is more suitable for client responses. description: >- - QueryParamsResponse is response type for the Query/Params RPC method. + QueryDelegationResponse is response type for the Query/Delegation RPC method. default: description: An unexpected error response. schema: @@ -17485,27 +16740,82 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string tags: - Query - /cosmos/staking/v1beta1/pool: + /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation: get: - summary: Pool queries the pool info. - operationId: Pool + summary: |- + UnbondingDelegation queries unbonding info for given validator delegator + pair. + operationId: UnbondingDelegation responses: '200': description: A successful response. schema: type: object properties: - pool: - description: pool defines the pool info. + unbond: type: object properties: - not_bonded_tokens: + delegator_address: type: string - bonded_tokens: + description: delegator_address is the encoded address of the delegator. + validator_address: type: string - description: QueryPoolResponse is response type for the Query/Pool RPC method. + description: validator_address is the encoded address of the validator. + entries: + type: array + items: + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to receive at completion. + balance: + type: string + description: balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by external modules + description: >- + UnbondingDelegationEntry defines an unbonding object with relevant metadata. + description: entries are the unbonding delegation entries. + description: >- + UnbondingDelegation stores all of a single delegator's unbonding bonds + + for a single validator in an time-ordered list. + description: >- + QueryDelegationResponse is response type for the Query/UnbondingDelegation + + RPC method. default: description: An unexpected error response. schema: @@ -17669,296 +16979,87 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + parameters: + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true + type: string + - name: delegator_addr + description: delegator_addr defines the delegator address to query for. + in: path + required: true + type: string tags: - Query - /cosmos/staking/v1beta1/validators: + /cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations: get: - summary: Validators queries all validators that match the given status. + summary: >- + ValidatorUnbondingDelegations queries unbonding delegations of a validator. description: >- When called from another module, this query might consume a high amount of gas if the pagination field is incorrectly set. - operationId: Validators + operationId: ValidatorUnbondingDelegations responses: '200': description: A successful response. schema: type: object properties: - validators: + unbonding_responses: type: array items: type: object properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed from bonded status or not. - status: - description: >- - status is the validator status (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a validator's delegators. - description: - description: >- - description defines the description terms for the validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. - unbonding_time: + delegator_address: type: string - format: date-time description: >- - unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission rates to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to delegators, as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate which validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily increase of the validator commission, as a fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate was changed. - min_self_delegation: + delegator_address is the encoded address of the delegator. + validator_address: type: string description: >- - min_self_delegation is the validator's self declared minimum self delegation. - - Since: cosmos-sdk 0.46 - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - strictly positive if this validator's unbonding has been stopped by external modules - unbonding_ids: + validator_address is the encoded address of the validator. + entries: type: array items: - type: string - format: uint64 - title: >- - list of unbonding ids, each uniquely identifing an unbonding of this validator + type: object + properties: + creation_height: + type: string + format: int64 + description: >- + creation_height is the height which the unbonding took place. + completion_time: + type: string + format: date-time + description: >- + completion_time is the unix time for unbonding completion. + initial_balance: + type: string + description: >- + initial_balance defines the tokens initially scheduled to receive at completion. + balance: + type: string + description: >- + balance defines the tokens to receive at completion. + unbonding_id: + type: string + format: uint64 + title: >- + Incrementing id that uniquely identifies this entry + unbonding_on_hold_ref_count: + type: string + format: int64 + title: >- + Strictly positive if this entry's unbonding has been stopped by external modules + description: >- + UnbondingDelegationEntry defines an unbonding object with relevant metadata. + description: entries are the unbonding delegation entries. description: >- - Validator defines a validator, together with the total amount of the - - Validator's bond shares and their exchange rate to coins. Slashing results in - - a decrease in the exchange rate, allowing correct calculation of future - - undelegations without iterating over delegators. When coins are delegated to - - this validator, the validator is credited with a delegation whose number of - - bond shares is based on the amount of coins delegated divided by the current - - exchange rate. Voting power can be calculated as total bonded shares + UnbondingDelegation stores all of a single delegator's unbonding bonds - multiplied by exchange rate. - description: validators contains all the queried validators. + for a single validator in an time-ordered list. pagination: description: pagination defines the pagination in the response. type: object @@ -17977,8 +17078,10 @@ paths: total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise - title: >- - QueryValidatorsResponse is response type for the Query/Validators RPC method + description: >- + QueryValidatorUnbondingDelegationsResponse is response type for the + + Query/ValidatorUnbondingDelegations RPC method. default: description: An unexpected error response. schema: @@ -18143,10 +17246,10 @@ paths: "value": "1.212s" } parameters: - - name: status - description: status enables to query for validators matching a given status. - in: query - required: false + - name: validator_addr + description: validator_addr defines the validator address to query for. + in: path + required: true type: string - name: pagination.key description: |- @@ -18199,289 +17302,16 @@ paths: type: boolean tags: - Query - /cosmos/staking/v1beta1/validators/{validator_addr}: - get: - summary: Validator queries validator info for given validator address. - operationId: Validator + /cosmos/tx/v1beta1/decode: + post: + summary: TxDecode decodes the transaction. + description: 'Since: cosmos-sdk 0.47' + operationId: TxDecode responses: '200': description: A successful response. schema: - type: object - properties: - validator: - type: object - properties: - operator_address: - type: string - description: >- - operator_address defines the address of the validator's operator; bech encoded in JSON. - consensus_pubkey: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - jailed: - type: boolean - description: >- - jailed defined whether the validator has been jailed from bonded status or not. - status: - description: >- - status is the validator status (bonded/unbonding/unbonded). - type: string - enum: - - BOND_STATUS_UNSPECIFIED - - BOND_STATUS_UNBONDED - - BOND_STATUS_UNBONDING - - BOND_STATUS_BONDED - default: BOND_STATUS_UNSPECIFIED - tokens: - type: string - description: >- - tokens define the delegated tokens (incl. self-delegation). - delegator_shares: - type: string - description: >- - delegator_shares defines total shares issued to a validator's delegators. - description: - description: >- - description defines the description terms for the validator. - type: object - properties: - moniker: - type: string - description: >- - moniker defines a human-readable name for the validator. - identity: - type: string - description: >- - identity defines an optional identity signature (ex. UPort or Keybase). - website: - type: string - description: website defines an optional website link. - security_contact: - type: string - description: >- - security_contact defines an optional email for security contact. - details: - type: string - description: details define other optional details. - unbonding_height: - type: string - format: int64 - description: >- - unbonding_height defines, if unbonding, the height at which this validator has begun unbonding. - unbonding_time: - type: string - format: date-time - description: >- - unbonding_time defines, if unbonding, the min time for the validator to complete unbonding. - commission: - description: commission defines the commission parameters. - type: object - properties: - commission_rates: - description: >- - commission_rates defines the initial commission rates to be used for creating a validator. - type: object - properties: - rate: - type: string - description: >- - rate is the commission rate charged to delegators, as a fraction. - max_rate: - type: string - description: >- - max_rate defines the maximum commission rate which validator can ever charge, as a fraction. - max_change_rate: - type: string - description: >- - max_change_rate defines the maximum daily increase of the validator commission, as a fraction. - update_time: - type: string - format: date-time - description: >- - update_time is the last time the commission rate was changed. - min_self_delegation: - type: string - description: >- - min_self_delegation is the validator's self declared minimum self delegation. - - Since: cosmos-sdk 0.46 - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - strictly positive if this validator's unbonding has been stopped by external modules - unbonding_ids: - type: array - items: - type: string - format: uint64 - title: >- - list of unbonding ids, each uniquely identifing an unbonding of this validator - description: >- - Validator defines a validator, together with the total amount of the - - Validator's bond shares and their exchange rate to coins. Slashing results in - - a decrease in the exchange rate, allowing correct calculation of future - - undelegations without iterating over delegators. When coins are delegated to - - this validator, the validator is credited with a delegation whose number of - - bond shares is based on the amount of coins delegated divided by the current - - exchange rate. Voting power can be calculated as total bonded shares - - multiplied by exchange rate. - title: >- - QueryValidatorResponse is response type for the Query/Validator RPC method + $ref: '#/definitions/cosmos.tx.v1beta1.TxDecodeResponse' default: description: An unexpected error response. schema: @@ -18646,90 +17476,42 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path + - name: body + in: body required: true - type: string - tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations: - get: - summary: ValidatorDelegations queries delegate info for given validator. - description: >- - When called from another module, this query might consume a high amount of + schema: + type: object + properties: + tx_bytes: + type: string + format: byte + description: tx_bytes is the raw transaction. + description: |- + TxDecodeRequest is the request type for the Service.TxDecode + RPC method. - gas if the pagination field is incorrectly set. - operationId: ValidatorDelegations + Since: cosmos-sdk 0.47 + tags: + - Service + /cosmos/tx/v1beta1/decode/amino: + post: + summary: TxDecodeAmino decodes an Amino transaction from encoded bytes to JSON. + description: 'Since: cosmos-sdk 0.47' + operationId: TxDecodeAmino responses: '200': description: A successful response. schema: type: object properties: - delegation_responses: - type: array - items: - type: object - properties: - delegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the encoded address of the delegator. - validator_address: - type: string - description: >- - validator_address is the encoded address of the validator. - shares: - type: string - description: shares define the delegation shares received. - description: >- - Delegation represents the bond with tokens held by an account. It is - - owned by one delegator, and is associated with the voting power of one - - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: >- - DelegationResponse is equivalent to Delegation except that it contains a + amino_json: + type: string + description: >- + TxDecodeAminoResponse is the response type for the Service.TxDecodeAmino - balance in addition to shares which is more suitable for client responses. - pagination: - description: pagination defines the pagination in the response. - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + RPC method. - was set, its value is undefined otherwise - title: |- - QueryValidatorDelegationsResponse is response type for the - Query/ValidatorDelegations RPC method + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -18894,114 +17676,43 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path + - name: body + in: body required: true - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. + schema: + type: object + properties: + amino_binary: + type: string + format: byte + description: >- + TxDecodeAminoRequest is the request type for the Service.TxDecodeAmino - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}: - get: - summary: Delegation queries delegate info for given validator delegator pair. - operationId: Delegation + RPC method. + + Since: cosmos-sdk 0.47 + tags: + - Service + /cosmos/tx/v1beta1/encode: + post: + summary: TxEncode encodes the transaction. + description: 'Since: cosmos-sdk 0.47' + operationId: TxEncode responses: '200': description: A successful response. schema: type: object properties: - delegation_response: - type: object - properties: - delegation: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the encoded address of the delegator. - validator_address: - type: string - description: >- - validator_address is the encoded address of the validator. - shares: - type: string - description: shares define the delegation shares received. - description: >- - Delegation represents the bond with tokens held by an account. It is - - owned by one delegator, and is associated with the voting power of one - - validator. - balance: - type: object - properties: - denom: - type: string - amount: - type: string - description: >- - Coin defines a token with a denomination and an amount. - - NOTE: The amount field is an Int which implements the custom method - - signatures required by gogoproto. - description: >- - DelegationResponse is equivalent to Delegation except that it contains a + tx_bytes: + type: string + format: byte + description: tx_bytes is the encoded transaction bytes. + description: |- + TxEncodeResponse is the response type for the + Service.TxEncode method. - balance in addition to shares which is more suitable for client responses. - description: >- - QueryDelegationResponse is response type for the Query/Delegation RPC method. + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -19166,81 +17877,33 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path - required: true - type: string - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path + - name: body + in: body required: true - type: string + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.TxEncodeRequest' tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/delegations/{delegator_addr}/unbonding_delegation: - get: - summary: |- - UnbondingDelegation queries unbonding info for given validator delegator - pair. - operationId: UnbondingDelegation + - Service + /cosmos/tx/v1beta1/encode/amino: + post: + summary: TxEncodeAmino encodes an Amino transaction from JSON to encoded bytes. + description: 'Since: cosmos-sdk 0.47' + operationId: TxEncodeAmino responses: '200': description: A successful response. schema: type: object properties: - unbond: - type: object - properties: - delegator_address: - type: string - description: delegator_address is the encoded address of the delegator. - validator_address: - type: string - description: validator_address is the encoded address of the validator. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding took place. - completion_time: - type: string - format: date-time - description: >- - completion_time is the unix time for unbonding completion. - initial_balance: - type: string - description: >- - initial_balance defines the tokens initially scheduled to receive at completion. - balance: - type: string - description: balance defines the tokens to receive at completion. - unbonding_id: - type: string - format: uint64 - title: Incrementing id that uniquely identifies this entry - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - Strictly positive if this entry's unbonding has been stopped by external modules - description: >- - UnbondingDelegationEntry defines an unbonding object with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's unbonding bonds - - for a single validator in an time-ordered list. + amino_binary: + type: string + format: byte description: >- - QueryDelegationResponse is response type for the Query/UnbondingDelegation + TxEncodeAminoResponse is the response type for the Service.TxEncodeAmino RPC method. + + Since: cosmos-sdk 0.47 default: description: An unexpected error response. schema: @@ -19405,108 +18068,252 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path - required: true - type: string - - name: delegator_addr - description: delegator_addr defines the delegator address to query for. - in: path + - name: body + in: body required: true - type: string - tags: - - Query - /cosmos/staking/v1beta1/validators/{validator_addr}/unbonding_delegations: - get: - summary: >- - ValidatorUnbondingDelegations queries unbonding delegations of a validator. - description: >- - When called from another module, this query might consume a high amount of + schema: + type: object + properties: + amino_json: + type: string + description: >- + TxEncodeAminoRequest is the request type for the Service.TxEncodeAmino - gas if the pagination field is incorrectly set. - operationId: ValidatorUnbondingDelegations + RPC method. + + Since: cosmos-sdk 0.47 + tags: + - Service + /cosmos/tx/v1beta1/simulate: + post: + summary: Simulate simulates executing a transaction for estimating gas usage. + operationId: Simulate responses: '200': description: A successful response. schema: type: object properties: - unbonding_responses: - type: array - items: - type: object - properties: - delegator_address: - type: string - description: >- - delegator_address is the encoded address of the delegator. - validator_address: - type: string - description: >- - validator_address is the encoded address of the validator. - entries: - type: array - items: - type: object - properties: - creation_height: - type: string - format: int64 - description: >- - creation_height is the height which the unbonding took place. - completion_time: - type: string - format: date-time - description: >- - completion_time is the unix time for unbonding completion. - initial_balance: - type: string - description: >- - initial_balance defines the tokens initially scheduled to receive at completion. - balance: - type: string - description: >- - balance defines the tokens to receive at completion. - unbonding_id: - type: string - format: uint64 - title: >- - Incrementing id that uniquely identifies this entry - unbonding_on_hold_ref_count: - type: string - format: int64 - title: >- - Strictly positive if this entry's unbonding has been stopped by external modules - description: >- - UnbondingDelegationEntry defines an unbonding object with relevant metadata. - description: entries are the unbonding delegation entries. - description: >- - UnbondingDelegation stores all of a single delegator's unbonding bonds - - for a single validator in an time-ordered list. - pagination: - description: pagination defines the pagination in the response. + gas_info: + description: gas_info is the information about gas used in the simulation. type: object properties: - next_key: + gas_wanted: type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: + format: uint64 + description: >- + GasWanted is the maximum units of work we allow this tx to perform. + gas_used: type: string format: uint64 - title: >- - total is total number of results available if PageRequest.count_total + description: GasUsed is the amount of gas actually consumed. + result: + description: result is the result of the simulation. + type: object + properties: + data: + type: string + format: byte + description: >- + Data is any data returned from message or handler execution. It MUST be - was set, its value is undefined otherwise - description: >- - QueryValidatorUnbondingDelegationsResponse is response type for the + length prefixed in order to separate data from multiple message executions. - Query/ValidatorUnbondingDelegations RPC method. + Deprecated. This field is still populated, but prefer msg_response instead + + because it also contains the Msg response typeURL. + log: + type: string + description: >- + Log contains the log information from message or handler execution. + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + index: + type: boolean + description: >- + EventAttribute is a single key-value pair, associated with an event. + description: >- + Event allows application developers to attach additional information to + + ResponseFinalizeBlock and ResponseCheckTx. + + Later, transactions may be queried using these events. + description: >- + Events contains a slice of Event objects that were emitted during message + + or handler execution. + msg_responses: + type: array + items: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + description: >- + msg_responses contains the Msg handler responses type packed in Anys. + + Since: cosmos-sdk 0.46 + description: |- + SimulateResponse is the response type for the + Service.SimulateRPC method. default: description: An unexpected error response. schema: @@ -19671,72 +18478,22 @@ paths: "value": "1.212s" } parameters: - - name: validator_addr - description: validator_addr defines the validator address to query for. - in: path + - name: body + in: body required: true - type: string - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean + schema: + $ref: '#/definitions/cosmos.tx.v1beta1.SimulateRequest' tags: - - Query - /cosmos/tx/v1beta1/decode: - post: - summary: TxDecode decodes the transaction. - description: 'Since: cosmos-sdk 0.47' - operationId: TxDecode + - Service + /cosmos/tx/v1beta1/txs: + get: + summary: GetTxsEvent fetches txs by event. + operationId: GetTxsEvent responses: '200': description: A successful response. schema: - $ref: '#/definitions/cosmos.tx.v1beta1.TxDecodeResponse' + $ref: '#/definitions/cosmos.tx.v1beta1.GetTxsEventResponse' default: description: An unexpected error response. schema: @@ -19901,42 +18658,399 @@ paths: "value": "1.212s" } parameters: - - name: body - in: body - required: true - schema: - type: object - properties: - tx_bytes: - type: string - format: byte - description: tx_bytes is the raw transaction. - description: |- - TxDecodeRequest is the request type for the Service.TxDecode - RPC method. + - name: events + description: >- + events is the list of transaction event type. - Since: cosmos-sdk 0.47 + Deprecated post v0.47.x: use query instead, which should contain a valid + + events query. + in: query + required: false + type: array + items: + type: string + collectionFormat: multi + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include + + a count of the total number of items available for pagination in UIs. + + count_total is only respected when offset is used. It is ignored when key + + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. + + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean + - name: order_by + description: |2- + - ORDER_BY_UNSPECIFIED: ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults + to ASC in this case. + - ORDER_BY_ASC: ORDER_BY_ASC defines ascending order + - ORDER_BY_DESC: ORDER_BY_DESC defines descending order + in: query + required: false + type: string + enum: + - ORDER_BY_UNSPECIFIED + - ORDER_BY_ASC + - ORDER_BY_DESC + default: ORDER_BY_UNSPECIFIED + - name: page + description: |- + page is the page number to query, starts at 1. If not provided, will + default to first page. + in: query + required: false + type: string + format: uint64 + - name: limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: query + description: >- + query defines the transaction event query that is proxied to Tendermint's + + TxSearch RPC method. The query must be valid. + + Since cosmos-sdk 0.50 + in: query + required: false + type: string tags: - Service - /cosmos/tx/v1beta1/decode/amino: post: - summary: TxDecodeAmino decodes an Amino transaction from encoded bytes to JSON. - description: 'Since: cosmos-sdk 0.47' - operationId: TxDecodeAmino + summary: BroadcastTx broadcast transaction. + operationId: BroadcastTx responses: '200': description: A successful response. schema: type: object properties: - amino_json: - type: string - description: >- - TxDecodeAminoResponse is the response type for the Service.TxDecodeAmino + tx_response: + type: object + properties: + height: + type: string + format: int64 + title: The block height + txhash: + type: string + description: The transaction hash. + codespace: + type: string + title: Namespace for the Code + code: + type: integer + format: int64 + description: Response code. + data: + type: string + description: Result bytes, if any. + raw_log: + type: string + description: >- + The output of the application's logger (raw string). May be - RPC method. + non-deterministic. + logs: + type: array + items: + type: object + properties: + msg_index: + type: integer + format: int64 + log: + type: string + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + description: >- + Attribute defines an attribute wrapper where the key and value are - Since: cosmos-sdk 0.47 + strings instead of raw bytes. + description: >- + StringEvent defines en Event object wrapper where all the attributes + + contain key/value pairs that are strings instead of raw bytes. + description: >- + Events contains a slice of Event objects that were emitted during some + + execution. + description: >- + ABCIMessageLog defines a structure containing an indexed tx ABCI message log. + description: >- + The output of the application's logger (typed). May be non-deterministic. + info: + type: string + description: Additional information. May be non-deterministic. + gas_wanted: + type: string + format: int64 + description: Amount of gas requested for transaction. + gas_used: + type: string + format: int64 + description: Amount of gas consumed by transaction. + tx: + type: object + properties: + type_url: + type: string + description: >- + A URL/resource name that uniquely identifies the type of the serialized + + protocol buffer message. This string must contain at least + + one "/" character. The last segment of the URL's path must represent + + the fully qualified name of the type (as in + + `path/google.protobuf.Duration`). The name should be in a canonical form + + (e.g., leading "." is not accepted). + + In practice, teams usually precompile into the binary all types that they + + expect it to use in the context of Any. However, for URLs which use the + + scheme `http`, `https`, or no scheme, one can optionally set up a type + + server that maps type URLs to message definitions as follows: + + * If no scheme is provided, `https` is assumed. + + * An HTTP GET on the URL must yield a [google.protobuf.Type][] + + value in binary format, or produce an error. + * Applications are allowed to cache lookup results based on the + + URL, or have them precompiled into a binary to avoid any + lookup. Therefore, binary compatibility needs to be preserved + on changes to types. (Use versioned type names to manage + breaking changes.) + + Note: this functionality is not currently available in the official + + protobuf release, and it is not used for type URLs beginning with + + type.googleapis.com. + + Schemes other than `http`, `https` (or the empty scheme) might be + + used with implementation specific semantics. + value: + type: string + format: byte + description: >- + Must be a valid serialized protocol buffer of the above specified type. + description: >- + `Any` contains an arbitrary serialized protocol buffer message along with a + + URL that describes the type of the serialized message. + + Protobuf library provides support to pack/unpack Any values in the form + + of utility functions or additional generated methods of the Any type. + + Example 1: Pack and unpack a message in C++. + + Foo foo = ...; + Any any; + any.PackFrom(foo); + ... + if (any.UnpackTo(&foo)) { + ... + } + + Example 2: Pack and unpack a message in Java. + + Foo foo = ...; + Any any = Any.pack(foo); + ... + if (any.is(Foo.class)) { + foo = any.unpack(Foo.class); + } + // or ... + if (any.isSameTypeAs(Foo.getDefaultInstance())) { + foo = any.unpack(Foo.getDefaultInstance()); + } + + Example 3: Pack and unpack a message in Python. + + foo = Foo(...) + any = Any() + any.Pack(foo) + ... + if any.Is(Foo.DESCRIPTOR): + any.Unpack(foo) + ... + + Example 4: Pack and unpack a message in Go + + foo := &pb.Foo{...} + any, err := anypb.New(foo) + if err != nil { + ... + } + ... + foo := &pb.Foo{} + if err := any.UnmarshalTo(foo); err != nil { + ... + } + + The pack methods provided by protobuf library will by default use + + 'type.googleapis.com/full.type.name' as the type URL and the unpack + + methods only use the fully qualified type name after the last '/' + + in the type URL, for example "foo.bar.com/x/y.z" will yield type + + name "y.z". + + JSON + + The JSON representation of an `Any` value uses the regular + + representation of the deserialized, embedded message, with an + + additional field `@type` which contains the type URL. Example: + + package google.profile; + message Person { + string first_name = 1; + string last_name = 2; + } + + { + "@type": "type.googleapis.com/google.profile.Person", + "firstName": , + "lastName": + } + + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + timestamp: + type: string + description: >- + Time of the previous block. For heights > 1, it's the weighted median of + + the timestamps of the valid votes in the block.LastCommit. For height == 1, + + it's genesis time. + events: + type: array + items: + type: object + properties: + type: + type: string + attributes: + type: array + items: + type: object + properties: + key: + type: string + value: + type: string + index: + type: boolean + description: >- + EventAttribute is a single key-value pair, associated with an event. + description: >- + Event allows application developers to attach additional information to + + ResponseFinalizeBlock and ResponseCheckTx. + + Later, transactions may be queried using these events. + description: >- + Events defines all the events emitted by processing a transaction. Note, + + these events include those emitted by processing all the messages and those + + emitted from the ante. Whereas Logs contains the events, with + + additional metadata, emitted only by processing the messages. + + Since: cosmos-sdk 0.42.11, 0.44.5, 0.45 + description: >- + TxResponse defines a structure containing relevant tx data and metadata. The + + tags are stringified and the log is JSON decoded. + description: |- + BroadcastTxResponse is the response type for the + Service.BroadcastTx method. default: description: An unexpected error response. schema: @@ -20107,37 +19221,48 @@ paths: schema: type: object properties: - amino_binary: + tx_bytes: type: string format: byte + description: tx_bytes is the raw transaction. + mode: + type: string + enum: + - BROADCAST_MODE_UNSPECIFIED + - BROADCAST_MODE_BLOCK + - BROADCAST_MODE_SYNC + - BROADCAST_MODE_ASYNC + default: BROADCAST_MODE_UNSPECIFIED + description: >- + BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC + + method. + + - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering + - BROADCAST_MODE_BLOCK: DEPRECATED: use BROADCAST_MODE_SYNC instead, + BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x onwards. + + - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits + for a CheckTx execution response only. + + - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client + returns immediately. description: >- - TxDecodeAminoRequest is the request type for the Service.TxDecodeAmino + BroadcastTxRequest is the request type for the Service.BroadcastTxRequest RPC method. - - Since: cosmos-sdk 0.47 tags: - Service - /cosmos/tx/v1beta1/encode: - post: - summary: TxEncode encodes the transaction. - description: 'Since: cosmos-sdk 0.47' - operationId: TxEncode + /cosmos/tx/v1beta1/txs/block/{height}: + get: + summary: GetBlockWithTxs fetches a block with decoded txs. + description: 'Since: cosmos-sdk 0.45.2' + operationId: GetBlockWithTxs responses: '200': description: A successful response. schema: - type: object - properties: - tx_bytes: - type: string - format: byte - description: tx_bytes is the encoded transaction bytes. - description: |- - TxEncodeResponse is the response type for the - Service.TxEncode method. - - Since: cosmos-sdk 0.47 + $ref: '#/definitions/cosmos.tx.v1beta1.GetBlockWithTxsResponse' default: description: An unexpected error response. schema: @@ -20289,46 +19414,85 @@ paths: "lastName": } - If the embedded message type is well-known and has a custom JSON + If the embedded message type is well-known and has a custom JSON + + representation, that representation will be embedded adding a field + + `value` which holds the custom JSON in addition to the `@type` + + field. Example (for message [google.protobuf.Duration][]): + + { + "@type": "type.googleapis.com/google.protobuf.Duration", + "value": "1.212s" + } + parameters: + - name: height + description: height is the height of the block to query. + in: path + required: true + type: string + format: int64 + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should include - representation, that representation will be embedded adding a field + a count of the total number of items available for pagination in UIs. - `value` which holds the custom JSON in addition to the `@type` + count_total is only respected when offset is used. It is ignored when key - field. Example (for message [google.protobuf.Duration][]): + is set. + in: query + required: false + type: boolean + - name: pagination.reverse + description: >- + reverse is set to true if results are to be returned in the descending order. - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - parameters: - - name: body - in: body - required: true - schema: - $ref: '#/definitions/cosmos.tx.v1beta1.TxEncodeRequest' + Since: cosmos-sdk 0.43 + in: query + required: false + type: boolean tags: - Service - /cosmos/tx/v1beta1/encode/amino: - post: - summary: TxEncodeAmino encodes an Amino transaction from JSON to encoded bytes. - description: 'Since: cosmos-sdk 0.47' - operationId: TxEncodeAmino + /cosmos/tx/v1beta1/txs/{hash}: + get: + summary: GetTx fetches a tx by hash. + operationId: GetTx responses: '200': description: A successful response. schema: - type: object - properties: - amino_binary: - type: string - format: byte - description: >- - TxEncodeAminoResponse is the response type for the Service.TxEncodeAmino - - RPC method. - - Since: cosmos-sdk 0.47 + $ref: '#/definitions/cosmos.tx.v1beta1.GetTxResponse' default: description: An unexpected error response. schema: @@ -20493,252 +19657,31 @@ paths: "value": "1.212s" } parameters: - - name: body - in: body + - name: hash + description: hash is the tx hash to query, encoded as a hex string. + in: path required: true - schema: - type: object - properties: - amino_json: - type: string - description: >- - TxEncodeAminoRequest is the request type for the Service.TxEncodeAmino - - RPC method. - - Since: cosmos-sdk 0.47 + type: string tags: - Service - /cosmos/tx/v1beta1/simulate: - post: - summary: Simulate simulates executing a transaction for estimating gas usage. - operationId: Simulate + /cosmos/upgrade/v1beta1/applied_plan/{name}: + get: + summary: AppliedPlan queries a previously applied upgrade plan by its name. + operationId: AppliedPlan responses: '200': description: A successful response. schema: type: object properties: - gas_info: - description: gas_info is the information about gas used in the simulation. - type: object - properties: - gas_wanted: - type: string - format: uint64 - description: >- - GasWanted is the maximum units of work we allow this tx to perform. - gas_used: - type: string - format: uint64 - description: GasUsed is the amount of gas actually consumed. - result: - description: result is the result of the simulation. - type: object - properties: - data: - type: string - format: byte - description: >- - Data is any data returned from message or handler execution. It MUST be - - length prefixed in order to separate data from multiple message executions. - - Deprecated. This field is still populated, but prefer msg_response instead - - because it also contains the Msg response typeURL. - log: - type: string - description: >- - Log contains the log information from message or handler execution. - events: - type: array - items: - type: object - properties: - type: - type: string - attributes: - type: array - items: - type: object - properties: - key: - type: string - value: - type: string - index: - type: boolean - description: >- - EventAttribute is a single key-value pair, associated with an event. - description: >- - Event allows application developers to attach additional information to - - ResponseFinalizeBlock and ResponseCheckTx. - - Later, transactions may be queried using these events. - description: >- - Events contains a slice of Event objects that were emitted during message - - or handler execution. - msg_responses: - type: array - items: - type: object - properties: - type_url: - type: string - description: >- - A URL/resource name that uniquely identifies the type of the serialized - - protocol buffer message. This string must contain at least - - one "/" character. The last segment of the URL's path must represent - - the fully qualified name of the type (as in - - `path/google.protobuf.Duration`). The name should be in a canonical form - - (e.g., leading "." is not accepted). - - In practice, teams usually precompile into the binary all types that they - - expect it to use in the context of Any. However, for URLs which use the - - scheme `http`, `https`, or no scheme, one can optionally set up a type - - server that maps type URLs to message definitions as follows: - - * If no scheme is provided, `https` is assumed. - - * An HTTP GET on the URL must yield a [google.protobuf.Type][] - - value in binary format, or produce an error. - * Applications are allowed to cache lookup results based on the - - URL, or have them precompiled into a binary to avoid any - lookup. Therefore, binary compatibility needs to be preserved - on changes to types. (Use versioned type names to manage - breaking changes.) - - Note: this functionality is not currently available in the official - - protobuf release, and it is not used for type URLs beginning with - - type.googleapis.com. - - Schemes other than `http`, `https` (or the empty scheme) might be - - used with implementation specific semantics. - value: - type: string - format: byte - description: >- - Must be a valid serialized protocol buffer of the above specified type. - description: >- - `Any` contains an arbitrary serialized protocol buffer message along with a - - URL that describes the type of the serialized message. - - Protobuf library provides support to pack/unpack Any values in the form - - of utility functions or additional generated methods of the Any type. - - Example 1: Pack and unpack a message in C++. - - Foo foo = ...; - Any any; - any.PackFrom(foo); - ... - if (any.UnpackTo(&foo)) { - ... - } - - Example 2: Pack and unpack a message in Java. - - Foo foo = ...; - Any any = Any.pack(foo); - ... - if (any.is(Foo.class)) { - foo = any.unpack(Foo.class); - } - // or ... - if (any.isSameTypeAs(Foo.getDefaultInstance())) { - foo = any.unpack(Foo.getDefaultInstance()); - } - - Example 3: Pack and unpack a message in Python. - - foo = Foo(...) - any = Any() - any.Pack(foo) - ... - if any.Is(Foo.DESCRIPTOR): - any.Unpack(foo) - ... - - Example 4: Pack and unpack a message in Go - - foo := &pb.Foo{...} - any, err := anypb.New(foo) - if err != nil { - ... - } - ... - foo := &pb.Foo{} - if err := any.UnmarshalTo(foo); err != nil { - ... - } - - The pack methods provided by protobuf library will by default use - - 'type.googleapis.com/full.type.name' as the type URL and the unpack - - methods only use the fully qualified type name after the last '/' - - in the type URL, for example "foo.bar.com/x/y.z" will yield type - - name "y.z". - - JSON - - The JSON representation of an `Any` value uses the regular - - representation of the deserialized, embedded message, with an - - additional field `@type` which contains the type URL. Example: - - package google.profile; - message Person { - string first_name = 1; - string last_name = 2; - } - - { - "@type": "type.googleapis.com/google.profile.Person", - "firstName": , - "lastName": - } - - If the embedded message type is well-known and has a custom JSON - - representation, that representation will be embedded adding a field - - `value` which holds the custom JSON in addition to the `@type` - - field. Example (for message [google.protobuf.Duration][]): - - { - "@type": "type.googleapis.com/google.protobuf.Duration", - "value": "1.212s" - } - description: >- - msg_responses contains the Msg handler responses type packed in Anys. + height: + type: string + format: int64 + description: height is the block height at which the plan was applied. + description: >- + QueryAppliedPlanResponse is the response type for the Query/AppliedPlan RPC - Since: cosmos-sdk 0.46 - description: |- - SimulateResponse is the response type for the - Service.SimulateRPC method. + method. default: description: An unexpected error response. schema: @@ -20903,22 +19846,28 @@ paths: "value": "1.212s" } parameters: - - name: body - in: body + - name: name + description: name is the name of the applied plan to query for. + in: path required: true - schema: - $ref: '#/definitions/cosmos.tx.v1beta1.SimulateRequest' + type: string tags: - - Service - /cosmos/tx/v1beta1/txs: + - Query + /cosmos/upgrade/v1beta1/authority: get: - summary: GetTxsEvent fetches txs by event. - operationId: GetTxsEvent + summary: Returns the account with authority to conduct upgrades + description: 'Since: cosmos-sdk 0.46' + operationId: Authority responses: '200': description: A successful response. schema: - $ref: '#/definitions/cosmos.tx.v1beta1.GetTxsEventResponse' + type: object + properties: + address: + type: string + description: 'Since: cosmos-sdk 0.46' + title: QueryAuthorityResponse is the response type for Query/Authority default: description: An unexpected error response. schema: @@ -21082,201 +20031,58 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: events - description: >- - events is the list of transaction event type. - - Deprecated post v0.47.x: use query instead, which should contain a valid - - events query. - in: query - required: false - type: array - items: - type: string - collectionFormat: multi - - name: pagination.key - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - in: query - required: false - type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. - - It is less efficient than using key. Only one of offset or key should - - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include - - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key - - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - - name: order_by - description: |2- - - ORDER_BY_UNSPECIFIED: ORDER_BY_UNSPECIFIED specifies an unknown sorting order. OrderBy defaults - to ASC in this case. - - ORDER_BY_ASC: ORDER_BY_ASC defines ascending order - - ORDER_BY_DESC: ORDER_BY_DESC defines descending order - in: query - required: false - type: string - enum: - - ORDER_BY_UNSPECIFIED - - ORDER_BY_ASC - - ORDER_BY_DESC - default: ORDER_BY_UNSPECIFIED - - name: page - description: |- - page is the page number to query, starts at 1. If not provided, will - default to first page. - in: query - required: false - type: string - format: uint64 - - name: limit - description: >- - limit is the total number of results to be returned in the result page. - - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: query - description: >- - query defines the transaction event query that is proxied to Tendermint's - - TxSearch RPC method. The query must be valid. - - Since cosmos-sdk 0.50 - in: query - required: false - type: string tags: - - Service - post: - summary: BroadcastTx broadcast transaction. - operationId: BroadcastTx + - Query + /cosmos/upgrade/v1beta1/current_plan: + get: + summary: CurrentPlan queries the current upgrade plan. + operationId: CurrentPlan responses: '200': description: A successful response. schema: type: object properties: - tx_response: + plan: + description: plan is the current upgrade plan. type: object properties: - height: - type: string - format: int64 - title: The block height - txhash: - type: string - description: The transaction hash. - codespace: - type: string - title: Namespace for the Code - code: - type: integer - format: int64 - description: Response code. - data: - type: string - description: Result bytes, if any. - raw_log: + name: type: string description: >- - The output of the application's logger (raw string). May be + Sets the name for the upgrade. This name will be used by the upgraded - non-deterministic. - logs: - type: array - items: - type: object - properties: - msg_index: - type: integer - format: int64 - log: - type: string - events: - type: array - items: - type: object - properties: - type: - type: string - attributes: - type: array - items: - type: object - properties: - key: - type: string - value: - type: string - description: >- - Attribute defines an attribute wrapper where the key and value are + version of the software to apply any special "on-upgrade" commands during - strings instead of raw bytes. - description: >- - StringEvent defines en Event object wrapper where all the attributes + the first BeginBlock method after the upgrade is applied. It is also used - contain key/value pairs that are strings instead of raw bytes. - description: >- - Events contains a slice of Event objects that were emitted during some + to detect whether a software version can handle a given upgrade. If no - execution. - description: >- - ABCIMessageLog defines a structure containing an indexed tx ABCI message log. - description: >- - The output of the application's logger (typed). May be non-deterministic. - info: + upgrade handler with this name has been set in the software, it will be + + assumed that the software is out-of-date when the upgrade Time or Height is + + reached and the software will exit. + time: type: string - description: Additional information. May be non-deterministic. - gas_wanted: + format: date-time + description: >- + Deprecated: Time based upgrades have been deprecated. Time based upgrade logic + + has been removed from the SDK. + + If this field is not empty, an error will be thrown. + height: type: string format: int64 - description: Amount of gas requested for transaction. - gas_used: + description: The height at which the upgrade must be performed. + info: type: string - format: int64 - description: Amount of gas consumed by transaction. - tx: + title: >- + Any application specific upgrade info to be included on-chain + + such as a git commit that validators could automatically upgrade to + upgraded_client_state: type: object properties: type_url: @@ -21425,57 +20231,10 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - timestamp: - type: string - description: >- - Time of the previous block. For heights > 1, it's the weighted median of - - the timestamps of the valid votes in the block.LastCommit. For height == 1, - - it's genesis time. - events: - type: array - items: - type: object - properties: - type: - type: string - attributes: - type: array - items: - type: object - properties: - key: - type: string - value: - type: string - index: - type: boolean - description: >- - EventAttribute is a single key-value pair, associated with an event. - description: >- - Event allows application developers to attach additional information to - - ResponseFinalizeBlock and ResponseCheckTx. - - Later, transactions may be queried using these events. - description: >- - Events defines all the events emitted by processing a transaction. Note, - - these events include those emitted by processing all the messages and those - - emitted from the ante. Whereas Logs contains the events, with - - additional metadata, emitted only by processing the messages. - - Since: cosmos-sdk 0.42.11, 0.44.5, 0.45 - description: >- - TxResponse defines a structure containing relevant tx data and metadata. The + description: >- + QueryCurrentPlanResponse is the response type for the Query/CurrentPlan RPC - tags are stringified and the log is JSON decoded. - description: |- - BroadcastTxResponse is the response type for the - Service.BroadcastTx method. + method. default: description: An unexpected error response. schema: @@ -21639,55 +20398,43 @@ paths: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } - parameters: - - name: body - in: body - required: true + tags: + - Query + /cosmos/upgrade/v1beta1/module_versions: + get: + summary: ModuleVersions queries the list of module versions from state. + description: 'Since: cosmos-sdk 0.43' + operationId: ModuleVersions + responses: + '200': + description: A successful response. schema: type: object properties: - tx_bytes: - type: string - format: byte - description: tx_bytes is the raw transaction. - mode: - type: string - enum: - - BROADCAST_MODE_UNSPECIFIED - - BROADCAST_MODE_BLOCK - - BROADCAST_MODE_SYNC - - BROADCAST_MODE_ASYNC - default: BROADCAST_MODE_UNSPECIFIED - description: >- - BroadcastMode specifies the broadcast mode for the TxService.Broadcast RPC - - method. - - - BROADCAST_MODE_UNSPECIFIED: zero-value for mode ordering - - BROADCAST_MODE_BLOCK: DEPRECATED: use BROADCAST_MODE_SYNC instead, - BROADCAST_MODE_BLOCK is not supported by the SDK from v0.47.x onwards. - - - BROADCAST_MODE_SYNC: BROADCAST_MODE_SYNC defines a tx broadcasting mode where the client waits - for a CheckTx execution response only. + module_versions: + type: array + items: + type: object + properties: + name: + type: string + title: name of the app module + version: + type: string + format: uint64 + title: consensus version of the app module + description: |- + ModuleVersion specifies a module and its consensus version. - - BROADCAST_MODE_ASYNC: BROADCAST_MODE_ASYNC defines a tx broadcasting mode where the client - returns immediately. + Since: cosmos-sdk 0.43 + description: >- + module_versions is a list of module names with their consensus versions. description: >- - BroadcastTxRequest is the request type for the Service.BroadcastTxRequest + QueryModuleVersionsResponse is the response type for the Query/ModuleVersions RPC method. - tags: - - Service - /cosmos/tx/v1beta1/txs/block/{height}: - get: - summary: GetBlockWithTxs fetches a block with decoded txs. - description: 'Since: cosmos-sdk 0.45.2' - operationId: GetBlockWithTxs - responses: - '200': - description: A successful response. - schema: - $ref: '#/definitions/cosmos.tx.v1beta1.GetBlockWithTxsResponse' + + Since: cosmos-sdk 0.43 default: description: An unexpected error response. schema: @@ -21852,72 +20599,45 @@ paths: "value": "1.212s" } parameters: - - name: height - description: height is the height of the block to query. - in: path - required: true - type: string - format: int64 - - name: pagination.key + - name: module_name description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. + module_name is a field to query a specific module + consensus version from state. Leaving this empty will + fetch the full list of module versions from state. in: query required: false type: string - format: byte - - name: pagination.offset - description: >- - offset is a numeric offset that can be used when key is unavailable. + tags: + - Query + /cosmos/upgrade/v1beta1/upgraded_consensus_state/{last_height}: + get: + summary: >- + UpgradedConsensusState queries the consensus state that will serve - It is less efficient than using key. Only one of offset or key should + as a trusted kernel for the next version of this chain. It will only be - be set. - in: query - required: false - type: string - format: uint64 - - name: pagination.limit - description: >- - limit is the total number of results to be returned in the result page. + stored at the last height of this chain. - If left empty it will default to a value to be set by each app. - in: query - required: false - type: string - format: uint64 - - name: pagination.count_total - description: >- - count_total is set to true to indicate that the result set should include + UpgradedConsensusState RPC not supported with legacy querier - a count of the total number of items available for pagination in UIs. - - count_total is only respected when offset is used. It is ignored when key + This rpc is deprecated now that IBC has its own replacement - is set. - in: query - required: false - type: boolean - - name: pagination.reverse - description: >- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - in: query - required: false - type: boolean - tags: - - Service - /cosmos/tx/v1beta1/txs/{hash}: - get: - summary: GetTx fetches a tx by hash. - operationId: GetTx + (https://github.com/cosmos/ibc-go/blob/2c880a22e9f9cc75f62b527ca94aa75ce1106001/proto/ibc/core/client/v1/query.proto#L54) + operationId: UpgradedConsensusState responses: '200': description: A successful response. schema: - $ref: '#/definitions/cosmos.tx.v1beta1.GetTxResponse' + type: object + properties: + upgraded_consensus_state: + type: string + format: byte + title: 'Since: cosmos-sdk 0.43' + description: >- + QueryUpgradedConsensusStateResponse is the response type for the Query/UpgradedConsensusState + + RPC method. default: description: An unexpected error response. schema: @@ -22082,13 +20802,16 @@ paths: "value": "1.212s" } parameters: - - name: hash - description: hash is the tx hash to query, encoded as a hex string. + - name: last_height + description: |- + last height of the current chain must be sent in request + as this is the height under which next consensus state is stored in: path required: true type: string + format: int64 tags: - - Service + - Query /cosmos/authz/v1beta1/grants: get: summary: Returns list of `Authorization`, granted to the grantee by the granter. @@ -28077,20 +26800,20 @@ paths: format: byte tags: - Query - /zeta-chain/authority/authorization/{msg_url}: + /zeta-chain/authority/authorization/{msgUrl}: get: - operationId: Query_Authorization + operationId: Authorization responses: "200": description: A successful response. schema: - $ref: '#/definitions/authorityQueryAuthorizationResponse' + $ref: '#/definitions/zetachain.zetacore.authority.QueryAuthorizationResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: msg_url + - name: msgUrl in: path required: true type: string @@ -28098,61 +26821,61 @@ paths: - Query /zeta-chain/authority/authorizations: get: - operationId: Query_AuthorizationList + operationId: AuthorizationList responses: "200": description: A successful response. schema: - $ref: '#/definitions/authorityQueryAuthorizationListResponse' + $ref: '#/definitions/zetachain.zetacore.authority.QueryAuthorizationListResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/authority/chainInfo: get: summary: Queries ChainInfo - operationId: Query_ChainInfo + operationId: ChainInfo responses: "200": description: A successful response. schema: - $ref: '#/definitions/authorityQueryGetChainInfoResponse' + $ref: '#/definitions/zetachain.zetacore.authority.QueryGetChainInfoResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/authority/policies: get: summary: Queries Policies - operationId: Query_Policies + operationId: Policies responses: "200": description: A successful response. schema: - $ref: '#/definitions/authorityQueryGetPoliciesResponse' + $ref: '#/definitions/zetachain.zetacore.authority.QueryGetPoliciesResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/crosschain/cctx: get: summary: Queries a list of cctx items. - operationId: Query_CctxAll + operationId: CctxAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28180,7 +26903,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28197,21 +26920,28 @@ paths: in: query required: false type: boolean + - name: unordered + description: |- + Bulk dump all CCTX without ordering. + This is useful for initializing a CCTX indexer. + in: query + required: false + type: boolean tags: - Query /zeta-chain/crosschain/cctx/{chainID}/{nonce}: get: summary: Queries a cctx by nonce. - operationId: Query_CctxByNonce + operationId: CctxByNonce responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chainID in: path @@ -28228,16 +26958,16 @@ paths: /zeta-chain/crosschain/cctx/{index}: get: summary: Queries a send by index. - operationId: Query_Cctx + operationId: Cctx responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: index in: path @@ -28247,16 +26977,16 @@ paths: - Query /zeta-chain/crosschain/convertGasToZeta: get: - operationId: Query_ConvertGasToZeta + operationId: ConvertGasToZeta responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryConvertGasToZetaResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryConvertGasToZetaResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chainId in: query @@ -28272,16 +27002,16 @@ paths: /zeta-chain/crosschain/gasPrice: get: summary: Queries a list of gasPrice items. - operationId: Query_GasPriceAll + operationId: GasPriceAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllGasPriceResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllGasPriceResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28309,7 +27039,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28331,16 +27061,16 @@ paths: /zeta-chain/crosschain/gasPrice/{index}: get: summary: Queries a gasPrice by index. - operationId: Query_GasPrice + operationId: GasPrice responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetGasPriceResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetGasPriceResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: index in: path @@ -28351,16 +27081,16 @@ paths: /zeta-chain/crosschain/inTxHashToCctx: get: summary: 'Deprecated(v17): use InboundHashToCctxAll' - operationId: Query_InTxHashToCctxAll + operationId: InTxHashToCctxAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundHashToCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundHashToCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28388,7 +27118,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28407,19 +27137,20 @@ paths: type: boolean tags: - Query + deprecated: true /zeta-chain/crosschain/inTxHashToCctx/{inboundHash}: get: summary: 'Deprecated(v17): use InboundHashToCctx' - operationId: Query_InTxHashToCctx + operationId: InTxHashToCctx responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetInboundHashToCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetInboundHashToCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: inboundHash in: path @@ -28427,19 +27158,20 @@ paths: type: string tags: - Query + deprecated: true /zeta-chain/crosschain/inTxHashToCctxData/{inboundHash}: get: summary: 'Deprecated(v17): use InboundHashToCctxData' - operationId: Query_InTxHashToCctxData + operationId: InTxHashToCctxData responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryInboundHashToCctxDataResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryInboundHashToCctxDataResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: inboundHash in: path @@ -28447,19 +27179,20 @@ paths: type: string tags: - Query + deprecated: true /zeta-chain/crosschain/inTxTracker: get: summary: 'Deprecated(v17): use InboundTrackerAll' - operationId: Query_InTxTrackerAll + operationId: InTxTrackerAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundTrackersResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundTrackersResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28487,7 +27220,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28506,21 +27239,22 @@ paths: type: boolean tags: - Query - /zeta-chain/crosschain/inTxTrackerByChain/{chain_id}: + deprecated: true + /zeta-chain/crosschain/inTxTrackerByChain/{chainId}: get: summary: 'Deprecated(v17): use InboundTrackerAllByChain' - operationId: Query_InTxTrackerAllByChain + operationId: InTxTrackerAllByChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundTrackerByChainResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundTrackerByChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -28551,7 +27285,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28570,19 +27304,20 @@ paths: type: boolean tags: - Query + deprecated: true /zeta-chain/crosschain/inboundHashToCctx: get: summary: Queries a list of InboundHashToCctx items. - operationId: Query_InboundHashToCctxAll + operationId: InboundHashToCctxAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundHashToCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundHashToCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28610,7 +27345,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28632,16 +27367,16 @@ paths: /zeta-chain/crosschain/inboundHashToCctx/{inboundHash}: get: summary: Queries a InboundHashToCctx by index. - operationId: Query_InboundHashToCctx + operationId: InboundHashToCctx responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetInboundHashToCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetInboundHashToCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: inboundHash in: path @@ -28652,16 +27387,16 @@ paths: /zeta-chain/crosschain/inboundHashToCctxData/{inboundHash}: get: summary: Queries a InboundHashToCctx data by index. - operationId: Query_InboundHashToCctxData + operationId: InboundHashToCctxData responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryInboundHashToCctxDataResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryInboundHashToCctxDataResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: inboundHash in: path @@ -28669,44 +27404,44 @@ paths: type: string tags: - Query - /zeta-chain/crosschain/inboundTracker/{chain_id}/{tx_hash}: + /zeta-chain/crosschain/inboundTracker/{chainId}/{txHash}: get: - operationId: Query_InboundTracker + operationId: InboundTracker responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryInboundTrackerResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryInboundTrackerResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string format: int64 - - name: tx_hash + - name: txHash in: path required: true type: string tags: - Query - /zeta-chain/crosschain/inboundTrackerByChain/{chain_id}: + /zeta-chain/crosschain/inboundTrackerByChain/{chainId}: get: - operationId: Query_InboundTrackerAllByChain + operationId: InboundTrackerAllByChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundTrackerByChainResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundTrackerByChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -28737,7 +27472,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28758,16 +27493,16 @@ paths: - Query /zeta-chain/crosschain/inboundTrackers: get: - operationId: Query_InboundTrackerAll + operationId: InboundTrackerAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllInboundTrackersResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllInboundTrackersResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28795,7 +27530,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28817,16 +27552,16 @@ paths: /zeta-chain/crosschain/lastBlockHeight: get: summary: Queries a list of lastBlockHeight items. - operationId: Query_LastBlockHeightAll + operationId: LastBlockHeightAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllLastBlockHeightResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllLastBlockHeightResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28854,7 +27589,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28876,16 +27611,16 @@ paths: /zeta-chain/crosschain/lastBlockHeight/{index}: get: summary: Queries a lastBlockHeight by index. - operationId: Query_LastBlockHeight + operationId: LastBlockHeight responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetLastBlockHeightResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetLastBlockHeightResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: index in: path @@ -28896,31 +27631,31 @@ paths: /zeta-chain/crosschain/lastZetaHeight: get: summary: Queries a list of lastMetaHeight items. - operationId: Query_LastZetaHeight + operationId: LastZetaHeight responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryLastZetaHeightResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryLastZetaHeightResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/crosschain/outTxTracker: get: summary: 'Deprecated(v17): use OutboundTrackerAll' - operationId: Query_OutTxTrackerAll + operationId: OutTxTrackerAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllOutboundTrackerResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllOutboundTrackerResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -28948,7 +27683,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -28967,19 +27702,20 @@ paths: type: boolean tags: - Query + deprecated: true /zeta-chain/crosschain/outTxTracker/{chainID}/{nonce}: get: summary: 'Deprecated(v17): use OutboundTracker' - operationId: Query_OutTxTracker + operationId: OutTxTracker responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetOutboundTrackerResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetOutboundTrackerResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chainID in: path @@ -28993,19 +27729,20 @@ paths: format: uint64 tags: - Query + deprecated: true /zeta-chain/crosschain/outTxTrackerByChain/{chain}: get: summary: 'Deprecated(v17): use OutboundTrackerAllByChain' - operationId: Query_OutTxTrackerAllByChain + operationId: OutTxTrackerAllByChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllOutboundTrackerByChainResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllOutboundTrackerByChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chain in: path @@ -29038,7 +27775,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29057,19 +27794,20 @@ paths: type: boolean tags: - Query + deprecated: true /zeta-chain/crosschain/outboundTracker: get: summary: Queries a list of OutboundTracker items. - operationId: Query_OutboundTrackerAll + operationId: OutboundTrackerAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllOutboundTrackerResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllOutboundTrackerResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29097,7 +27835,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29119,16 +27857,16 @@ paths: /zeta-chain/crosschain/outboundTracker/{chainID}/{nonce}: get: summary: Queries a outbound tracker by index. - operationId: Query_OutboundTracker + operationId: OutboundTracker responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryGetOutboundTrackerResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryGetOutboundTrackerResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chainID in: path @@ -29144,16 +27882,16 @@ paths: - Query /zeta-chain/crosschain/outboundTrackerByChain/{chain}: get: - operationId: Query_OutboundTrackerAllByChain + operationId: OutboundTrackerAllByChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryAllOutboundTrackerByChainResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryAllOutboundTrackerByChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: chain in: path @@ -29186,7 +27924,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29208,18 +27946,18 @@ paths: /zeta-chain/crosschain/pendingCctx: get: summary: Queries a list of pending cctxs. - operationId: Query_ListPendingCctx + operationId: ListPendingCctx responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryListPendingCctxResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryListPendingCctxResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: query required: false type: string @@ -29234,16 +27972,16 @@ paths: /zeta-chain/crosschain/pendingCctxWithinRateLimit: get: summary: Queries a list of pending cctxs within rate limit. - operationId: Query_ListPendingCctxWithinRateLimit + operationId: ListPendingCctxWithinRateLimit responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryListPendingCctxWithinRateLimitResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: limit in: query @@ -29254,46 +27992,46 @@ paths: - Query /zeta-chain/crosschain/protocolFee: get: - operationId: Query_ProtocolFee + operationId: ProtocolFee responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryMessagePassingProtocolFeeResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryMessagePassingProtocolFeeResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/crosschain/rateLimiterFlags: get: summary: Queries the rate limiter flags - operationId: Query_RateLimiterFlags + operationId: RateLimiterFlags responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryRateLimiterFlagsResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryRateLimiterFlagsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/crosschain/rateLimiterInput: get: summary: Queries the input data of rate limiter. - operationId: Query_RateLimiterInput + operationId: RateLimiterInput responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryRateLimiterInputResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryRateLimiterInputResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: limit in: query @@ -29309,61 +28047,61 @@ paths: - Query /zeta-chain/crosschain/zetaAccounting: get: - operationId: Query_ZetaAccounting + operationId: ZetaAccounting responses: "200": description: A successful response. schema: - $ref: '#/definitions/crosschainQueryZetaAccountingResponse' + $ref: '#/definitions/zetachain.zetacore.crosschain.QueryZetaAccountingResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/emissions/list_addresses: get: summary: Queries a list of ListBalances items. - operationId: Query_ListPoolAddresses + operationId: ListPoolAddresses responses: "200": description: A successful response. schema: - $ref: '#/definitions/emissionsQueryListPoolAddressesResponse' + $ref: '#/definitions/zetachain.zetacore.emissions.QueryListPoolAddressesResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/emissions/params: get: summary: Parameters queries the parameters of the module. - operationId: Query_Params + operationId: Params responses: "200": description: A successful response. schema: - $ref: '#/definitions/emissionsQueryParamsResponse' + $ref: '#/definitions/zetachain.zetacore.emissions.QueryParamsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/emissions/show_available_emissions/{address}: get: summary: Queries a list of ShowAvailableEmissions items. - operationId: Query_ShowAvailableEmissions + operationId: ShowAvailableEmissions responses: "200": description: A successful response. schema: - $ref: '#/definitions/emissionsQueryShowAvailableEmissionsResponse' + $ref: '#/definitions/zetachain.zetacore.emissions.QueryShowAvailableEmissionsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: address in: path @@ -29374,16 +28112,16 @@ paths: /zeta-chain/fungible/code_hash/{address}: get: summary: Code hash query the code hash of a contract. - operationId: Query_CodeHash + operationId: CodeHash responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryCodeHashResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryCodeHashResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: address in: path @@ -29394,16 +28132,16 @@ paths: /zeta-chain/fungible/foreign_coins: get: summary: Queries a list of ForeignCoins items. - operationId: Query_ForeignCoinsAll + operationId: ForeignCoinsAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryAllForeignCoinsResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryAllForeignCoinsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29431,7 +28169,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29453,16 +28191,16 @@ paths: /zeta-chain/fungible/foreign_coins/{index}: get: summary: Queries a ForeignCoins by index. - operationId: Query_ForeignCoins + operationId: ForeignCoins responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryGetForeignCoinsResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryGetForeignCoinsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: index in: path @@ -29473,33 +28211,33 @@ paths: /zeta-chain/fungible/gas_stability_pool_address: get: summary: Queries the address of a gas stability pool on a given chain. - operationId: Query_GasStabilityPoolAddress + operationId: GasStabilityPoolAddress responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryGetGasStabilityPoolAddressResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryGetGasStabilityPoolAddressResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query - /zeta-chain/fungible/gas_stability_pool_balance/{chain_id}: + /zeta-chain/fungible/gas_stability_pool_balance/{chainId}: get: summary: Queries the balance of a gas stability pool on a given chain. - operationId: Query_GasStabilityPoolBalance + operationId: GasStabilityPoolBalance responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryGetGasStabilityPoolBalanceResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryGetGasStabilityPoolBalanceResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -29509,30 +28247,30 @@ paths: /zeta-chain/fungible/system_contract: get: summary: Queries SystemContract - operationId: Query_SystemContract + operationId: SystemContract responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryGetSystemContractResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryGetSystemContractResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/lightclient/block_headers: get: - operationId: Query_BlockHeaderAll + operationId: BlockHeaderAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryAllBlockHeaderResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryAllBlockHeaderResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29560,7 +28298,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29579,38 +28317,40 @@ paths: type: boolean tags: - Query - /zeta-chain/lightclient/block_headers/{block_hash}: + deprecated: true + /zeta-chain/lightclient/block_headers/{blockHash}: get: - operationId: Query_BlockHeader + operationId: BlockHeader responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryGetBlockHeaderResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryGetBlockHeaderResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: block_hash + - name: blockHash in: path required: true type: string format: byte tags: - Query + deprecated: true /zeta-chain/lightclient/chain_state: get: - operationId: Query_ChainStateAll + operationId: ChainStateAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryAllChainStateResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryAllChainStateResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29638,7 +28378,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29657,77 +28397,81 @@ paths: type: boolean tags: - Query - /zeta-chain/lightclient/chain_state/{chain_id}: + deprecated: true + /zeta-chain/lightclient/chain_state/{chainId}: get: - operationId: Query_ChainState + operationId: ChainState responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryGetChainStateResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryGetChainStateResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string format: int64 tags: - Query + deprecated: true /zeta-chain/lightclient/header_enabled_chains: get: - operationId: Query_HeaderEnabledChains + operationId: HeaderEnabledChains responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryHeaderEnabledChainsResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryHeaderEnabledChainsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query + deprecated: true /zeta-chain/lightclient/header_supported_chains: get: - operationId: Query_HeaderSupportedChains + operationId: HeaderSupportedChains responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryHeaderSupportedChainsResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryHeaderSupportedChainsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query + deprecated: true /zeta-chain/lightclient/prove: get: - operationId: Query_Prove + operationId: Prove responses: "200": description: A successful response. schema: - $ref: '#/definitions/lightclientQueryProveResponse' + $ref: '#/definitions/zetachain.zetacore.lightclient.QueryProveResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: query required: false type: string format: int64 - - name: tx_hash + - name: txHash in: query required: false type: string - - name: proof.ethereum_proof.keys + - name: proof.ethereumProof.keys in: query required: false type: array @@ -29735,7 +28479,7 @@ paths: type: string format: byte collectionFormat: multi - - name: proof.ethereum_proof.values + - name: proof.ethereumProof.values in: query required: false type: array @@ -29743,62 +28487,63 @@ paths: type: string format: byte collectionFormat: multi - - name: proof.bitcoin_proof.tx_bytes + - name: proof.bitcoinProof.txBytes in: query required: false type: string format: byte - - name: proof.bitcoin_proof.path + - name: proof.bitcoinProof.path in: query required: false type: string format: byte - - name: proof.bitcoin_proof.index + - name: proof.bitcoinProof.index in: query required: false type: integer format: int64 - - name: block_hash + - name: blockHash in: query required: false type: string - - name: tx_index + - name: txIndex in: query required: false type: string format: int64 tags: - Query + deprecated: true /zeta-chain/observer/TSS: get: summary: Queries a tSS by index. - operationId: Query_TSS + operationId: TSS responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetTSSResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetTSSResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query - /zeta-chain/observer/ballot_by_identifier/{ballot_identifier}: + /zeta-chain/observer/ballot_by_identifier/{ballotIdentifier}: get: summary: Queries a list of VoterByIdentifier items. - operationId: Query_BallotByIdentifier + operationId: BallotByIdentifier responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryBallotByIdentifierResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryBallotByIdentifierResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: ballot_identifier + - name: ballotIdentifier in: path required: true type: string @@ -29807,16 +28552,16 @@ paths: /zeta-chain/observer/ballots: get: summary: Query all ballots - operationId: Query_Ballots + operationId: Ballots responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryBallotsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryBallotsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29844,7 +28589,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29863,21 +28608,21 @@ paths: type: boolean tags: - Query - /zeta-chain/observer/blame_by_chain_and_nonce/{chain_id}/{nonce}: + /zeta-chain/observer/blame_by_chain_and_nonce/{chainId}/{nonce}: get: summary: Queries a list of VoterByIdentifier items. - operationId: Query_BlamesByChainAndNonce + operationId: BlamesByChainAndNonce responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryBlameByChainAndNonceResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryBlameByChainAndNonceResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -29889,21 +28634,21 @@ paths: format: int64 tags: - Query - /zeta-chain/observer/blame_by_identifier/{blame_identifier}: + /zeta-chain/observer/blame_by_identifier/{blameIdentifier}: get: summary: Queries a list of VoterByIdentifier items. - operationId: Query_BlameByIdentifier + operationId: BlameByIdentifier responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryBlameByIdentifierResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryBlameByIdentifierResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: blame_identifier + - name: blameIdentifier in: path required: true type: string @@ -29912,16 +28657,16 @@ paths: /zeta-chain/observer/chainNonces: get: summary: Queries a list of chainNonces items. - operationId: Query_ChainNoncesAll + operationId: ChainNoncesAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryAllChainNoncesResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryAllChainNoncesResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -29949,7 +28694,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -29968,21 +28713,21 @@ paths: type: boolean tags: - Query - /zeta-chain/observer/chainNonces/{chain_id}: + /zeta-chain/observer/chainNonces/{chainId}: get: summary: Queries a chainNonces by index. - operationId: Query_ChainNonces + operationId: ChainNonces responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetChainNoncesResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetChainNoncesResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -29991,31 +28736,67 @@ paths: - Query /zeta-chain/observer/crosschain_flags: get: - operationId: Query_CrosschainFlags + operationId: CrosschainFlags + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetCrosschainFlagsResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/google.rpc.Status' + tags: + - Query + /zeta-chain/observer/getAllTssFundsMigrators: + get: + summary: Queries all TssFundMigratorInfo + operationId: TssFundsMigratorInfoAll + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/zetachain.zetacore.observer.QueryTssFundsMigratorInfoAllResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/google.rpc.Status' + tags: + - Query + /zeta-chain/observer/getTssFundsMigrator: + get: + summary: Queries the TssFundMigratorInfo for a specific chain + operationId: TssFundsMigratorInfo responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetCrosschainFlagsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryTssFundsMigratorInfoResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' + parameters: + - name: chainId + in: query + required: false + type: string + format: int64 tags: - Query /zeta-chain/observer/get_all_blame_records: get: summary: Queries a list of VoterByIdentifier items. - operationId: Query_GetAllBlameRecords + operationId: GetAllBlameRecords responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryAllBlameRecordsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryAllBlameRecordsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -30043,7 +28824,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -30065,140 +28846,104 @@ paths: /zeta-chain/observer/get_chain_params: get: summary: Queries a list of GetChainParams items. - operationId: Query_GetChainParams + operationId: GetChainParams responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetChainParamsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetChainParamsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query - /zeta-chain/observer/get_chain_params_for_chain/{chain_id}: + /zeta-chain/observer/get_chain_params_for_chain/{chainId}: get: summary: Queries a list of GetChainParamsForChain items. - operationId: Query_GetChainParamsForChain + operationId: GetChainParamsForChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetChainParamsForChainResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetChainParamsForChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string format: int64 tags: - Query - /zeta-chain/observer/get_tss_address/{bitcoin_chain_id}: + /zeta-chain/observer/get_tss_address/{bitcoinChainId}: get: summary: Queries a list of GetTssAddress items. - operationId: Query_GetTssAddress + operationId: GetTssAddress responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetTssAddressResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetTssAddressResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: bitcoin_chain_id + - name: bitcoinChainId in: path required: true type: string format: int64 tags: - Query - /zeta-chain/observer/get_tss_address_historical/{finalized_zeta_height}/{bitcoin_chain_id}: + /zeta-chain/observer/get_tss_address_historical/{finalizedZetaHeight}/{bitcoinChainId}: get: - operationId: Query_GetTssAddressByFinalizedHeight + operationId: GetTssAddressByFinalizedHeight responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetTssAddressByFinalizedHeightResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetTssAddressByFinalizedHeightResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: finalized_zeta_height + - name: finalizedZetaHeight in: path required: true type: string format: int64 - - name: bitcoin_chain_id + - name: bitcoinChainId in: path required: true type: string format: int64 tags: - Query - /zeta-chain/observer/getAllTssFundsMigrators: - get: - summary: Queries all TssFundMigratorInfo - operationId: Query_TssFundsMigratorInfoAll - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/observerQueryTssFundsMigratorInfoAllResponse' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - tags: - - Query - /zeta-chain/observer/getTssFundsMigrator: - get: - summary: Queries the TssFundMigratorInfo for a specific chain - operationId: Query_TssFundsMigratorInfo - responses: - "200": - description: A successful response. - schema: - $ref: '#/definitions/observerQueryTssFundsMigratorInfoResponse' - default: - description: An unexpected error response. - schema: - $ref: '#/definitions/googlerpcStatus' - parameters: - - name: chain_id - in: query - required: false - type: string - format: int64 - tags: - - Query - /zeta-chain/observer/has_voted/{ballot_identifier}/{voter_address}: + /zeta-chain/observer/has_voted/{ballotIdentifier}/{voterAddress}: get: summary: Query if a voter has voted for a ballot - operationId: Query_HasVoted + operationId: HasVoted responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryHasVotedResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryHasVotedResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: ballot_identifier + - name: ballotIdentifier in: path required: true type: string - - name: voter_address + - name: voterAddress in: path required: true type: string @@ -30207,31 +28952,31 @@ paths: /zeta-chain/observer/keygen: get: summary: Queries a keygen by index. - operationId: Query_Keygen + operationId: Keygen responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetKeygenResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetKeygenResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/observer/nodeAccount: get: summary: Queries a list of nodeAccount items. - operationId: Query_NodeAccountAll + operationId: NodeAccountAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryAllNodeAccountResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryAllNodeAccountResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -30259,7 +29004,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -30281,16 +29026,16 @@ paths: /zeta-chain/observer/nodeAccount/{index}: get: summary: Queries a nodeAccount by index. - operationId: Query_NodeAccount + operationId: NodeAccount responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryGetNodeAccountResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryGetNodeAccountResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: index in: path @@ -30301,45 +29046,45 @@ paths: /zeta-chain/observer/observer_set: get: summary: Queries a list of ObserversByChainAndType items. - operationId: Query_ObserverSet + operationId: ObserverSet responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryObserverSetResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryObserverSetResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/observer/operationalFlags: get: summary: Queries operational flags - operationId: Query_OperationalFlags + operationId: OperationalFlags responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryOperationalFlagsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryOperationalFlagsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/observer/pendingNonces: get: - operationId: Query_PendingNoncesAll + operationId: PendingNoncesAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryAllPendingNoncesResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryAllPendingNoncesResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -30367,7 +29112,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -30386,20 +29131,20 @@ paths: type: boolean tags: - Query - /zeta-chain/observer/pendingNonces/{chain_id}: + /zeta-chain/observer/pendingNonces/{chainId}: get: - operationId: Query_PendingNoncesByChain + operationId: PendingNoncesByChain responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryPendingNoncesByChainResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryPendingNoncesByChainResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - - name: chain_id + - name: chainId in: path required: true type: string @@ -30408,30 +29153,30 @@ paths: - Query /zeta-chain/observer/supportedChains: get: - operationId: Query_SupportedChains + operationId: SupportedChains responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQuerySupportedChainsResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QuerySupportedChainsResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/observer/tssHistory: get: - operationId: Query_TssHistory + operationId: TssHistory responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryTssHistoryResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryTssHistoryResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' parameters: - name: pagination.key description: |- @@ -30459,7 +29204,7 @@ paths: required: false type: string format: uint64 - - name: pagination.count_total + - name: pagination.countTotal description: |- count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. @@ -30481,31 +29226,31 @@ paths: /zeta-chain/zetacore/fungible/gas_stability_pool_balance: get: summary: Queries all gas stability pool balances. - operationId: Query_GasStabilityPoolBalanceAll + operationId: GasStabilityPoolBalanceAll responses: "200": description: A successful response. schema: - $ref: '#/definitions/fungibleQueryAllGasStabilityPoolBalanceResponse' + $ref: '#/definitions/zetachain.zetacore.fungible.QueryAllGasStabilityPoolBalanceResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /zeta-chain/zetacore/observer/show_observer_count: get: summary: Queries a list of ShowObserverCount items. - operationId: Query_ShowObserverCount + operationId: ShowObserverCount responses: "200": description: A successful response. schema: - $ref: '#/definitions/observerQueryShowObserverCountResponse' + $ref: '#/definitions/zetachain.zetacore.observer.QueryShowObserverCountResponse' default: description: An unexpected error response. schema: - $ref: '#/definitions/googlerpcStatus' + $ref: '#/definitions/google.rpc.Status' tags: - Query /ethermint/evm/v1/account/{address}: @@ -34525,6 +33270,13 @@ definitions: reverse is set to true if results are to be returned in the descending order. Since: cosmos-sdk 0.43 + countTotal: + type: boolean + description: |- + count_total is set to true to indicate that the result set should include + a count of the total number of items available for pagination in UIs. + count_total is only respected when offset is used. It is ignored when key + is set. description: |- message SomeRequest { Foo some_parameter = 1; @@ -34549,6 +33301,13 @@ definitions: title: |- total is total number of results available if PageRequest.count_total was set, its value is undefined otherwise + nextKey: + type: string + format: byte + description: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently. It will be empty if + there are no more results. description: |- PageResponse is to be embedded in gRPC response messages where the corresponding request message has used PageRequest. @@ -34609,6 +33368,8 @@ definitions: format: byte description: >- Must be a valid serialized protocol buffer of the above specified type. + '@type': + type: string description: >- `Any` contains an arbitrary serialized protocol buffer message along with a @@ -34702,6 +33463,7 @@ definitions: "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } + additionalProperties: {} grpc.gateway.runtime.Error: type: object properties: @@ -58611,81 +57373,86 @@ definitions: field's configuration is global (not module specific). description: QueryConfigRequest is the Query/Config response type. - QueryAllGasStabilityPoolBalanceResponseBalance: + google.rpc.Status: type: object properties: - chain_id: - type: string - format: int64 - balance: + code: + type: integer + format: int32 + message: type: string - authorityAuthorization: + details: + type: array + items: + type: object + $ref: '#/definitions/google.protobuf.Any' + zetachain.zetacore.authority.Authorization: type: object properties: - msg_url: + msgUrl: type: string title: The URL of the message that needs to be authorized - authorized_policy: - $ref: '#/definitions/authorityPolicyType' + authorizedPolicy: + $ref: '#/definitions/zetachain.zetacore.authority.PolicyType' title: The policy that is authorized to access the message title: |- Authorization defines the authorization required to access use a message which needs special permissions - authorityAuthorizationList: + zetachain.zetacore.authority.AuthorizationList: type: object properties: authorizations: type: array items: type: object - $ref: '#/definitions/authorityAuthorization' + $ref: '#/definitions/zetachain.zetacore.authority.Authorization' title: AuthorizationList holds the list of authorizations on zetachain - authorityChainInfo: + zetachain.zetacore.authority.ChainInfo: type: object properties: chains: type: array items: type: object - $ref: '#/definitions/chainsChain' + $ref: '#/definitions/zetachain.zetacore.pkg.chains.Chain' title: |- ChainInfo contains static information about the chains This structure is used to dynamically update these info on a live network before hardcoding the values in a upgrade - authorityMsgAddAuthorizationResponse: + zetachain.zetacore.authority.MsgAddAuthorizationResponse: type: object description: MsgAddAuthorizationResponse defines the MsgAddAuthorizationResponse service. - authorityMsgRemoveAuthorizationResponse: + zetachain.zetacore.authority.MsgRemoveAuthorizationResponse: type: object description: |- MsgRemoveAuthorizationResponse defines the MsgRemoveAuthorizationResponse service. - authorityMsgRemoveChainInfoResponse: + zetachain.zetacore.authority.MsgRemoveChainInfoResponse: type: object description: MsgRemoveChainInfoResponse defines the MsgRemoveChainInfoResponse service. - authorityMsgUpdateChainInfoResponse: + zetachain.zetacore.authority.MsgUpdateChainInfoResponse: type: object description: MsgUpdateChainInfoResponse defines the MsgUpdateChainInfoResponse service. - authorityMsgUpdatePoliciesResponse: + zetachain.zetacore.authority.MsgUpdatePoliciesResponse: type: object description: MsgUpdatePoliciesResponse defines the MsgUpdatePoliciesResponse service. - authorityPolicies: + zetachain.zetacore.authority.Policies: type: object properties: items: type: array items: type: object - $ref: '#/definitions/authorityPolicy' + $ref: '#/definitions/zetachain.zetacore.authority.Policy' title: Policy contains info about authority policies - authorityPolicy: + zetachain.zetacore.authority.Policy: type: object properties: - policy_type: - $ref: '#/definitions/authorityPolicyType' + policyType: + $ref: '#/definitions/zetachain.zetacore.authority.PolicyType' address: type: string - authorityPolicyType: + zetachain.zetacore.authority.PolicyType: type: string enum: - groupEmergency @@ -58703,214 +57470,47 @@ definitions: Used for empty policy, no action is allowed title: PolicyType defines the type of policy - authorityQueryAuthorizationListResponse: + zetachain.zetacore.authority.QueryAuthorizationListResponse: type: object properties: - authorization_list: - $ref: '#/definitions/authorityAuthorizationList' + authorizationList: + $ref: '#/definitions/zetachain.zetacore.authority.AuthorizationList' title: |- QueryAuthorizationListResponse is the response type for the Query/AuthorizationList RPC - authorityQueryAuthorizationResponse: + zetachain.zetacore.authority.QueryAuthorizationResponse: type: object properties: authorization: - $ref: '#/definitions/authorityAuthorization' + $ref: '#/definitions/zetachain.zetacore.authority.Authorization' description: |- QueryAuthorizationResponse is the response type for the Query/Authorization RPC method. - authorityQueryGetChainInfoResponse: + zetachain.zetacore.authority.QueryGetChainInfoResponse: type: object properties: - chain_info: - $ref: '#/definitions/authorityChainInfo' + chainInfo: + $ref: '#/definitions/zetachain.zetacore.authority.ChainInfo' description: |- QueryGetChainInfoResponse is the response type for the Query/ChainInfo RPC method. - authorityQueryGetPoliciesResponse: + zetachain.zetacore.authority.QueryGetPoliciesResponse: type: object properties: policies: - $ref: '#/definitions/authorityPolicies' + $ref: '#/definitions/zetachain.zetacore.authority.Policies' description: |- QueryGetPoliciesResponse is the response type for the Query/Policies RPC method. - chainsCCTXGateway: - type: string - enum: - - zevm - - observers - default: zevm - description: |- - - zevm: zevm is the internal CCTX gateway to process outbound on the ZEVM and read - inbound events from the ZEVM only used for ZetaChain chains - - observers: observers is the CCTX gateway for chains relying on the observer set to - observe inbounds and TSS for outbounds - title: CCTXGateway describes for the chain the gateway used to handle CCTX outbounds - chainsChain: - type: object - properties: - chain_id: - type: string - format: int64 - title: ChainId is the unique identifier of the chain - chain_name: - $ref: '#/definitions/chainsChainName' - title: |- - ChainName is the name of the chain - Deprecated(v19): replaced with Name - network: - $ref: '#/definitions/chainsNetwork' - title: Network is the network of the chain - network_type: - $ref: '#/definitions/chainsNetworkType' - description: 'NetworkType is the network type of the chain: mainnet, testnet, etc..' - vm: - $ref: '#/definitions/chainsVm' - title: Vm is the virtual machine used in the chain - consensus: - $ref: '#/definitions/chainsConsensus' - title: Consensus is the underlying consensus algorithm used by the chain - is_external: - type: boolean - title: IsExternal describe if the chain is ZetaChain or external - cctx_gateway: - $ref: '#/definitions/chainsCCTXGateway' - title: CCTXGateway is the gateway used to handle CCTX outbounds - name: - type: string - title: Name is the name of the chain - title: |- - Chain represents static data about a blockchain network - it is identified by a unique chain ID - chainsChainName: - type: string - enum: - - empty - - eth_mainnet - - zeta_mainnet - - btc_mainnet - - polygon_mainnet - - bsc_mainnet - - goerli_testnet - - mumbai_testnet - - bsc_testnet - - zeta_testnet - - btc_testnet - - sepolia_testnet - - goerli_localnet - - btc_regtest - - amoy_testnet - - optimism_mainnet - - optimism_sepolia - - base_mainnet - - base_sepolia - - solana_mainnet - - solana_devnet - - solana_localnet - default: empty - title: |- - ChainName represents the name of the chain - Deprecated(v19): replaced with Chain.Name as string - chainsConsensus: - type: string - enum: - - ethereum - - tendermint - - bitcoin - - op_stack - - solana_consensus - - catchain_consensus - - snowman - - arbitrum_nitro - - sui_consensus - default: ethereum - description: |- - - catchain_consensus: ton - - snowman: avalanche - title: |- - Consensus represents the consensus algorithm used by the chain - this can represent the consensus of a L1 - this can also represent the solution of a L2 - chainsNetwork: - type: string - enum: - - eth - - zeta - - btc - - polygon - - bsc - - optimism - - base - - solana - - ton - - avalanche - - arbitrum - - worldchain - - sui - default: eth - title: |- - Network represents the network of the chain - there is a single instance of the network on mainnet - then the network can have eventual testnets or devnets - chainsNetworkType: - type: string - enum: - - mainnet - - testnet - - privnet - - devnet - default: mainnet - title: |- - NetworkType represents the network type of the chain - Mainnet, Testnet, Privnet, Devnet - chainsReceiveStatus: - type: string - enum: - - created - - success - - failed - default: created - description: '- created: Created is used for inbounds' - title: |- - ReceiveStatus represents the status of an outbound - TODO: Rename and move - https://github.com/zeta-chain/node/issues/2257 - chainsVm: - type: string - enum: - - no_vm - - evm - - svm - - tvm - - mvm_sui - default: no_vm - title: |- - Vm represents the virtual machine type of the chain to support smart - contracts - coinCoinType: - type: string - enum: - - Zeta - - Gas - - ERC20 - - Cmd - - NoAssetCall - default: Zeta - title: |- - - Gas: Ether, BNB, Matic, Klay, BTC, etc - - ERC20: ERC20 token - - Cmd: no asset, used for admin command - - NoAssetCall: no asset, used for contract call - crosschainCallOptions: + zetachain.zetacore.crosschain.CallOptions: type: object properties: - gas_limit: + gasLimit: type: string format: uint64 - is_arbitrary_call: + isArbitraryCall: type: boolean - crosschainCctxStatus: + zetachain.zetacore.crosschain.CctxStatus: type: string enum: - PendingInbound @@ -58927,7 +57527,7 @@ definitions: - PendingRevert: outbound cannot succeed; should revert inbound - Reverted: inbound reverted. - Aborted: inbound tx error or invalid paramters and cannot revert; just abort. - crosschainConfirmationMode: + zetachain.zetacore.crosschain.ConfirmationMode: type: string enum: - SAFE @@ -58936,53 +57536,53 @@ definitions: title: |- - SAFE: an inbound/outbound is confirmed using safe confirmation count - FAST: an inbound/outbound is confirmed using fast confirmation count - crosschainConversion: + zetachain.zetacore.crosschain.Conversion: type: object properties: zrc20: type: string rate: type: string - crosschainCrossChainTx: + zetachain.zetacore.crosschain.CrossChainTx: type: object properties: creator: type: string index: type: string - zeta_fees: + zetaFees: type: string - relayed_message: + relayedMessage: type: string title: Not used by protocol , just relayed across - cctx_status: - $ref: '#/definitions/zetacorecrosschainStatus' - inbound_params: - $ref: '#/definitions/crosschainInboundParams' - outbound_params: + cctxStatus: + $ref: '#/definitions/zetachain.zetacore.crosschain.Status' + inboundParams: + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundParams' + outboundParams: type: array items: type: object - $ref: '#/definitions/crosschainOutboundParams' - protocol_contract_version: - $ref: '#/definitions/crosschainProtocolContractVersion' - revert_options: - $ref: '#/definitions/crosschainRevertOptions' - crosschainGasPrice: + $ref: '#/definitions/zetachain.zetacore.crosschain.OutboundParams' + protocolContractVersion: + $ref: '#/definitions/zetachain.zetacore.crosschain.ProtocolContractVersion' + revertOptions: + $ref: '#/definitions/zetachain.zetacore.crosschain.RevertOptions' + zetachain.zetacore.crosschain.GasPrice: type: object properties: creator: type: string index: type: string - chain_id: + chainId: type: string format: int64 signers: type: array items: type: string - block_nums: + blockNums: type: array items: type: string @@ -58992,69 +57592,69 @@ definitions: items: type: string format: uint64 - median_index: + medianIndex: type: string format: uint64 title: index of the median gas price in the prices array - priority_fees: + priorityFees: type: array items: type: string format: uint64 title: priority fees for EIP-1559 - crosschainInboundHashToCctx: + zetachain.zetacore.crosschain.InboundHashToCctx: type: object properties: - inbound_hash: + inboundHash: type: string - cctx_index: + cctxIndex: type: array items: type: string - crosschainInboundParams: + zetachain.zetacore.crosschain.InboundParams: type: object properties: sender: type: string title: this address is the immediate contract/EOA that calls - sender_chain_id: + senderChainId: type: string format: int64 title: the Connector.send() - tx_origin: + txOrigin: type: string title: this address is the EOA that signs the inbound tx - coin_type: - $ref: '#/definitions/coinCoinType' + coinType: + $ref: '#/definitions/zetachain.zetacore.pkg.coin.CoinType' asset: type: string title: for ERC20 coin type, the asset is an address of the ERC20 contract amount: type: string - observed_hash: + observedHash: type: string - observed_external_height: + observedExternalHeight: type: string format: uint64 - ballot_index: + ballotIndex: type: string - finalized_zeta_height: + finalizedZetaHeight: type: string format: uint64 - tx_finalization_status: - $ref: '#/definitions/crosschainTxFinalizationStatus' - is_cross_chain_call: + txFinalizationStatus: + $ref: '#/definitions/zetachain.zetacore.crosschain.TxFinalizationStatus' + isCrossChainCall: type: boolean title: |- this field describes if a smart contract call should be made for a inbound with assets only used for protocol contract version 2 status: - $ref: '#/definitions/crosschainInboundStatus' + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundStatus' title: status of the inbound observation - confirmation_mode: - $ref: '#/definitions/crosschainConfirmationMode' + confirmationMode: + $ref: '#/definitions/zetachain.zetacore.crosschain.ConfirmationMode' title: confirmation mode used for the inbound - crosschainInboundStatus: + zetachain.zetacore.crosschain.InboundStatus: type: string enum: - SUCCESS @@ -59066,17 +57666,17 @@ definitions: depositor fee - INVALID_RECEIVER_ADDRESS: the receiver address parsed from the inbound is invalid title: InboundStatus represents the status of an observed inbound - crosschainInboundTracker: + zetachain.zetacore.crosschain.InboundTracker: type: object properties: - chain_id: + chainId: type: string format: int64 - tx_hash: + txHash: type: string - coin_type: - $ref: '#/definitions/coinCoinType' - crosschainLastBlockHeight: + coinType: + $ref: '#/definitions/zetachain.zetacore.pkg.coin.CoinType' + zetachain.zetacore.crosschain.LastBlockHeight: type: object properties: creator: @@ -59091,119 +57691,119 @@ definitions: lastOutboundHeight: type: string format: uint64 - crosschainMsgAbortStuckCCTXResponse: + zetachain.zetacore.crosschain.MsgAbortStuckCCTXResponse: type: object - crosschainMsgAddInboundTrackerResponse: + zetachain.zetacore.crosschain.MsgAddInboundTrackerResponse: type: object - crosschainMsgAddOutboundTrackerResponse: + zetachain.zetacore.crosschain.MsgAddOutboundTrackerResponse: type: object properties: - is_removed: + isRemoved: type: boolean title: if the tx was removed from the tracker due to no pending cctx - crosschainMsgMigrateERC20CustodyFundsResponse: + zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFundsResponse: type: object properties: - cctx_index: + cctxIndex: type: string - crosschainMsgMigrateTssFundsResponse: + zetachain.zetacore.crosschain.MsgMigrateTssFundsResponse: type: object - crosschainMsgRefundAbortedCCTXResponse: + zetachain.zetacore.crosschain.MsgRefundAbortedCCTXResponse: type: object - crosschainMsgRemoveInboundTrackerResponse: + zetachain.zetacore.crosschain.MsgRemoveInboundTrackerResponse: type: object - crosschainMsgRemoveOutboundTrackerResponse: + zetachain.zetacore.crosschain.MsgRemoveOutboundTrackerResponse: type: object - crosschainMsgUpdateERC20CustodyPauseStatusResponse: + zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatusResponse: type: object properties: - cctx_index: + cctxIndex: type: string - crosschainMsgUpdateRateLimiterFlagsResponse: + zetachain.zetacore.crosschain.MsgUpdateRateLimiterFlagsResponse: type: object - crosschainMsgUpdateTssAddressResponse: + zetachain.zetacore.crosschain.MsgUpdateTssAddressResponse: type: object - crosschainMsgVoteGasPriceResponse: + zetachain.zetacore.crosschain.MsgVoteGasPriceResponse: type: object - crosschainMsgVoteInboundResponse: + zetachain.zetacore.crosschain.MsgVoteInboundResponse: type: object - crosschainMsgVoteOutboundResponse: + zetachain.zetacore.crosschain.MsgVoteOutboundResponse: type: object - crosschainMsgWhitelistERC20Response: + zetachain.zetacore.crosschain.MsgWhitelistERC20Response: type: object properties: - zrc20_address: + zrc20Address: type: string - cctx_index: + cctxIndex: type: string - crosschainOutboundParams: + zetachain.zetacore.crosschain.OutboundParams: type: object properties: receiver: type: string - receiver_chainId: + receiverChainId: type: string format: int64 - coin_type: - $ref: '#/definitions/coinCoinType' + coinType: + $ref: '#/definitions/zetachain.zetacore.pkg.coin.CoinType' amount: type: string - tss_nonce: + tssNonce: type: string format: uint64 - gas_limit: + gasLimit: type: string format: uint64 title: Deprecated (v21), use CallOptions - gas_price: + gasPrice: type: string - gas_priority_fee: + gasPriorityFee: type: string hash: type: string title: |- the above are commands for zetaclients the following fields are used when the outbound tx is mined - ballot_index: + ballotIndex: type: string - observed_external_height: + observedExternalHeight: type: string format: uint64 - gas_used: + gasUsed: type: string format: uint64 - effective_gas_price: + effectiveGasPrice: type: string - effective_gas_limit: + effectiveGasLimit: type: string format: uint64 - tss_pubkey: - type: string - tx_finalization_status: - $ref: '#/definitions/crosschainTxFinalizationStatus' - call_options: - $ref: '#/definitions/crosschainCallOptions' - confirmation_mode: - $ref: '#/definitions/crosschainConfirmationMode' + tssPubkey: + type: string + txFinalizationStatus: + $ref: '#/definitions/zetachain.zetacore.crosschain.TxFinalizationStatus' + callOptions: + $ref: '#/definitions/zetachain.zetacore.crosschain.CallOptions' + confirmationMode: + $ref: '#/definitions/zetachain.zetacore.crosschain.ConfirmationMode' title: confirmation mode used for the outbound - crosschainOutboundTracker: + zetachain.zetacore.crosschain.OutboundTracker: type: object properties: index: type: string title: 'format: "chain-nonce"' - chain_id: + chainId: type: string format: int64 nonce: type: string format: uint64 - hash_list: + hashList: type: array items: type: object - $ref: '#/definitions/crosschainTxHash' - crosschainProtocolContractVersion: + $ref: '#/definitions/zetachain.zetacore.crosschain.TxHash' + zetachain.zetacore.crosschain.ProtocolContractVersion: type: string enum: - V1 @@ -59212,87 +57812,87 @@ definitions: title: |- ProtocolContractVersion represents the version of the protocol contract used for cctx workflow - crosschainQueryAllCctxResponse: + zetachain.zetacore.crosschain.QueryAllCctxResponse: type: object properties: CrossChainTx: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllGasPriceResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllGasPriceResponse: type: object properties: GasPrice: type: array items: type: object - $ref: '#/definitions/crosschainGasPrice' + $ref: '#/definitions/zetachain.zetacore.crosschain.GasPrice' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllInboundHashToCctxResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllInboundHashToCctxResponse: type: object properties: inboundHashToCctx: type: array items: type: object - $ref: '#/definitions/crosschainInboundHashToCctx' + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundHashToCctx' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllInboundTrackerByChainResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllInboundTrackerByChainResponse: type: object properties: inboundTracker: type: array items: type: object - $ref: '#/definitions/crosschainInboundTracker' + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundTracker' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllInboundTrackersResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllInboundTrackersResponse: type: object properties: inboundTracker: type: array items: type: object - $ref: '#/definitions/crosschainInboundTracker' + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundTracker' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllLastBlockHeightResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllLastBlockHeightResponse: type: object properties: LastBlockHeight: type: array items: type: object - $ref: '#/definitions/crosschainLastBlockHeight' + $ref: '#/definitions/zetachain.zetacore.crosschain.LastBlockHeight' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllOutboundTrackerByChainResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllOutboundTrackerByChainResponse: type: object properties: outboundTracker: type: array items: type: object - $ref: '#/definitions/crosschainOutboundTracker' + $ref: '#/definitions/zetachain.zetacore.crosschain.OutboundTracker' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryAllOutboundTrackerResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryAllOutboundTrackerResponse: type: object properties: outboundTracker: type: array items: type: object - $ref: '#/definitions/crosschainOutboundTracker' + $ref: '#/definitions/zetachain.zetacore.crosschain.OutboundTracker' pagination: - $ref: '#/definitions/v1beta1PageResponse' - crosschainQueryConvertGasToZetaResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.crosschain.QueryConvertGasToZetaResponse: type: object properties: outboundGasInZeta: @@ -59302,121 +57902,121 @@ definitions: ZetaBlockHeight: type: string format: uint64 - crosschainQueryGetCctxResponse: + zetachain.zetacore.crosschain.QueryGetCctxResponse: type: object properties: CrossChainTx: - $ref: '#/definitions/crosschainCrossChainTx' - crosschainQueryGetGasPriceResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' + zetachain.zetacore.crosschain.QueryGetGasPriceResponse: type: object properties: GasPrice: - $ref: '#/definitions/crosschainGasPrice' - crosschainQueryGetInboundHashToCctxResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.GasPrice' + zetachain.zetacore.crosschain.QueryGetInboundHashToCctxResponse: type: object properties: inboundHashToCctx: - $ref: '#/definitions/crosschainInboundHashToCctx' - crosschainQueryGetLastBlockHeightResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundHashToCctx' + zetachain.zetacore.crosschain.QueryGetLastBlockHeightResponse: type: object properties: LastBlockHeight: - $ref: '#/definitions/crosschainLastBlockHeight' - crosschainQueryGetOutboundTrackerResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.LastBlockHeight' + zetachain.zetacore.crosschain.QueryGetOutboundTrackerResponse: type: object properties: outboundTracker: - $ref: '#/definitions/crosschainOutboundTracker' - crosschainQueryInboundHashToCctxDataResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.OutboundTracker' + zetachain.zetacore.crosschain.QueryInboundHashToCctxDataResponse: type: object properties: CrossChainTxs: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' - crosschainQueryInboundTrackerResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' + zetachain.zetacore.crosschain.QueryInboundTrackerResponse: type: object properties: - inbound_tracker: - $ref: '#/definitions/crosschainInboundTracker' - crosschainQueryLastZetaHeightResponse: + inboundTracker: + $ref: '#/definitions/zetachain.zetacore.crosschain.InboundTracker' + zetachain.zetacore.crosschain.QueryLastZetaHeightResponse: type: object properties: Height: type: string format: int64 - crosschainQueryListPendingCctxResponse: + zetachain.zetacore.crosschain.QueryListPendingCctxResponse: type: object properties: CrossChainTx: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' totalPending: type: string format: uint64 - crosschainQueryListPendingCctxWithinRateLimitResponse: + zetachain.zetacore.crosschain.QueryListPendingCctxWithinRateLimitResponse: type: object properties: - cross_chain_tx: + crossChainTx: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' - total_pending: + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' + totalPending: type: string format: uint64 - current_withdraw_window: + currentWithdrawWindow: type: string format: int64 - current_withdraw_rate: + currentWithdrawRate: type: string - rate_limit_exceeded: + rateLimitExceeded: type: boolean - crosschainQueryMessagePassingProtocolFeeResponse: + zetachain.zetacore.crosschain.QueryMessagePassingProtocolFeeResponse: type: object properties: feeInZeta: type: string - crosschainQueryRateLimiterFlagsResponse: + zetachain.zetacore.crosschain.QueryRateLimiterFlagsResponse: type: object properties: rateLimiterFlags: - $ref: '#/definitions/crosschainRateLimiterFlags' - crosschainQueryRateLimiterInputResponse: + $ref: '#/definitions/zetachain.zetacore.crosschain.RateLimiterFlags' + zetachain.zetacore.crosschain.QueryRateLimiterInputResponse: type: object properties: height: type: string format: int64 - cctxs_missed: + cctxsMissed: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' - cctxs_pending: + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' + cctxsPending: type: array items: type: object - $ref: '#/definitions/crosschainCrossChainTx' - total_pending: + $ref: '#/definitions/zetachain.zetacore.crosschain.CrossChainTx' + totalPending: type: string format: uint64 - past_cctxs_value: + pastCctxsValue: type: string - pending_cctxs_value: + pendingCctxsValue: type: string - lowest_pending_cctx_height: + lowestPendingCctxHeight: type: string format: int64 - crosschainQueryZetaAccountingResponse: + zetachain.zetacore.crosschain.QueryZetaAccountingResponse: type: object properties: - aborted_zeta_amount: + abortedZetaAmount: type: string - crosschainRateLimiterFlags: + zetachain.zetacore.crosschain.RateLimiterFlags: type: object properties: enabled: @@ -59432,24 +58032,56 @@ definitions: type: array items: type: object - $ref: '#/definitions/crosschainConversion' + $ref: '#/definitions/zetachain.zetacore.crosschain.Conversion' title: conversion in azeta per token - crosschainRevertOptions: + zetachain.zetacore.crosschain.RevertOptions: type: object properties: - revert_address: + revertAddress: type: string - call_on_revert: + callOnRevert: type: boolean - abort_address: + abortAddress: type: string - revert_message: + revertMessage: type: string format: byte - revert_gas_limit: + revertGasLimit: type: string title: RevertOptions represents the options for reverting a cctx - crosschainTxFinalizationStatus: + zetachain.zetacore.crosschain.Status: + type: object + properties: + status: + $ref: '#/definitions/zetachain.zetacore.crosschain.CctxStatus' + statusMessage: + type: string + description: |- + status_message carries information about the status transitions: + why they were triggered, old and new status. + errorMessage: + type: string + description: |- + error_message carries information about the error that caused the tx + to be PendingRevert, Reverted or Aborted. + lastUpdateTimestamp: + type: string + format: int64 + isAbortRefunded: + type: boolean + createdTimestamp: + type: string + format: int64 + description: when the CCTX was created. only populated on new transactions. + errorMessageRevert: + type: string + title: |- + error_message_revert carries information about the revert outbound tx , + which is created if the first outbound tx fails + errorMessageAbort: + type: string + title: error_message_abort carries information when aborting the CCTX fails + zetachain.zetacore.crosschain.TxFinalizationStatus: type: string enum: - NotFinalized @@ -59460,58 +58092,75 @@ definitions: - NotFinalized: the corresponding tx is not finalized - Finalized: the corresponding tx is finalized but not executed yet - Executed: the corresponding tx is executed - crosschainTxHash: + zetachain.zetacore.crosschain.TxHash: type: object properties: - tx_hash: + txHash: type: string - tx_signer: + txSigner: type: string proven: type: boolean - cryptoPubKeySet: + zetachain.zetacore.emissions.MsgUpdateParamsResponse: + type: object + zetachain.zetacore.emissions.MsgWithdrawEmissionResponse: + type: object + zetachain.zetacore.emissions.Params: type: object properties: - secp256k1: + validatorEmissionPercentage: type: string - ed25519: + observerEmissionPercentage: type: string - title: PubKeySet contains two pub keys , secp256k1 and ed25519 - emissionsMsgUpdateParamsResponse: - type: object - emissionsMsgWithdrawEmissionResponse: - type: object - emissionsQueryListPoolAddressesResponse: + tssSignerEmissionPercentage: + type: string + observerSlashAmount: + type: string + ballotMaturityBlocks: + type: string + format: int64 + blockRewardAmount: + type: string + title: |- + Params defines the parameters for the module. + Sample values: + ValidatorEmissionPercentage: "00.50", + ObserverEmissionPercentage: "00.25", + TssSignerEmissionPercentage: "00.25", + ObserverSlashAmount: 100000000000000000, + BallotMaturityBlocks: 100, + BlockRewardAmount: 9620949074074074074.074070733466756687, + zetachain.zetacore.emissions.QueryListPoolAddressesResponse: type: object properties: - undistributed_observer_balances_address: + undistributedObserverBalancesAddress: type: string - undistributed_tss_balances_address: + undistributedTssBalancesAddress: type: string - emission_module_address: + emissionModuleAddress: type: string - emissionsQueryParamsResponse: + zetachain.zetacore.emissions.QueryParamsResponse: type: object properties: params: - $ref: '#/definitions/zetacoreemissionsParams' + $ref: '#/definitions/zetachain.zetacore.emissions.Params' description: params holds all the parameters of this module. description: QueryParamsResponse is response type for the Query/Params RPC method. - emissionsQueryShowAvailableEmissionsResponse: + zetachain.zetacore.emissions.QueryShowAvailableEmissionsResponse: type: object properties: amount: type: string - fungibleForeignCoins: + zetachain.zetacore.fungible.ForeignCoins: type: object properties: - zrc20_contract_address: + zrc20ContractAddress: type: string description: index title: string index = 1; asset: type: string - foreign_chain_id: + foreignChainId: type: string format: int64 decimals: @@ -59521,21 +58170,21 @@ definitions: type: string symbol: type: string - coin_type: - $ref: '#/definitions/coinCoinType' - gas_limit: + coinType: + $ref: '#/definitions/zetachain.zetacore.pkg.coin.CoinType' + gasLimit: type: string format: uint64 paused: type: boolean - liquidity_cap: + liquidityCap: type: string - fungibleMsgDeployFungibleCoinZRC20Response: + zetachain.zetacore.fungible.MsgDeployFungibleCoinZRC20Response: type: object properties: address: type: string - fungibleMsgDeploySystemContractsResponse: + zetachain.zetacore.fungible.MsgDeploySystemContractsResponse: type: object properties: uniswapV2Factory: @@ -59548,111 +58197,106 @@ definitions: type: string systemContract: type: string - fungibleMsgPauseZRC20Response: + zetachain.zetacore.fungible.MsgPauseZRC20Response: type: object - fungibleMsgRemoveForeignCoinResponse: + zetachain.zetacore.fungible.MsgRemoveForeignCoinResponse: type: object - fungibleMsgUnpauseZRC20Response: + zetachain.zetacore.fungible.MsgUnpauseZRC20Response: type: object - fungibleMsgUpdateContractBytecodeResponse: + zetachain.zetacore.fungible.MsgUpdateContractBytecodeResponse: type: object - fungibleMsgUpdateGatewayContractResponse: + zetachain.zetacore.fungible.MsgUpdateGatewayContractResponse: type: object - fungibleMsgUpdateSystemContractResponse: + zetachain.zetacore.fungible.MsgUpdateSystemContractResponse: type: object - fungibleMsgUpdateZRC20LiquidityCapResponse: + zetachain.zetacore.fungible.MsgUpdateZRC20LiquidityCapResponse: type: object - fungibleMsgUpdateZRC20NameResponse: + zetachain.zetacore.fungible.MsgUpdateZRC20NameResponse: type: object - fungibleMsgUpdateZRC20WithdrawFeeResponse: + zetachain.zetacore.fungible.MsgUpdateZRC20WithdrawFeeResponse: type: object - fungibleQueryAllForeignCoinsResponse: + zetachain.zetacore.fungible.QueryAllForeignCoinsResponse: type: object properties: foreignCoins: type: array items: type: object - $ref: '#/definitions/fungibleForeignCoins' + $ref: '#/definitions/zetachain.zetacore.fungible.ForeignCoins' pagination: - $ref: '#/definitions/v1beta1PageResponse' - fungibleQueryAllGasStabilityPoolBalanceResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.fungible.QueryAllGasStabilityPoolBalanceResponse: type: object properties: balances: type: array items: type: object - $ref: '#/definitions/QueryAllGasStabilityPoolBalanceResponseBalance' - fungibleQueryCodeHashResponse: + $ref: '#/definitions/zetachain.zetacore.fungible.QueryAllGasStabilityPoolBalanceResponse.Balance' + zetachain.zetacore.fungible.QueryAllGasStabilityPoolBalanceResponse.Balance: type: object properties: - code_hash: + chainId: + type: string + format: int64 + balance: + type: string + zetachain.zetacore.fungible.QueryCodeHashResponse: + type: object + properties: + codeHash: type: string - fungibleQueryGetForeignCoinsResponse: + zetachain.zetacore.fungible.QueryGetForeignCoinsResponse: type: object properties: foreignCoins: - $ref: '#/definitions/fungibleForeignCoins' - fungibleQueryGetGasStabilityPoolAddressResponse: + $ref: '#/definitions/zetachain.zetacore.fungible.ForeignCoins' + zetachain.zetacore.fungible.QueryGetGasStabilityPoolAddressResponse: type: object properties: - cosmos_address: + cosmosAddress: type: string - evm_address: + evmAddress: type: string - fungibleQueryGetGasStabilityPoolBalanceResponse: + zetachain.zetacore.fungible.QueryGetGasStabilityPoolBalanceResponse: type: object properties: balance: type: string - fungibleQueryGetSystemContractResponse: + zetachain.zetacore.fungible.QueryGetSystemContractResponse: type: object properties: SystemContract: - $ref: '#/definitions/fungibleSystemContract' - fungibleSystemContract: + $ref: '#/definitions/zetachain.zetacore.fungible.SystemContract' + zetachain.zetacore.fungible.SystemContract: type: object properties: - system_contract: + systemContract: type: string - connector_zevm: + connectorZevm: type: string gateway: type: string - googlerpcStatus: - type: object - properties: - code: - type: integer - format: int32 - message: - type: string - details: - type: array - items: - type: object - $ref: '#/definitions/protobufAny' - lightclientChainState: + zetachain.zetacore.lightclient.ChainState: type: object properties: - chain_id: + chainId: type: string format: int64 - latest_height: + latestHeight: type: string format: int64 - earliest_height: + earliestHeight: type: string format: int64 - latest_block_hash: + latestBlockHash: type: string format: byte title: ChainState defines the overall state of the block headers for a given chain - lightclientHeaderSupportedChain: + zetachain.zetacore.lightclient.HeaderSupportedChain: type: object properties: - chain_id: + chainId: type: string format: int64 enabled: @@ -59660,106 +58304,106 @@ definitions: title: |- HeaderSupportedChain is a structure containing information of weather a chain is enabled or not for block header verification - lightclientMsgDisableHeaderVerificationResponse: + zetachain.zetacore.lightclient.MsgDisableHeaderVerificationResponse: type: object - lightclientMsgEnableHeaderVerificationResponse: + zetachain.zetacore.lightclient.MsgEnableHeaderVerificationResponse: type: object - lightclientQueryAllBlockHeaderResponse: + zetachain.zetacore.lightclient.QueryAllBlockHeaderResponse: type: object properties: - block_headers: + blockHeaders: type: array items: type: object - $ref: '#/definitions/proofsBlockHeader' + $ref: '#/definitions/zetachain.zetacore.pkg.proofs.BlockHeader' pagination: - $ref: '#/definitions/v1beta1PageResponse' - lightclientQueryAllChainStateResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.lightclient.QueryAllChainStateResponse: type: object properties: - chain_state: + chainState: type: array items: type: object - $ref: '#/definitions/lightclientChainState' + $ref: '#/definitions/zetachain.zetacore.lightclient.ChainState' pagination: - $ref: '#/definitions/v1beta1PageResponse' - lightclientQueryGetBlockHeaderResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.lightclient.QueryGetBlockHeaderResponse: type: object properties: - block_header: - $ref: '#/definitions/proofsBlockHeader' - lightclientQueryGetChainStateResponse: + blockHeader: + $ref: '#/definitions/zetachain.zetacore.pkg.proofs.BlockHeader' + zetachain.zetacore.lightclient.QueryGetChainStateResponse: type: object properties: - chain_state: - $ref: '#/definitions/lightclientChainState' - lightclientQueryHeaderEnabledChainsResponse: + chainState: + $ref: '#/definitions/zetachain.zetacore.lightclient.ChainState' + zetachain.zetacore.lightclient.QueryHeaderEnabledChainsResponse: type: object properties: - header_enabled_chains: + headerEnabledChains: type: array items: type: object - $ref: '#/definitions/lightclientHeaderSupportedChain' - lightclientQueryHeaderSupportedChainsResponse: + $ref: '#/definitions/zetachain.zetacore.lightclient.HeaderSupportedChain' + zetachain.zetacore.lightclient.QueryHeaderSupportedChainsResponse: type: object properties: - header_supported_chains: + headerSupportedChains: type: array items: type: object - $ref: '#/definitions/lightclientHeaderSupportedChain' - lightclientQueryProveResponse: + $ref: '#/definitions/zetachain.zetacore.lightclient.HeaderSupportedChain' + zetachain.zetacore.lightclient.QueryProveResponse: type: object properties: valid: type: boolean - observerBallot: + zetachain.zetacore.observer.Ballot: type: object properties: index: type: string - ballot_identifier: + ballotIdentifier: type: string - voter_list: + voterList: type: array items: type: string votes: type: array items: - $ref: '#/definitions/observerVoteType' - observation_type: - $ref: '#/definitions/observerObservationType' - ballot_threshold: + $ref: '#/definitions/zetachain.zetacore.observer.VoteType' + observationType: + $ref: '#/definitions/zetachain.zetacore.observer.ObservationType' + ballotThreshold: type: string - ballot_status: - $ref: '#/definitions/observerBallotStatus' - ballot_creation_height: + ballotStatus: + $ref: '#/definitions/zetachain.zetacore.observer.BallotStatus' + ballotCreationHeight: type: string format: int64 title: https://github.com/zeta-chain/node/issues/939 - observerBallotStatus: + zetachain.zetacore.observer.BallotStatus: type: string enum: - BallotFinalized_SuccessObservation - BallotFinalized_FailureObservation - BallotInProgress default: BallotFinalized_SuccessObservation - observerBlame: + zetachain.zetacore.observer.Blame: type: object properties: index: type: string - failure_reason: + failureReason: type: string nodes: type: array items: type: object - $ref: '#/definitions/observerNode' - observerChainNonces: + $ref: '#/definitions/zetachain.zetacore.observer.Node' + zetachain.zetacore.observer.ChainNonces: type: object properties: creator: @@ -59767,7 +58411,7 @@ definitions: index: type: string title: 'deprecated(v19): index has been replaced by chain_id for unique identifier' - chain_id: + chainId: type: string format: int64 nonce: @@ -59780,87 +58424,87 @@ definitions: finalizedHeight: type: string format: uint64 - observerChainParams: + zetachain.zetacore.observer.ChainParams: type: object properties: - chain_id: + chainId: type: string format: int64 - confirmation_count: + confirmationCount: type: string format: uint64 title: 'Deprecated(v28): use confirmation_params instead' - gas_price_ticker: + gasPriceTicker: type: string format: uint64 - inbound_ticker: + inboundTicker: type: string format: uint64 - outbound_ticker: + outboundTicker: type: string format: uint64 - watch_utxo_ticker: + watchUtxoTicker: type: string format: uint64 - zeta_token_contract_address: + zetaTokenContractAddress: type: string - connector_contract_address: + connectorContractAddress: type: string - erc20_custody_contract_address: + erc20CustodyContractAddress: type: string - outbound_schedule_interval: + outboundScheduleInterval: type: string format: int64 - outbound_schedule_lookahead: + outboundScheduleLookahead: type: string format: int64 - ballot_threshold: + ballotThreshold: type: string - min_observer_delegation: + minObserverDelegation: type: string - is_supported: + isSupported: type: boolean - gateway_address: + gatewayAddress: type: string - confirmation_params: - $ref: '#/definitions/observerConfirmationParams' + confirmationParams: + $ref: '#/definitions/zetachain.zetacore.observer.ConfirmationParams' title: Advanced confirmation parameters for chain to support fast observation - observerChainParamsList: + zetachain.zetacore.observer.ChainParamsList: type: object properties: - chain_params: + chainParams: type: array items: type: object - $ref: '#/definitions/observerChainParams' - observerConfirmationParams: + $ref: '#/definitions/zetachain.zetacore.observer.ChainParams' + zetachain.zetacore.observer.ConfirmationParams: type: object properties: - safe_inbound_count: + safeInboundCount: type: string format: uint64 description: |- This is the safe number of confirmations to wait before an inbound is considered finalized. - fast_inbound_count: + fastInboundCount: type: string format: uint64 description: |- This is the number of confirmations for fast inbound observation, which is shorter than safe_inbound_count. - safe_outbound_count: + safeOutboundCount: type: string format: uint64 description: |- This is the safe number of confirmations to wait before an outbound is considered finalized. - fast_outbound_count: + fastOutboundCount: type: string format: uint64 description: |- This is the number of confirmations for fast outbound observation, which is shorter than safe_outbound_count. - observerCrosschainFlags: + zetachain.zetacore.observer.CrosschainFlags: type: object properties: isInboundEnabled: @@ -59868,8 +58512,8 @@ definitions: isOutboundEnabled: type: boolean gasPriceIncreaseFlags: - $ref: '#/definitions/observerGasPriceIncreaseFlags' - observerGasPriceIncreaseFlags: + $ref: '#/definitions/zetachain.zetacore.observer.GasPriceIncreaseFlags' + zetachain.zetacore.observer.GasPriceIncreaseFlags: type: object properties: epochLength: @@ -59892,11 +58536,11 @@ definitions: title: |- Maximum number of pending crosschain transactions to check for gas price increase - observerKeygen: + zetachain.zetacore.observer.Keygen: type: object properties: status: - $ref: '#/definitions/observerKeygenStatus' + $ref: '#/definitions/zetachain.zetacore.observer.KeygenStatus' title: 0--to generate key; 1--generated; 2--error granteePubkeys: type: array @@ -59906,76 +58550,76 @@ definitions: type: string format: int64 title: the blocknum that the key needs to be generated - observerKeygenStatus: + zetachain.zetacore.observer.KeygenStatus: type: string enum: - PendingKeygen - KeyGenSuccess - KeyGenFailed default: PendingKeygen - observerLastObserverCount: + zetachain.zetacore.observer.LastObserverCount: type: object properties: count: type: string format: uint64 - last_change_height: + lastChangeHeight: type: string format: int64 - observerMsgAddObserverResponse: + zetachain.zetacore.observer.MsgAddObserverResponse: type: object - observerMsgDisableCCTXResponse: + zetachain.zetacore.observer.MsgDisableCCTXResponse: type: object - observerMsgDisableFastConfirmationResponse: + zetachain.zetacore.observer.MsgDisableFastConfirmationResponse: type: object - observerMsgEnableCCTXResponse: + zetachain.zetacore.observer.MsgEnableCCTXResponse: type: object - observerMsgRemoveChainParamsResponse: + zetachain.zetacore.observer.MsgRemoveChainParamsResponse: type: object - observerMsgResetChainNoncesResponse: + zetachain.zetacore.observer.MsgResetChainNoncesResponse: type: object - observerMsgUpdateChainParamsResponse: + zetachain.zetacore.observer.MsgUpdateChainParamsResponse: type: object - observerMsgUpdateGasPriceIncreaseFlagsResponse: + zetachain.zetacore.observer.MsgUpdateGasPriceIncreaseFlagsResponse: type: object - observerMsgUpdateKeygenResponse: + zetachain.zetacore.observer.MsgUpdateKeygenResponse: type: object - observerMsgUpdateObserverResponse: + zetachain.zetacore.observer.MsgUpdateObserverResponse: type: object - observerMsgUpdateOperationalChainParamsResponse: + zetachain.zetacore.observer.MsgUpdateOperationalChainParamsResponse: type: object - observerMsgUpdateOperationalFlagsResponse: + zetachain.zetacore.observer.MsgUpdateOperationalFlagsResponse: type: object - observerMsgVoteBlameResponse: + zetachain.zetacore.observer.MsgVoteBlameResponse: type: object - observerMsgVoteBlockHeaderResponse: + zetachain.zetacore.observer.MsgVoteBlockHeaderResponse: type: object properties: - ballot_created: + ballotCreated: type: boolean - vote_finalized: + voteFinalized: type: boolean - observerMsgVoteTSSResponse: + zetachain.zetacore.observer.MsgVoteTSSResponse: type: object properties: - ballot_created: + ballotCreated: type: boolean - vote_finalized: + voteFinalized: type: boolean - keygen_success: + keygenSuccess: type: boolean - observerNode: + zetachain.zetacore.observer.Node: type: object properties: - pub_key: + pubKey: type: string - blame_data: + blameData: type: string format: byte - blame_signature: + blameSignature: type: string format: byte - observerNodeAccount: + zetachain.zetacore.observer.NodeAccount: type: object properties: operator: @@ -59983,10 +58627,10 @@ definitions: granteeAddress: type: string granteePubkey: - $ref: '#/definitions/cryptoPubKeySet' + $ref: '#/definitions/zetachain.zetacore.pkg.crypto.PubKeySet' nodeStatus: - $ref: '#/definitions/observerNodeStatus' - observerNodeStatus: + $ref: '#/definitions/zetachain.zetacore.observer.NodeStatus' + zetachain.zetacore.observer.NodeStatus: type: string enum: - Unknown @@ -59996,7 +58640,7 @@ definitions: - Active - Disabled default: Unknown - observerObservationType: + zetachain.zetacore.observer.ObservationType: type: string enum: - EmptyObserverType @@ -60005,243 +58649,243 @@ definitions: - TSSKeyGen - TSSKeySign default: EmptyObserverType - observerObserverUpdateReason: + zetachain.zetacore.observer.ObserverUpdateReason: type: string enum: - Undefined - Tombstoned - AdminUpdate default: Undefined - observerOperationalFlags: + zetachain.zetacore.observer.OperationalFlags: type: object properties: - restart_height: + restartHeight: type: string format: int64 description: |- Height for a coordinated zetaclient restart. Will be ignored if missed. - signer_block_time_offset: + signerBlockTimeOffset: type: string description: |- Offset from the zetacore block time to initiate signing. Should be calculated and set based on max(zetaclient_core_block_latency). - minimum_version: + minimumVersion: type: string description: |- Minimum version of zetaclient that is allowed to run. This must be either a valid semver string (v23.0.1) or empty. If empty, all versions are allowed. description: Flags for the top-level operation of zetaclient. - observerPendingNonces: + zetachain.zetacore.observer.PendingNonces: type: object properties: - nonce_low: + nonceLow: type: string format: int64 - nonce_high: + nonceHigh: type: string format: int64 - chain_id: + chainId: type: string format: int64 tss: type: string title: store key is tss+chainid - observerQueryAllBlameRecordsResponse: + zetachain.zetacore.observer.QueryAllBlameRecordsResponse: type: object properties: - blame_info: + blameInfo: type: array items: type: object - $ref: '#/definitions/observerBlame' + $ref: '#/definitions/zetachain.zetacore.observer.Blame' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerQueryAllChainNoncesResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.QueryAllChainNoncesResponse: type: object properties: ChainNonces: type: array items: type: object - $ref: '#/definitions/observerChainNonces' + $ref: '#/definitions/zetachain.zetacore.observer.ChainNonces' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerQueryAllNodeAccountResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.QueryAllNodeAccountResponse: type: object properties: NodeAccount: type: array items: type: object - $ref: '#/definitions/observerNodeAccount' + $ref: '#/definitions/zetachain.zetacore.observer.NodeAccount' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerQueryAllPendingNoncesResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.QueryAllPendingNoncesResponse: type: object properties: - pending_nonces: + pendingNonces: type: array items: type: object - $ref: '#/definitions/observerPendingNonces' + $ref: '#/definitions/zetachain.zetacore.observer.PendingNonces' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerQueryBallotByIdentifierResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.QueryBallotByIdentifierResponse: type: object properties: - ballot_identifier: + ballotIdentifier: type: string voters: type: array items: type: object - $ref: '#/definitions/observerVoterList' - observation_type: - $ref: '#/definitions/observerObservationType' - ballot_status: - $ref: '#/definitions/observerBallotStatus' - observerQueryBallotsResponse: + $ref: '#/definitions/zetachain.zetacore.observer.VoterList' + observationType: + $ref: '#/definitions/zetachain.zetacore.observer.ObservationType' + ballotStatus: + $ref: '#/definitions/zetachain.zetacore.observer.BallotStatus' + zetachain.zetacore.observer.QueryBallotsResponse: type: object properties: ballots: type: array items: type: object - $ref: '#/definitions/observerBallot' + $ref: '#/definitions/zetachain.zetacore.observer.Ballot' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerQueryBlameByChainAndNonceResponse: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.QueryBlameByChainAndNonceResponse: type: object properties: - blame_info: + blameInfo: type: array items: type: object - $ref: '#/definitions/observerBlame' - observerQueryBlameByIdentifierResponse: + $ref: '#/definitions/zetachain.zetacore.observer.Blame' + zetachain.zetacore.observer.QueryBlameByIdentifierResponse: type: object properties: - blame_info: - $ref: '#/definitions/observerBlame' - observerQueryGetChainNoncesResponse: + blameInfo: + $ref: '#/definitions/zetachain.zetacore.observer.Blame' + zetachain.zetacore.observer.QueryGetChainNoncesResponse: type: object properties: ChainNonces: - $ref: '#/definitions/observerChainNonces' - observerQueryGetChainParamsForChainResponse: + $ref: '#/definitions/zetachain.zetacore.observer.ChainNonces' + zetachain.zetacore.observer.QueryGetChainParamsForChainResponse: type: object properties: - chain_params: - $ref: '#/definitions/observerChainParams' - observerQueryGetChainParamsResponse: + chainParams: + $ref: '#/definitions/zetachain.zetacore.observer.ChainParams' + zetachain.zetacore.observer.QueryGetChainParamsResponse: type: object properties: - chain_params: - $ref: '#/definitions/observerChainParamsList' - observerQueryGetCrosschainFlagsResponse: + chainParams: + $ref: '#/definitions/zetachain.zetacore.observer.ChainParamsList' + zetachain.zetacore.observer.QueryGetCrosschainFlagsResponse: type: object properties: - crosschain_flags: - $ref: '#/definitions/observerCrosschainFlags' - observerQueryGetKeygenResponse: + crosschainFlags: + $ref: '#/definitions/zetachain.zetacore.observer.CrosschainFlags' + zetachain.zetacore.observer.QueryGetKeygenResponse: type: object properties: keygen: - $ref: '#/definitions/observerKeygen' - observerQueryGetNodeAccountResponse: + $ref: '#/definitions/zetachain.zetacore.observer.Keygen' + zetachain.zetacore.observer.QueryGetNodeAccountResponse: type: object properties: - node_account: - $ref: '#/definitions/observerNodeAccount' - observerQueryGetTSSResponse: + nodeAccount: + $ref: '#/definitions/zetachain.zetacore.observer.NodeAccount' + zetachain.zetacore.observer.QueryGetTSSResponse: type: object properties: TSS: - $ref: '#/definitions/observerTSS' - observerQueryGetTssAddressByFinalizedHeightResponse: + $ref: '#/definitions/zetachain.zetacore.observer.TSS' + zetachain.zetacore.observer.QueryGetTssAddressByFinalizedHeightResponse: type: object properties: eth: type: string btc: type: string - observerQueryGetTssAddressResponse: + zetachain.zetacore.observer.QueryGetTssAddressResponse: type: object properties: eth: type: string btc: type: string - observerQueryHasVotedResponse: + zetachain.zetacore.observer.QueryHasVotedResponse: type: object properties: - has_voted: + hasVoted: type: boolean - observerQueryObserverSetResponse: + zetachain.zetacore.observer.QueryObserverSetResponse: type: object properties: observers: type: array items: type: string - observerQueryOperationalFlagsResponse: + zetachain.zetacore.observer.QueryOperationalFlagsResponse: type: object properties: - operational_flags: - $ref: '#/definitions/observerOperationalFlags' - observerQueryPendingNoncesByChainResponse: + operationalFlags: + $ref: '#/definitions/zetachain.zetacore.observer.OperationalFlags' + zetachain.zetacore.observer.QueryPendingNoncesByChainResponse: type: object properties: - pending_nonces: - $ref: '#/definitions/observerPendingNonces' - observerQueryShowObserverCountResponse: + pendingNonces: + $ref: '#/definitions/zetachain.zetacore.observer.PendingNonces' + zetachain.zetacore.observer.QueryShowObserverCountResponse: type: object properties: - last_observer_count: - $ref: '#/definitions/observerLastObserverCount' - observerQuerySupportedChainsResponse: + lastObserverCount: + $ref: '#/definitions/zetachain.zetacore.observer.LastObserverCount' + zetachain.zetacore.observer.QuerySupportedChainsResponse: type: object properties: chains: type: array items: type: object - $ref: '#/definitions/chainsChain' - observerQueryTssFundsMigratorInfoAllResponse: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.Chain' + zetachain.zetacore.observer.QueryTssFundsMigratorInfoAllResponse: type: object properties: - tss_funds_migrators: + tssFundsMigrators: type: array items: type: object - $ref: '#/definitions/observerTssFundMigratorInfo' - observerQueryTssFundsMigratorInfoResponse: + $ref: '#/definitions/zetachain.zetacore.observer.TssFundMigratorInfo' + zetachain.zetacore.observer.QueryTssFundsMigratorInfoResponse: type: object properties: - tss_funds_migrator: - $ref: '#/definitions/observerTssFundMigratorInfo' - observerQueryTssHistoryResponse: + tssFundsMigrator: + $ref: '#/definitions/zetachain.zetacore.observer.TssFundMigratorInfo' + zetachain.zetacore.observer.QueryTssHistoryResponse: type: object properties: - tss_list: + tssList: type: array items: type: object - $ref: '#/definitions/observerTSS' + $ref: '#/definitions/zetachain.zetacore.observer.TSS' pagination: - $ref: '#/definitions/v1beta1PageResponse' - observerTSS: + $ref: '#/definitions/cosmos.base.query.v1beta1.PageResponse' + zetachain.zetacore.observer.TSS: type: object properties: - tss_pubkey: + tssPubkey: type: string - tss_participant_list: + tssParticipantList: type: array items: type: string - operator_address_list: + operatorAddressList: type: array items: type: string @@ -60251,15 +58895,15 @@ definitions: keyGenZetaHeight: type: string format: int64 - observerTssFundMigratorInfo: + zetachain.zetacore.observer.TssFundMigratorInfo: type: object properties: - chain_id: + chainId: type: string format: int64 - migration_cctx_index: + migrationCctxIndex: type: string - observerVoteType: + zetachain.zetacore.observer.VoteType: type: string enum: - SuccessObservation @@ -60270,21 +58914,189 @@ definitions: - FailureObservation: Failure observation means , the the message that - NotYetVoted: this voter is observing failed / reverted . It does not mean it was unable to observe. - observerVoterList: + zetachain.zetacore.observer.VoterList: type: object properties: - voter_address: + voterAddress: type: string - vote_type: - $ref: '#/definitions/observerVoteType' - pkgproofsProof: + voteType: + $ref: '#/definitions/zetachain.zetacore.observer.VoteType' + zetachain.zetacore.pkg.chains.CCTXGateway: + type: string + enum: + - zevm + - observers + default: zevm + description: |- + - zevm: zevm is the internal CCTX gateway to process outbound on the ZEVM and read + inbound events from the ZEVM only used for ZetaChain chains + - observers: observers is the CCTX gateway for chains relying on the observer set to + observe inbounds and TSS for outbounds + title: CCTXGateway describes for the chain the gateway used to handle CCTX outbounds + zetachain.zetacore.pkg.chains.Chain: + type: object + properties: + chainId: + type: string + format: int64 + title: ChainId is the unique identifier of the chain + chainName: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.ChainName' + title: |- + ChainName is the name of the chain + Deprecated(v19): replaced with Name + network: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.Network' + title: Network is the network of the chain + networkType: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.NetworkType' + description: 'NetworkType is the network type of the chain: mainnet, testnet, etc..' + vm: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.Vm' + title: Vm is the virtual machine used in the chain + consensus: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.Consensus' + title: Consensus is the underlying consensus algorithm used by the chain + isExternal: + type: boolean + title: IsExternal describe if the chain is ZetaChain or external + cctxGateway: + $ref: '#/definitions/zetachain.zetacore.pkg.chains.CCTXGateway' + title: CCTXGateway is the gateway used to handle CCTX outbounds + name: + type: string + title: Name is the name of the chain + title: |- + Chain represents static data about a blockchain network + it is identified by a unique chain ID + zetachain.zetacore.pkg.chains.ChainName: + type: string + enum: + - empty + - eth_mainnet + - zeta_mainnet + - btc_mainnet + - polygon_mainnet + - bsc_mainnet + - goerli_testnet + - mumbai_testnet + - bsc_testnet + - zeta_testnet + - btc_testnet + - sepolia_testnet + - goerli_localnet + - btc_regtest + - amoy_testnet + - optimism_mainnet + - optimism_sepolia + - base_mainnet + - base_sepolia + - solana_mainnet + - solana_devnet + - solana_localnet + default: empty + title: |- + ChainName represents the name of the chain + Deprecated(v19): replaced with Chain.Name as string + zetachain.zetacore.pkg.chains.Consensus: + type: string + enum: + - ethereum + - tendermint + - bitcoin + - op_stack + - solana_consensus + - catchain_consensus + - snowman + - arbitrum_nitro + - sui_consensus + default: ethereum + description: |- + - catchain_consensus: ton + - snowman: avalanche + title: |- + Consensus represents the consensus algorithm used by the chain + this can represent the consensus of a L1 + this can also represent the solution of a L2 + zetachain.zetacore.pkg.chains.Network: + type: string + enum: + - eth + - zeta + - btc + - polygon + - bsc + - optimism + - base + - solana + - ton + - avalanche + - arbitrum + - worldchain + - sui + default: eth + title: |- + Network represents the network of the chain + there is a single instance of the network on mainnet + then the network can have eventual testnets or devnets + zetachain.zetacore.pkg.chains.NetworkType: + type: string + enum: + - mainnet + - testnet + - privnet + - devnet + default: mainnet + title: |- + NetworkType represents the network type of the chain + Mainnet, Testnet, Privnet, Devnet + zetachain.zetacore.pkg.chains.ReceiveStatus: + type: string + enum: + - created + - success + - failed + default: created + description: '- created: Created is used for inbounds' + title: |- + ReceiveStatus represents the status of an outbound + TODO: Rename and move + https://github.com/zeta-chain/node/issues/2257 + zetachain.zetacore.pkg.chains.Vm: + type: string + enum: + - no_vm + - evm + - svm + - tvm + - mvm_sui + default: no_vm + title: |- + Vm represents the virtual machine type of the chain to support smart + contracts + zetachain.zetacore.pkg.coin.CoinType: + type: string + enum: + - Zeta + - Gas + - ERC20 + - Cmd + - NoAssetCall + default: Zeta + title: |- + - Gas: Ether, BNB, Matic, Klay, BTC, etc + - ERC20: ERC20 token + - Cmd: no asset, used for admin command + - NoAssetCall: no asset, used for contract call + zetachain.zetacore.pkg.crypto.PubKeySet: type: object properties: - ethereum_proof: - $ref: '#/definitions/proofsethereumProof' - bitcoin_proof: - $ref: '#/definitions/proofsbitcoinProof' - proofsBlockHeader: + secp256k1: + type: string + ed25519: + type: string + title: PubKeySet contains two pub keys , secp256k1 and ed25519 + zetachain.zetacore.pkg.proofs.BlockHeader: type: object properties: height: @@ -60293,30 +59105,37 @@ definitions: hash: type: string format: byte - parent_hash: + parentHash: type: string format: byte - chain_id: + chainId: type: string format: int64 header: - $ref: '#/definitions/proofsHeaderData' + $ref: '#/definitions/zetachain.zetacore.pkg.proofs.HeaderData' title: chain specific header - proofsHeaderData: + zetachain.zetacore.pkg.proofs.HeaderData: type: object properties: - ethereum_header: + ethereumHeader: type: string format: byte title: binary encoded headers; RLP for ethereum - bitcoin_header: + bitcoinHeader: type: string format: byte title: 80-byte little-endian encoded binary data - proofsbitcoinProof: + zetachain.zetacore.pkg.proofs.Proof: type: object properties: - tx_bytes: + ethereumProof: + $ref: '#/definitions/zetachain.zetacore.pkg.proofs.ethereum.Proof' + bitcoinProof: + $ref: '#/definitions/zetachain.zetacore.pkg.proofs.bitcoin.Proof' + zetachain.zetacore.pkg.proofs.bitcoin.Proof: + type: object + properties: + txBytes: type: string format: byte path: @@ -60325,7 +59144,7 @@ definitions: index: type: integer format: int64 - proofsethereumProof: + zetachain.zetacore.pkg.proofs.ethereum.Proof: type: object properties: keys: @@ -60338,137 +59157,6 @@ definitions: items: type: string format: byte - protobufAny: - type: object - properties: - '@type': - type: string - additionalProperties: {} - v1beta1PageRequest: - type: object - properties: - key: - type: string - format: byte - description: |- - key is a value returned in PageResponse.next_key to begin - querying the next page most efficiently. Only one of offset or key - should be set. - offset: - type: string - format: uint64 - description: |- - offset is a numeric offset that can be used when key is unavailable. - It is less efficient than using key. Only one of offset or key should - be set. - limit: - type: string - format: uint64 - description: |- - limit is the total number of results to be returned in the result page. - If left empty it will default to a value to be set by each app. - count_total: - type: boolean - description: |- - count_total is set to true to indicate that the result set should include - a count of the total number of items available for pagination in UIs. - count_total is only respected when offset is used. It is ignored when key - is set. - reverse: - type: boolean - description: |- - reverse is set to true if results are to be returned in the descending order. - - Since: cosmos-sdk 0.43 - description: |- - message SomeRequest { - Foo some_parameter = 1; - PageRequest pagination = 2; - } - title: |- - PageRequest is to be embedded in gRPC request messages for efficient - pagination. Ex: - v1beta1PageResponse: - type: object - properties: - next_key: - type: string - format: byte - description: |- - next_key is the key to be passed to PageRequest.key to - query the next page most efficiently. It will be empty if - there are no more results. - total: - type: string - format: uint64 - title: |- - total is total number of results available if PageRequest.count_total - was set, its value is undefined otherwise - description: |- - PageResponse is to be embedded in gRPC response messages where the - corresponding request message has used PageRequest. - - message SomeResponse { - repeated Bar results = 1; - PageResponse page = 2; - } - zetacorecrosschainStatus: - type: object - properties: - status: - $ref: '#/definitions/crosschainCctxStatus' - status_message: - type: string - description: |- - status_message carries information about the status transitions: - why they were triggered, old and new status. - error_message: - type: string - description: |- - error_message carries information about the error that caused the tx - to be PendingRevert, Reverted or Aborted. - lastUpdate_timestamp: - type: string - format: int64 - isAbortRefunded: - type: boolean - created_timestamp: - type: string - format: int64 - description: when the CCTX was created. only populated on new transactions. - error_message_revert: - type: string - title: |- - error_message_revert carries information about the revert outbound tx , - which is created if the first outbound tx fails - error_message_abort: - type: string - title: error_message_abort carries information when aborting the CCTX fails - zetacoreemissionsParams: - type: object - properties: - validator_emission_percentage: - type: string - observer_emission_percentage: - type: string - tss_signer_emission_percentage: - type: string - observer_slash_amount: - type: string - ballot_maturity_blocks: - type: string - format: int64 - block_reward_amount: - type: string - title: |- - Params defines the parameters for the module. - Sample values: - ValidatorEmissionPercentage: "00.50", - ObserverEmissionPercentage: "00.25", - TssSignerEmissionPercentage: "00.25", - ObserverSlashAmount: 100000000000000000, - BallotMaturityBlocks: 100, - BlockRewardAmount: 9620949074074074074.074070733466756687, ethermint.evm.v1.ChainConfig: type: object properties: diff --git a/e2e/config/config.go b/e2e/config/config.go index b6a24f1dd8..8e4adcd41e 100644 --- a/e2e/config/config.go +++ b/e2e/config/config.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "gopkg.in/yaml.v3" - sui_utils "github.com/zeta-chain/node/e2e/utils/sui" + "github.com/zeta-chain/node/pkg/contracts/sui" ) // DoubleQuotedString forces a string to be double quoted when marshaling to yaml. @@ -72,6 +72,7 @@ type AdditionalAccounts struct { UserBitcoinWithdraw Account `yaml:"user_bitcoin_withdraw"` UserSolana Account `yaml:"user_solana"` UserSPL Account `yaml:"user_spl"` + UserSui Account `yaml:"user_sui"` UserMisc Account `yaml:"user_misc"` UserAdmin Account `yaml:"user_admin"` UserMigration Account `yaml:"user_migration"` // used for TSS migration, TODO: rename (https://github.com/zeta-chain/node/issues/2780) @@ -122,6 +123,7 @@ type Contracts struct { EVM EVM `yaml:"evm"` ZEVM ZEVM `yaml:"zevm"` Solana Solana `yaml:"solana"` + Sui Sui `yaml:"sui"` } // Solana contains the addresses of predeployed contracts and accounts on the Solana chain @@ -130,6 +132,14 @@ type Solana struct { SPLAddr DoubleQuotedString `yaml:"spl"` } +// Sui contains the addresses of predeployed contracts on the Sui chain +type Sui struct { + GatewayPackageID DoubleQuotedString `yaml:"gateway_package_id"` + GatewayObjectID DoubleQuotedString `yaml:"gateway_object_id"` + FungibleTokenCoinType DoubleQuotedString `yaml:"fungible_token_coin_type"` + FungibleTokenTreasuryCap DoubleQuotedString `yaml:"fungible_token_treasury_cap"` +} + // EVM contains the addresses of predeployed contracts on the EVM chain type EVM struct { ZetaEthAddr DoubleQuotedString `yaml:"zeta_eth"` @@ -151,6 +161,8 @@ type ZEVM struct { SOLZRC20Addr DoubleQuotedString `yaml:"sol_zrc20"` SPLZRC20Addr DoubleQuotedString `yaml:"spl_zrc20"` TONZRC20Addr DoubleQuotedString `yaml:"ton_zrc20"` + SUIZRC20Addr DoubleQuotedString `yaml:"sui_zrc20"` + SuiTokenZRC20Addr DoubleQuotedString `yaml:"sui_token_zrc20"` UniswapFactoryAddr DoubleQuotedString `yaml:"uniswap_factory"` UniswapRouterAddr DoubleQuotedString `yaml:"uniswap_router"` ConnectorZEVMAddr DoubleQuotedString `yaml:"connector_zevm"` @@ -243,6 +255,7 @@ func (a AdditionalAccounts) AsSlice() []Account { a.UserBitcoinDeposit, a.UserBitcoinWithdraw, a.UserSolana, + a.UserSui, a.UserSPL, a.UserLegacyEther, a.UserMisc, @@ -338,6 +351,10 @@ func (c *Config) GenerateKeys() error { if err != nil { return err } + c.AdditionalAccounts.UserSui, err = generateAccount() + if err != nil { + return err + } c.AdditionalAccounts.UserLegacyEther, err = generateAccount() if err != nil { return err @@ -403,13 +420,13 @@ func (a Account) PrivateKey() (*ecdsa.PrivateKey, error) { } // SuiAddress derives the blake2b hash from the private key -func (a Account) SuiSigner() (*sui_utils.SignerSecp256k1, error) { +func (a Account) SuiSigner() (*sui.SignerSecp256k1, error) { privateKeyBytes, err := hex.DecodeString(a.RawPrivateKey.String()) if err != nil { return nil, fmt.Errorf("decode private key: %w", err) } - signer := sui_utils.NewSignerSecp256k1FromSecretKey(privateKeyBytes) - return signer, nil + + return sui.NewSignerSecp256k1(privateKeyBytes), nil } // Validate that the address and the private key specified in the diff --git a/testutil/contracts/Dapp.abi b/e2e/contracts/dapp/Dapp.abi similarity index 100% rename from testutil/contracts/Dapp.abi rename to e2e/contracts/dapp/Dapp.abi diff --git a/testutil/contracts/Dapp.bin b/e2e/contracts/dapp/Dapp.bin similarity index 100% rename from testutil/contracts/Dapp.bin rename to e2e/contracts/dapp/Dapp.bin diff --git a/testutil/contracts/Dapp.go b/e2e/contracts/dapp/Dapp.go similarity index 99% rename from testutil/contracts/Dapp.go rename to e2e/contracts/dapp/Dapp.go index cfd07f12e4..c541cd20eb 100644 --- a/testutil/contracts/Dapp.go +++ b/e2e/contracts/dapp/Dapp.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package dapp import ( "errors" diff --git a/testutil/contracts/Dapp.json b/e2e/contracts/dapp/Dapp.json similarity index 100% rename from testutil/contracts/Dapp.json rename to e2e/contracts/dapp/Dapp.json diff --git a/testutil/contracts/Dapp.sol b/e2e/contracts/dapp/Dapp.sol similarity index 100% rename from testutil/contracts/Dapp.sol rename to e2e/contracts/dapp/Dapp.sol diff --git a/e2e/contracts/dapp/bindings.go b/e2e/contracts/dapp/bindings.go new file mode 100644 index 0000000000..d26bc4e504 --- /dev/null +++ b/e2e/contracts/dapp/bindings.go @@ -0,0 +1,8 @@ +// Dapp +// +//go:generate sh -c "solc --evm-version paris Dapp.sol --combined-json abi,bin | jq '.contracts.\"Dapp.sol:Dapp\"' > Dapp.json" +//go:generate sh -c "cat Dapp.json | jq .abi > Dapp.abi" +//go:generate sh -c "cat Dapp.json | jq .bin | tr -d '\"' > Dapp.bin" +//go:generate sh -c "abigen --abi Dapp.abi --bin Dapp.bin --pkg dapp --type Dapp --out Dapp.go" + +package dapp diff --git a/testutil/contracts/DappReverter.abi b/e2e/contracts/dappreverter/DappReverter.abi similarity index 100% rename from testutil/contracts/DappReverter.abi rename to e2e/contracts/dappreverter/DappReverter.abi diff --git a/testutil/contracts/DappReverter.bin b/e2e/contracts/dappreverter/DappReverter.bin similarity index 100% rename from testutil/contracts/DappReverter.bin rename to e2e/contracts/dappreverter/DappReverter.bin diff --git a/testutil/contracts/DappReverter.go b/e2e/contracts/dappreverter/DappReverter.go similarity index 99% rename from testutil/contracts/DappReverter.go rename to e2e/contracts/dappreverter/DappReverter.go index 450f44173c..af4783960b 100644 --- a/testutil/contracts/DappReverter.go +++ b/e2e/contracts/dappreverter/DappReverter.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package dappreverter import ( "errors" diff --git a/testutil/contracts/DappReverter.json b/e2e/contracts/dappreverter/DappReverter.json similarity index 100% rename from testutil/contracts/DappReverter.json rename to e2e/contracts/dappreverter/DappReverter.json diff --git a/testutil/contracts/DappReverter.sol b/e2e/contracts/dappreverter/DappReverter.sol similarity index 100% rename from testutil/contracts/DappReverter.sol rename to e2e/contracts/dappreverter/DappReverter.sol diff --git a/e2e/contracts/dappreverter/bindings.go b/e2e/contracts/dappreverter/bindings.go new file mode 100644 index 0000000000..9ecc3f7daf --- /dev/null +++ b/e2e/contracts/dappreverter/bindings.go @@ -0,0 +1,7 @@ +// DappReverter +//go:generate sh -c "solc --evm-version paris DappReverter.sol --combined-json abi,bin | jq '.contracts.\"DappReverter.sol:DappReverter\"' > DappReverter.json" +//go:generate sh -c "cat DappReverter.json | jq .abi > DappReverter.abi" +//go:generate sh -c "cat DappReverter.json | jq .bin | tr -d '\"' > DappReverter.bin" +//go:generate sh -c "abigen --abi DappReverter.abi --bin DappReverter.bin --pkg dappreverter --type DappReverter --out DappReverter.go" + +package dappreverter diff --git a/testutil/contracts/Depositor.abi b/e2e/contracts/depositor/Depositor.abi similarity index 100% rename from testutil/contracts/Depositor.abi rename to e2e/contracts/depositor/Depositor.abi diff --git a/testutil/contracts/Depositor.bin b/e2e/contracts/depositor/Depositor.bin similarity index 100% rename from testutil/contracts/Depositor.bin rename to e2e/contracts/depositor/Depositor.bin diff --git a/testutil/contracts/Depositor.go b/e2e/contracts/depositor/Depositor.go similarity index 99% rename from testutil/contracts/Depositor.go rename to e2e/contracts/depositor/Depositor.go index b545be3e03..9234b498da 100644 --- a/testutil/contracts/Depositor.go +++ b/e2e/contracts/depositor/Depositor.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package depositor import ( "errors" diff --git a/testutil/contracts/Depositor.json b/e2e/contracts/depositor/Depositor.json similarity index 100% rename from testutil/contracts/Depositor.json rename to e2e/contracts/depositor/Depositor.json diff --git a/testutil/contracts/Depositor.sol b/e2e/contracts/depositor/Depositor.sol similarity index 100% rename from testutil/contracts/Depositor.sol rename to e2e/contracts/depositor/Depositor.sol diff --git a/e2e/contracts/depositor/bindings.go b/e2e/contracts/depositor/bindings.go new file mode 100644 index 0000000000..9022af9951 --- /dev/null +++ b/e2e/contracts/depositor/bindings.go @@ -0,0 +1,7 @@ +// Depositor +//go:generate sh -c "solc --evm-version paris Depositor.sol --combined-json abi,bin | jq '.contracts.\"Depositor.sol:Depositor\"' > Depositor.json" +//go:generate sh -c "cat Depositor.json | jq .abi > Depositor.abi" +//go:generate sh -c "cat Depositor.json | jq .bin | tr -d '\"' > Depositor.bin" +//go:generate sh -c "abigen --abi Depositor.abi --bin Depositor.bin --pkg depositor --type Depositor --out Depositor.go" + +package depositor diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.abi b/e2e/contracts/erc1967proxy/ERC1967Proxy.abi similarity index 100% rename from pkg/contracts/erc1967proxy/ERC1967Proxy.abi rename to e2e/contracts/erc1967proxy/ERC1967Proxy.abi diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.bin b/e2e/contracts/erc1967proxy/ERC1967Proxy.bin similarity index 100% rename from pkg/contracts/erc1967proxy/ERC1967Proxy.bin rename to e2e/contracts/erc1967proxy/ERC1967Proxy.bin diff --git a/pkg/contracts/erc1967proxy/ERC1967Proxy.go b/e2e/contracts/erc1967proxy/ERC1967Proxy.go similarity index 100% rename from pkg/contracts/erc1967proxy/ERC1967Proxy.go rename to e2e/contracts/erc1967proxy/ERC1967Proxy.go diff --git a/pkg/contracts/erc1967proxy/bindings.go b/e2e/contracts/erc1967proxy/bindings.go similarity index 100% rename from pkg/contracts/erc1967proxy/bindings.go rename to e2e/contracts/erc1967proxy/bindings.go diff --git a/testutil/contracts/Example.abi b/e2e/contracts/example/Example.abi similarity index 100% rename from testutil/contracts/Example.abi rename to e2e/contracts/example/Example.abi diff --git a/testutil/contracts/Example.bin b/e2e/contracts/example/Example.bin similarity index 100% rename from testutil/contracts/Example.bin rename to e2e/contracts/example/Example.bin diff --git a/testutil/contracts/Example.go b/e2e/contracts/example/Example.go similarity index 99% rename from testutil/contracts/Example.go rename to e2e/contracts/example/Example.go index 1ded198bc4..b7bfc48582 100644 --- a/testutil/contracts/Example.go +++ b/e2e/contracts/example/Example.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package example import ( "errors" diff --git a/testutil/contracts/Example.json b/e2e/contracts/example/Example.json similarity index 100% rename from testutil/contracts/Example.json rename to e2e/contracts/example/Example.json diff --git a/testutil/contracts/Example.sol b/e2e/contracts/example/Example.sol similarity index 100% rename from testutil/contracts/Example.sol rename to e2e/contracts/example/Example.sol diff --git a/e2e/contracts/example/bindings.go b/e2e/contracts/example/bindings.go new file mode 100644 index 0000000000..2a6e3c7bc7 --- /dev/null +++ b/e2e/contracts/example/bindings.go @@ -0,0 +1,7 @@ +// Example +//go:generate sh -c "solc --evm-version paris Example.sol --combined-json abi,bin | jq '.contracts.\"Example.sol:Example\"' > Example.json" +//go:generate sh -c "cat Example.json | jq .abi > Example.abi" +//go:generate sh -c "cat Example.json | jq .bin | tr -d '\"' > Example.bin" +//go:generate sh -c "abigen --abi Example.abi --bin Example.bin --pkg example --type Example --out Example.go" + +package example diff --git a/pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.abi b/e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.abi similarity index 100% rename from pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.abi rename to e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.abi diff --git a/pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.bin b/e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.bin similarity index 100% rename from pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.bin rename to e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.bin diff --git a/pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.go b/e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.go similarity index 100% rename from pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.go rename to e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.go diff --git a/pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.json b/e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.json similarity index 100% rename from pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.json rename to e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.json diff --git a/pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.sol b/e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.sol similarity index 100% rename from pkg/contracts/gatewayzevmcaller/GatewayZEVMCaller.sol rename to e2e/contracts/gatewayzevmcaller/GatewayZEVMCaller.sol diff --git a/pkg/contracts/gatewayzevmcaller/bindings.go b/e2e/contracts/gatewayzevmcaller/bindings.go similarity index 100% rename from pkg/contracts/gatewayzevmcaller/bindings.go rename to e2e/contracts/gatewayzevmcaller/bindings.go diff --git a/testutil/contracts/Reverter.abi b/e2e/contracts/reverter/Reverter.abi similarity index 100% rename from testutil/contracts/Reverter.abi rename to e2e/contracts/reverter/Reverter.abi diff --git a/testutil/contracts/Reverter.bin b/e2e/contracts/reverter/Reverter.bin similarity index 100% rename from testutil/contracts/Reverter.bin rename to e2e/contracts/reverter/Reverter.bin diff --git a/testutil/contracts/Reverter.go b/e2e/contracts/reverter/Reverter.go similarity index 99% rename from testutil/contracts/Reverter.go rename to e2e/contracts/reverter/Reverter.go index 7d9cb13bc3..8872ad655d 100644 --- a/testutil/contracts/Reverter.go +++ b/e2e/contracts/reverter/Reverter.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package reverter import ( "errors" diff --git a/testutil/contracts/Reverter.json b/e2e/contracts/reverter/Reverter.json similarity index 100% rename from testutil/contracts/Reverter.json rename to e2e/contracts/reverter/Reverter.json diff --git a/testutil/contracts/Reverter.sol b/e2e/contracts/reverter/Reverter.sol similarity index 100% rename from testutil/contracts/Reverter.sol rename to e2e/contracts/reverter/Reverter.sol diff --git a/e2e/contracts/reverter/bindings.go b/e2e/contracts/reverter/bindings.go new file mode 100644 index 0000000000..9c37a2f4cf --- /dev/null +++ b/e2e/contracts/reverter/bindings.go @@ -0,0 +1,7 @@ +// Reverter +//go:generate sh -c "solc --evm-version paris Reverter.sol --combined-json abi,bin | jq '.contracts.\"Reverter.sol:Reverter\"' > Reverter.json" +//go:generate sh -c "cat Reverter.json | jq .abi > Reverter.abi" +//go:generate sh -c "cat Reverter.json | jq .bin | tr -d '\"' > Reverter.bin" +//go:generate sh -c "abigen --abi Reverter.abi --bin Reverter.bin --pkg reverter --type Reverter --out Reverter.go" + +package reverter diff --git a/e2e/contracts/sui/bin.go b/e2e/contracts/sui/bin.go index 3718d5e6dd..da247aab93 100644 --- a/e2e/contracts/sui/bin.go +++ b/e2e/contracts/sui/bin.go @@ -8,7 +8,15 @@ import ( //go:embed gateway.mv var gatewayBinary []byte +//go:embed fake_usdc.mv +var fakeUSDC []byte + // GatewayBytecodeBase64 gets the gateway binary encoded as base64 for deployment func GatewayBytecodeBase64() string { return base64.StdEncoding.EncodeToString(gatewayBinary) } + +// FakeUSDCBytecodeBase64 gets the fake USDC binary encoded as base64 for deployment +func FakeUSDCBytecodeBase64() string { + return base64.StdEncoding.EncodeToString(fakeUSDC) +} diff --git a/e2e/contracts/sui/fake_usdc.mv b/e2e/contracts/sui/fake_usdc.mv new file mode 100644 index 0000000000..4c604c2619 Binary files /dev/null and b/e2e/contracts/sui/fake_usdc.mv differ diff --git a/pkg/contracts/testdappv2/TestDAppV2.abi b/e2e/contracts/testdappv2/TestDAppV2.abi similarity index 100% rename from pkg/contracts/testdappv2/TestDAppV2.abi rename to e2e/contracts/testdappv2/TestDAppV2.abi diff --git a/pkg/contracts/testdappv2/TestDAppV2.bin b/e2e/contracts/testdappv2/TestDAppV2.bin similarity index 100% rename from pkg/contracts/testdappv2/TestDAppV2.bin rename to e2e/contracts/testdappv2/TestDAppV2.bin diff --git a/pkg/contracts/testdappv2/TestDAppV2.go b/e2e/contracts/testdappv2/TestDAppV2.go similarity index 100% rename from pkg/contracts/testdappv2/TestDAppV2.go rename to e2e/contracts/testdappv2/TestDAppV2.go diff --git a/pkg/contracts/testdappv2/TestDAppV2.json b/e2e/contracts/testdappv2/TestDAppV2.json similarity index 100% rename from pkg/contracts/testdappv2/TestDAppV2.json rename to e2e/contracts/testdappv2/TestDAppV2.json diff --git a/pkg/contracts/testdappv2/TestDAppV2.sol b/e2e/contracts/testdappv2/TestDAppV2.sol similarity index 100% rename from pkg/contracts/testdappv2/TestDAppV2.sol rename to e2e/contracts/testdappv2/TestDAppV2.sol diff --git a/pkg/contracts/testdappv2/bindings.go b/e2e/contracts/testdappv2/bindings.go similarity index 100% rename from pkg/contracts/testdappv2/bindings.go rename to e2e/contracts/testdappv2/bindings.go diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.bin b/e2e/contracts/testgasconsumer/TestGasConsumer.bin index 590b637cc1..b0aafe4254 100644 --- a/e2e/contracts/testgasconsumer/TestGasConsumer.bin +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.bin @@ -1 +1 @@ -6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033 +6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b60006216e36090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea26469706673582212208d28c81ee36a96847aff0ea97e72e0e3b384da903bf7760cf019cbe78945877e64736f6c634300081a0033 diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.go b/e2e/contracts/testgasconsumer/TestGasConsumer.go index b40770c40b..4bbb5543db 100644 --- a/e2e/contracts/testgasconsumer/TestGasConsumer.go +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.go @@ -39,7 +39,7 @@ type TestGasConsumerzContext struct { // TestGasConsumerMetaData contains all meta data concerning the TestGasConsumer contract. var TestGasConsumerMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structTestGasConsumer.zContext\",\"name\":\"_context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"_zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033", + Bin: "0x6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b60006216e36090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea26469706673582212208d28c81ee36a96847aff0ea97e72e0e3b384da903bf7760cf019cbe78945877e64736f6c634300081a0033", } // TestGasConsumerABI is the input ABI used to generate the binding from. diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.json b/e2e/contracts/testgasconsumer/TestGasConsumer.json index 9f80d60d5f..c038cd8f16 100644 --- a/e2e/contracts/testgasconsumer/TestGasConsumer.json +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.json @@ -46,5 +46,5 @@ "type": "function" } ], - "bin": "6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b6000624c4b4090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea2646970667358221220e1d03a34090a8a647a128849d9f9434831ba3b1e4d28a514d9c9dc922068351e64736f6c634300081a0033" + "bin": "6080604052348015600f57600080fd5b5061036d8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80635bcfd61614610030575b600080fd5b61004a60048036038101906100459190610233565b61004c565b005b61005461005b565b5050505050565b60006216e36090506000614e209050600081836100789190610306565b905060005b818110156100bb576000819080600181540180825580915050600190039060005260206000200160009091909190915055808060010191505061007d565b506000806100c991906100ce565b505050565b50805460008255906000526020600020908101906100ec91906100ef565b50565b5b808211156101085760008160009055506001016100f0565b5090565b600080fd5b600080fd5b600080fd5b60006060828403121561013157610130610116565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101658261013a565b9050919050565b6101758161015a565b811461018057600080fd5b50565b6000813590506101928161016c565b92915050565b6000819050919050565b6101ab81610198565b81146101b657600080fd5b50565b6000813590506101c8816101a2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101f3576101f26101ce565b5b8235905067ffffffffffffffff8111156102105761020f6101d3565b5b60208301915083600182028301111561022c5761022b6101d8565b5b9250929050565b60008060008060006080868803121561024f5761024e61010c565b5b600086013567ffffffffffffffff81111561026d5761026c610111565b5b6102798882890161011b565b955050602061028a88828901610183565b945050604061029b888289016101b9565b935050606086013567ffffffffffffffff8111156102bc576102bb610111565b5b6102c8888289016101dd565b92509250509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600061031182610198565b915061031c83610198565b92508261032c5761032b6102d7565b5b82820490509291505056fea26469706673582212208d28c81ee36a96847aff0ea97e72e0e3b384da903bf7760cf019cbe78945877e64736f6c634300081a0033" } diff --git a/e2e/contracts/testgasconsumer/TestGasConsumer.sol b/e2e/contracts/testgasconsumer/TestGasConsumer.sol index 4d1d322cd2..c44c134eee 100644 --- a/e2e/contracts/testgasconsumer/TestGasConsumer.sol +++ b/e2e/contracts/testgasconsumer/TestGasConsumer.sol @@ -26,7 +26,7 @@ contract TestGasConsumer { function consumeGas() internal { // Approximate target gas consumption - uint256 targetGas = 5000000; + uint256 targetGas = 1500000; // Approximate gas cost for a single storage write uint256 storageWriteGasCost = 20000; uint256 iterations = targetGas / storageWriteGasCost; diff --git a/testutil/contracts/Withdrawer.abi b/e2e/contracts/withdrawerv2/Withdrawer.abi similarity index 100% rename from testutil/contracts/Withdrawer.abi rename to e2e/contracts/withdrawerv2/Withdrawer.abi diff --git a/testutil/contracts/Withdrawer.bin b/e2e/contracts/withdrawerv2/Withdrawer.bin similarity index 100% rename from testutil/contracts/Withdrawer.bin rename to e2e/contracts/withdrawerv2/Withdrawer.bin diff --git a/testutil/contracts/Withdrawer.go b/e2e/contracts/withdrawerv2/Withdrawer.go similarity index 99% rename from testutil/contracts/Withdrawer.go rename to e2e/contracts/withdrawerv2/Withdrawer.go index 2e3c6aad5a..44fe1bc139 100644 --- a/testutil/contracts/Withdrawer.go +++ b/e2e/contracts/withdrawerv2/Withdrawer.go @@ -1,7 +1,7 @@ // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. -package contracts +package withdrawerv2 import ( "errors" diff --git a/testutil/contracts/Withdrawer.json b/e2e/contracts/withdrawerv2/Withdrawer.json similarity index 100% rename from testutil/contracts/Withdrawer.json rename to e2e/contracts/withdrawerv2/Withdrawer.json diff --git a/testutil/contracts/Withdrawer.sol b/e2e/contracts/withdrawerv2/Withdrawer.sol similarity index 100% rename from testutil/contracts/Withdrawer.sol rename to e2e/contracts/withdrawerv2/Withdrawer.sol diff --git a/e2e/contracts/withdrawerv2/bindings.go b/e2e/contracts/withdrawerv2/bindings.go new file mode 100644 index 0000000000..4ad00f4730 --- /dev/null +++ b/e2e/contracts/withdrawerv2/bindings.go @@ -0,0 +1,7 @@ +// Withdrawer +//go:generate sh -c "solc --evm-version paris Withdrawer.sol --combined-json abi,bin | jq '.contracts.\"Withdrawer.sol:Withdrawer\"' > Withdrawer.json" +//go:generate sh -c "cat Withdrawer.json | jq .abi > Withdrawer.abi" +//go:generate sh -c "cat Withdrawer.json | jq .bin | tr -d '\"' > Withdrawer.bin" +//go:generate sh -c "abigen --abi Withdrawer.abi --bin Withdrawer.bin --pkg withdrawerv2 --type Withdrawer --out Withdrawer.go" + +package withdrawerv2 diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index e9f45c0840..9d3c2c9733 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -59,19 +59,21 @@ const ( /* * Solana tests */ - TestSolanaDepositName = "solana_deposit" - TestSolanaWithdrawName = "solana_withdraw" - TestSolanaWithdrawAndCallName = "solana_withdraw_and_call" - TestSolanaDepositAndCallName = "solana_deposit_and_call" - TestSolanaDepositAndCallRevertName = "solana_deposit_and_call_revert" - TestSolanaDepositAndCallRevertWithDustName = "solana_deposit_and_call_revert_with_dust" - TestSolanaDepositRestrictedName = "solana_deposit_restricted" - TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" - TestSPLDepositName = "spl_deposit" - TestSPLDepositAndCallName = "spl_deposit_and_call" - TestSPLWithdrawName = "spl_withdraw" - TestSPLWithdrawAndCallName = "spl_withdraw_and_call" - TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata" + TestSolanaDepositName = "solana_deposit" + TestSolanaWithdrawName = "solana_withdraw" + TestSolanaWithdrawAndCallName = "solana_withdraw_and_call" + TestSolanaWithdrawAndCallRevertWithCallName = "solana_withdraw_and_call_revert_with_call" + TestSolanaDepositAndCallName = "solana_deposit_and_call" + TestSolanaDepositAndCallRevertName = "solana_deposit_and_call_revert" + TestSolanaDepositAndCallRevertWithDustName = "solana_deposit_and_call_revert_with_dust" + TestSolanaDepositRestrictedName = "solana_deposit_restricted" + TestSolanaWithdrawRestrictedName = "solana_withdraw_restricted" + TestSPLDepositName = "spl_deposit" + TestSPLDepositAndCallName = "spl_deposit_and_call" + TestSPLWithdrawName = "spl_withdraw" + TestSPLWithdrawAndCallName = "spl_withdraw_and_call" + TestSPLWithdrawAndCallRevertName = "spl_withdraw_and_call_revert" + TestSPLWithdrawAndCreateReceiverAtaName = "spl_withdraw_and_create_receiver_ata" /** * TON tests @@ -82,6 +84,14 @@ const ( TestTONWithdrawName = "ton_withdraw" TestTONWithdrawConcurrentName = "ton_withdraw_concurrent" + /* + Sui tests + */ + TestSuiDepositName = "sui_deposit" + TestSuiDepositAndCallName = "sui_deposit_and_call" + TestSuiTokenDepositName = "sui_token_deposit" // #nosec G101: Potential hardcoded credentials (gosec), not a credential + TestSuiTokenDepositAndCallName = "sui_token_deposit_and_call" // #nosec G101: Potential hardcoded credentials (gosec), not a credential + /* Bitcoin tests Test transfer of Bitcoin asset across chains @@ -533,6 +543,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestSolanaWithdrawAndCall, ), + runner.NewE2ETest( + TestSolanaWithdrawAndCallRevertWithCallName, + "withdraw SOL from ZEVM and call solana program that reverts", + []runner.ArgDefinition{ + {Description: "amount in lamport", DefaultValue: "1000000"}, + }, + TestSolanaWithdrawAndCallRevertWithCall, + ), runner.NewE2ETest( TestSPLWithdrawAndCallName, "withdraw SPL from ZEVM and call solana program", @@ -541,6 +559,14 @@ var AllE2ETests = []runner.E2ETest{ }, TestSPLWithdrawAndCall, ), + runner.NewE2ETest( + TestSPLWithdrawAndCallRevertName, + "withdraw SPL from ZEVM and call solana program that reverts", + []runner.ArgDefinition{ + {Description: "amount in lamport", DefaultValue: "1000000"}, + }, + TestSPLWithdrawAndCallRevert, + ), runner.NewE2ETest( TestSolanaDepositAndCallName, "deposit SOL into ZEVM and call a contract", @@ -660,6 +686,41 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestTONWithdrawConcurrent, ), + /* + Sui tests + */ + runner.NewE2ETest( + TestSuiDepositName, + "deposit SUI into ZEVM", + []runner.ArgDefinition{ + {Description: "amount in mist", DefaultValue: "1000000"}, + }, + TestSuiDeposit, + ), + runner.NewE2ETest( + TestSuiDepositAndCallName, + "deposit SUI into ZEVM and call a contract", + []runner.ArgDefinition{ + {Description: "amount in mist", DefaultValue: "1000000"}, + }, + TestSuiDepositAndCall, + ), + runner.NewE2ETest( + TestSuiTokenDepositName, + "deposit fungible token SUI into ZEVM", + []runner.ArgDefinition{ + {Description: "amount in base unit", DefaultValue: "1000000"}, + }, + TestSuiTokenDeposit, + ), + runner.NewE2ETest( + TestSuiTokenDepositAndCallName, + "deposit fungible token into ZEVM and call a contract", + []runner.ArgDefinition{ + {Description: "amount in base unit", DefaultValue: "1000000"}, + }, + TestSuiTokenDepositAndCall, + ), /* Bitcoin tests */ diff --git a/e2e/e2etests/legacy/test_erc20_multiple_deposits.go b/e2e/e2etests/legacy/test_erc20_multiple_deposits.go index d11d410206..634c5d06b3 100644 --- a/e2e/e2etests/legacy/test_erc20_multiple_deposits.go +++ b/e2e/e2etests/legacy/test_erc20_multiple_deposits.go @@ -7,9 +7,9 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/depositor" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" ) func TestMultipleERC20Deposit(r *runner.E2ERunner, args []string) { diff --git a/e2e/e2etests/legacy/test_erc20_multiple_withdraws.go b/e2e/e2etests/legacy/test_erc20_multiple_withdraws.go index 08f06b5986..f3c581222c 100644 --- a/e2e/e2etests/legacy/test_erc20_multiple_withdraws.go +++ b/e2e/e2etests/legacy/test_erc20_multiple_withdraws.go @@ -6,9 +6,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/withdrawerv2" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" ) func TestMultipleERC20Withdraws(r *runner.E2ERunner, args []string) { diff --git a/e2e/e2etests/legacy/test_eth_deposit_call.go b/e2e/e2etests/legacy/test_eth_deposit_call.go index b0d63ebbf1..c206906026 100644 --- a/e2e/e2etests/legacy/test_eth_deposit_call.go +++ b/e2e/e2etests/legacy/test_eth_deposit_call.go @@ -4,9 +4,10 @@ import ( ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/contracts/example" + testcontract "github.com/zeta-chain/node/e2e/contracts/reverter" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" "github.com/zeta-chain/node/x/crosschain/types" ) @@ -18,7 +19,7 @@ func TestEtherDepositAndCall(r *runner.E2ERunner, args []string) { value := utils.ParseBigInt(r, args[0]) r.Logger.Info("Deploying example contract") - exampleAddr, _, exampleContract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient) + exampleAddr, _, exampleContract, err := example.DeployExample(r.ZEVMAuth, r.ZEVMClient) require.NoError(r, err) r.Logger.Info("Example contract deployed") diff --git a/e2e/e2etests/test_bitcoin_deposit_call.go b/e2e/e2etests/test_bitcoin_deposit_call.go index 3cd7491192..b74f1061f0 100644 --- a/e2e/e2etests/test_bitcoin_deposit_call.go +++ b/e2e/e2etests/test_bitcoin_deposit_call.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/bitcoin/common" ) diff --git a/e2e/e2etests/test_bitcoin_std_deposit_and_call.go b/e2e/e2etests/test_bitcoin_std_deposit_and_call.go index 77af601aa2..1f99732500 100644 --- a/e2e/e2etests/test_bitcoin_std_deposit_and_call.go +++ b/e2e/e2etests/test_bitcoin_std_deposit_and_call.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/memo" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" zetabitcoin "github.com/zeta-chain/node/zetaclient/chains/bitcoin/common" ) diff --git a/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go b/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go index 92f907a20a..baf83ee05a 100644 --- a/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go +++ b/e2e/e2etests/test_bitcoin_std_memo_inscribed_deposit_and_call.go @@ -5,10 +5,10 @@ import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/memo" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "github.com/zeta-chain/node/zetaclient/chains/bitcoin/common" ) diff --git a/e2e/e2etests/test_deploy_contract.go b/e2e/e2etests/test_deploy_contract.go index 4a0c8474da..4d93de03ac 100644 --- a/e2e/e2etests/test_deploy_contract.go +++ b/e2e/e2etests/test_deploy_contract.go @@ -6,9 +6,9 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" ) // deployFunc is a function that deploys a contract diff --git a/e2e/e2etests/test_eth_withdraw_and_call_through_contract.go b/e2e/e2etests/test_eth_withdraw_and_call_through_contract.go index 3e92b70a70..71c6e002ea 100644 --- a/e2e/e2etests/test_eth_withdraw_and_call_through_contract.go +++ b/e2e/e2etests/test_eth_withdraw_and_call_through_contract.go @@ -6,9 +6,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/contracts/gatewayzevmcaller" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - gatewayzevmcaller "github.com/zeta-chain/node/pkg/contracts/gatewayzevmcaller" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/e2etests/test_migrate_tss.go b/e2e/e2etests/test_migrate_tss.go index 545e4abc55..b17f9431a2 100644 --- a/e2e/e2etests/test_migrate_tss.go +++ b/e2e/e2etests/test_migrate_tss.go @@ -147,8 +147,8 @@ func TestMigrateTSS(r *runner.E2ERunner, _ []string) { btcTSSBalanceNew += utxo.Amount } - r.Logger.Info("BTC Balance Old: %f", btcTSSBalanceOld*1e8) - r.Logger.Info("BTC Balance New: %f", btcTSSBalanceNew*1e8) + r.Logger.Info("BTC TSS Balance Old: %f", btcTSSBalanceOld*1e8) + r.Logger.Info("BTC TSS Balance New: %f", btcTSSBalanceNew*1e8) r.Logger.Info("Migrator amount : %s", cctxBTC.GetCurrentOutboundParam().Amount) // btcTSSBalanceNew should be less than btcTSSBalanceOld as there is some loss of funds during migration @@ -165,8 +165,8 @@ func TestMigrateTSS(r *runner.E2ERunner, _ []string) { ethTSSBalanceNew, err := r.EVMClient.BalanceAt(context.Background(), r.TSSAddress, nil) require.NoError(r, err) - r.Logger.Info("TSS Balance Old: %s", ethTSSBalanceOld.String()) - r.Logger.Info("TSS Balance New: %s", ethTSSBalanceNew.String()) + r.Logger.Info("ETH TSS Balance Old: %s", ethTSSBalanceOld.String()) + r.Logger.Info("ETH TSS Balance New: %s", ethTSSBalanceNew.String()) r.Logger.Info("Migrator amount : %s", cctxETH.GetCurrentOutboundParam().Amount.String()) // ethTSSBalanceNew should be less than ethTSSBalanceOld as there is some loss of funds during migration diff --git a/e2e/e2etests/test_solana_deposit_and_call_revert.go b/e2e/e2etests/test_solana_deposit_and_call_revert.go index 4b0791a3c8..18a1cffd87 100644 --- a/e2e/e2etests/test_solana_deposit_and_call_revert.go +++ b/e2e/e2etests/test_solana_deposit_and_call_revert.go @@ -3,9 +3,9 @@ package e2etests import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/reverter" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/e2etests/test_solana_deposit_call.go b/e2e/e2etests/test_solana_deposit_call.go index 9a7bc566ba..f506844fe2 100644 --- a/e2e/e2etests/test_solana_deposit_call.go +++ b/e2e/e2etests/test_solana_deposit_call.go @@ -3,9 +3,9 @@ package e2etests import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/e2etests/test_solana_withdraw_and_call.go b/e2e/e2etests/test_solana_withdraw_and_call.go index 19ebe0f369..fd4af07784 100644 --- a/e2e/e2etests/test_solana_withdraw_and_call.go +++ b/e2e/e2etests/test_solana_withdraw_and_call.go @@ -8,6 +8,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/near/borsh-go" "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" @@ -55,7 +56,15 @@ func TestSolanaWithdrawAndCall(r *runner.E2ERunner, args []string) { require.NoError(r, err) // withdraw and call - tx := r.WithdrawAndCallSOLZRC20(runner.ConnectedProgramID, withdrawAmount, approvedAmount, []byte("hello")) + tx := r.WithdrawAndCallSOLZRC20( + runner.ConnectedProgramID, + withdrawAmount, + approvedAmount, + []byte("hello"), + 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) diff --git a/e2e/e2etests/test_solana_withdraw_and_call_revert_with_call.go b/e2e/e2etests/test_solana_withdraw_and_call_revert_with_call.go new file mode 100644 index 0000000000..01133fa9eb --- /dev/null +++ b/e2e/e2etests/test_solana_withdraw_and_call_revert_with_call.go @@ -0,0 +1,87 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestSolanaWithdrawAndCallRevertWithCall executes withdrawAndCall on zevm and calls connected program on solana +// execution is reverted in connected program on_call function and onRevert is called on ZEVM TestDapp contract +func TestSolanaWithdrawAndCallRevertWithCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + withdrawAmount := utils.ParseBigInt(r, args[0]) + + // get ZRC20 SOL balance before withdraw + balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SOL before withdraw: %d", balanceBefore) + + require.Equal(r, 1, balanceBefore.Cmp(withdrawAmount), "Insufficient balance for withdrawal") + + // parse withdraw amount (in lamports), approve amount is 1 SOL + approvedAmount := new(big.Int).SetUint64(solana.LAMPORTS_PER_SOL) + require.Equal( + r, + -1, + withdrawAmount.Cmp(approvedAmount), + "Withdrawal amount must be less than the approved amount: %v", + approvedAmount, + ) + + // use a random address to get the revert amount + revertAddress := r.TestDAppV2ZEVMAddr + balance, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + payload := randomPayload(r) + r.AssertTestDAppEVMCalled(false, payload, withdrawAmount) + + // withdraw and call + tx := r.WithdrawAndCallSOLZRC20( + runner.ConnectedProgramID, + withdrawAmount, + approvedAmount, + []byte("revert"), + gatewayzevm.RevertOptions{ + CallOnRevert: true, + RevertAddress: revertAddress, + RevertMessage: []byte(payload), + 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) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) + + // get ZRC20 SOL balance after withdraw + balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SOL after withdraw: %d", balanceAfter) + + r.AssertTestDAppZEVMCalled(true, payload, big.NewInt(0)) + + // check expected sender was used + senderForMsg, err := r.TestDAppV2ZEVM.SenderWithMessage( + &bind.CallOpts{}, + []byte(payload), + ) + require.NoError(r, err) + require.Equal(r, r.ZEVMAuth.From, senderForMsg) + + // check the balance of revert address is equal to withdraw amount + balance, err = r.SOLZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + + require.Equal(r, withdrawAmount.Int64(), balance.Int64()) +} diff --git a/e2e/e2etests/test_spl_deposit_and_call.go b/e2e/e2etests/test_spl_deposit_and_call.go index 945147abc2..4987f86b9a 100644 --- a/e2e/e2etests/test_spl_deposit_and_call.go +++ b/e2e/e2etests/test_spl_deposit_and_call.go @@ -7,9 +7,9 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/e2etests/test_spl_withdraw_and_call.go b/e2e/e2etests/test_spl_withdraw_and_call.go index 8b2c2e6a83..c7eaecc95b 100644 --- a/e2e/e2etests/test_spl_withdraw_and_call.go +++ b/e2e/e2etests/test_spl_withdraw_and_call.go @@ -9,6 +9,7 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" @@ -65,7 +66,15 @@ func TestSPLWithdrawAndCall(r *runner.E2ERunner, args []string) { r.Logger.Info("connected pda balance of SPL before withdraw: %s", connectedPdaBalanceBefore.Value.Amount) // withdraw - tx := r.WithdrawAndCallSPLZRC20(runner.ConnectedSPLProgramID, withdrawAmount, approvedAmount, []byte("hello")) + tx := r.WithdrawAndCallSPLZRC20( + runner.ConnectedSPLProgramID, + withdrawAmount, + approvedAmount, + []byte("hello"), + 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) diff --git a/e2e/e2etests/test_spl_withdraw_and_call_revert.go b/e2e/e2etests/test_spl_withdraw_and_call_revert.go new file mode 100644 index 0000000000..46f41061d8 --- /dev/null +++ b/e2e/e2etests/test_spl_withdraw_and_call_revert.go @@ -0,0 +1,101 @@ +package e2etests + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + solanacontract "github.com/zeta-chain/node/pkg/contracts/solana" + "github.com/zeta-chain/node/testutil/sample" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +// TestSPLWithdrawAndCall executes withdrawAndCall on zevm and calls connected program on solana +// execution is reverted in connected program on_call function +func TestSPLWithdrawAndCallRevert(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + withdrawAmount := utils.ParseBigInt(r, args[0]) + + // get SPL ZRC20 balance before withdraw + zrc20BalanceBefore, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL before withdraw: %d", zrc20BalanceBefore) + + require.Equal(r, 1, zrc20BalanceBefore.Cmp(withdrawAmount), "Insufficient balance for withdrawal") + + // parse withdraw amount (in lamports), approve amount is 1 SOL + approvedAmount := new(big.Int).SetUint64(solana.LAMPORTS_PER_SOL) + require.Equal( + r, + -1, + withdrawAmount.Cmp(approvedAmount), + "Withdrawal amount must be less than the %v", + approvedAmount, + ) + + // load deployer private key + privkey := r.GetSolanaPrivKey() + + // get receiver ata balance before withdraw + receiverAta := r.ResolveSolanaATA(privkey, privkey.PublicKey(), r.SPLAddr) + receiverBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance(r.Ctx, receiverAta, rpc.CommitmentConfirmed) + require.NoError(r, err) + r.Logger.Info("receiver balance of SPL before withdraw: %s", receiverBalanceBefore.Value.Amount) + + connected := solana.MustPublicKeyFromBase58(runner.ConnectedSPLProgramID.String()) + connectedPda, err := solanacontract.ComputeConnectedSPLPdaAddress(connected) + require.NoError(r, err) + + connectedPdaAta := r.ResolveSolanaATA(privkey, connectedPda, r.SPLAddr) + connectedPdaBalanceBefore, err := r.SolanaClient.GetTokenAccountBalance( + r.Ctx, + connectedPdaAta, + rpc.CommitmentConfirmed, + ) + require.NoError(r, err) + r.Logger.Info("connected pda balance of SPL before withdraw: %s", connectedPdaBalanceBefore.Value.Amount) + + // use a random address to get the revert amount + revertAddress := sample.EthAddress() + balance, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + require.EqualValues(r, int64(0), balance.Int64()) + + // withdraw + tx := r.WithdrawAndCallSPLZRC20( + runner.ConnectedSPLProgramID, + withdrawAmount, + approvedAmount, + []byte("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) + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) + + // get SPL ZRC20 balance after withdraw + zrc20BalanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SPL after withdraw: %d", zrc20BalanceAfter) + + balanceAfter, err := r.SPLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SOL after withdraw: %d", balanceAfter) + + // check the balance of revert address is equal to withdraw amount + balance, err = r.SPLZRC20.BalanceOf(&bind.CallOpts{}, revertAddress) + require.NoError(r, err) + + require.Equal(r, withdrawAmount.Int64(), balance.Int64()) +} diff --git a/e2e/e2etests/test_sui_deposit.go b/e2e/e2etests/test_sui_deposit.go new file mode 100644 index 0000000000..e69f0fd4e3 --- /dev/null +++ b/e2e/e2etests/test_sui_deposit.go @@ -0,0 +1,37 @@ +package e2etests + +import ( + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/coin" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSuiDeposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := utils.ParseBigInt(r, args[0]) + + oldBalance, err := r.SUIZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + + // make the deposit transaction + resp := r.SuiDepositSUI(r.EVMAddress(), math.NewUintFromBigInt(amount)) + + r.Logger.Info("Sui deposit tx: %s", resp.Digest) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, resp.Digest, r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.EqualValues(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + require.EqualValues(r, coin.CoinType_Gas, cctx.InboundParams.CoinType) + require.EqualValues(r, amount.Uint64(), cctx.InboundParams.Amount.Uint64()) + + newBalance, err := r.SUIZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + require.EqualValues(r, oldBalance.Add(oldBalance, amount).Uint64(), newBalance.Uint64()) +} diff --git a/e2e/e2etests/test_sui_deposit_and_call.go b/e2e/e2etests/test_sui_deposit_and_call.go new file mode 100644 index 0000000000..f2ea2a331b --- /dev/null +++ b/e2e/e2etests/test_sui_deposit_and_call.go @@ -0,0 +1,43 @@ +package e2etests + +import ( + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/coin" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSuiDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := utils.ParseBigInt(r, args[0]) + + oldBalance, err := r.SUIZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + payload := randomPayload(r) + + // make the deposit transaction + resp := r.SuiDepositAndCallSUI(r.TestDAppV2ZEVMAddr, math.NewUintFromBigInt(amount), []byte(payload)) + + r.Logger.Info("Sui deposit and call tx: %s", resp.Digest) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, resp.Digest, r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.EqualValues(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + require.EqualValues(r, coin.CoinType_Gas, cctx.InboundParams.CoinType) + require.EqualValues(r, amount.Uint64(), cctx.InboundParams.Amount.Uint64()) + require.True(r, cctx.InboundParams.IsCrossChainCall) + + newBalance, err := r.SUIZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.EqualValues(r, oldBalance.Add(oldBalance, amount).Uint64(), newBalance.Uint64()) + + // check the payload was received on the contract + r.AssertTestDAppZEVMCalled(true, payload, amount) +} diff --git a/e2e/e2etests/test_sui_token_deposit.go b/e2e/e2etests/test_sui_token_deposit.go new file mode 100644 index 0000000000..5b4832f953 --- /dev/null +++ b/e2e/e2etests/test_sui_token_deposit.go @@ -0,0 +1,37 @@ +package e2etests + +import ( + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/coin" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSuiTokenDeposit(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := utils.ParseBigInt(r, args[0]) + + oldBalance, err := r.SuiTokenZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + + // make the deposit transaction + resp := r.SuiDepositFungibleToken(r.EVMAddress(), math.NewUintFromBigInt(amount)) + + r.Logger.Info("Sui deposit tx: %s", resp.Digest) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, resp.Digest, r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.EqualValues(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + require.EqualValues(r, coin.CoinType_ERC20, cctx.InboundParams.CoinType) + require.EqualValues(r, amount.Uint64(), cctx.InboundParams.Amount.Uint64()) + + newBalance, err := r.SuiTokenZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + require.EqualValues(r, oldBalance.Add(oldBalance, amount).Uint64(), newBalance.Uint64()) +} diff --git a/e2e/e2etests/test_sui_token_deposit_and_call.go b/e2e/e2etests/test_sui_token_deposit_and_call.go new file mode 100644 index 0000000000..36d6f2e69d --- /dev/null +++ b/e2e/e2etests/test_sui_token_deposit_and_call.go @@ -0,0 +1,42 @@ +package e2etests + +import ( + "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/e2e/runner" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/coin" + crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" +) + +func TestSuiTokenDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + amount := utils.ParseBigInt(r, args[0]) + + oldBalance, err := r.SuiTokenZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + + payload := randomPayload(r) + + // make the deposit transaction + resp := r.SuiFungibleTokenDepositAndCall(r.TestDAppV2ZEVMAddr, math.NewUintFromBigInt(amount), []byte(payload)) + + r.Logger.Info("Sui deposit and call tx: %s", resp.Digest) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, resp.Digest, r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "deposit") + require.EqualValues(r, crosschaintypes.CctxStatus_OutboundMined, cctx.CctxStatus.Status) + require.EqualValues(r, coin.CoinType_ERC20, cctx.InboundParams.CoinType) + require.EqualValues(r, amount.Uint64(), cctx.InboundParams.Amount.Uint64()) + + // check the payload was received on the contract + r.AssertTestDAppZEVMCalled(true, payload, amount) + + newBalance, err := r.SuiTokenZRC20.BalanceOf(&bind.CallOpts{}, r.TestDAppV2ZEVMAddr) + require.NoError(r, err) + require.EqualValues(r, oldBalance.Add(oldBalance, amount).Uint64(), newBalance.Uint64()) +} diff --git a/e2e/e2etests/test_ton_deposit_and_call.go b/e2e/e2etests/test_ton_deposit_and_call.go index 44fde2cb8d..0f329a272d 100644 --- a/e2e/e2etests/test_ton_deposit_and_call.go +++ b/e2e/e2etests/test_ton_deposit_and_call.go @@ -4,10 +4,10 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" - testcontract "github.com/zeta-chain/node/testutil/contracts" ) func TestTONDepositAndCall(r *runner.E2ERunner, args []string) { diff --git a/e2e/e2etests/test_ton_deposit_refund.go b/e2e/e2etests/test_ton_deposit_refund.go index 47d45c840f..bcce8b7a72 100644 --- a/e2e/e2etests/test_ton_deposit_refund.go +++ b/e2e/e2etests/test_ton_deposit_refund.go @@ -3,9 +3,9 @@ package e2etests import ( "github.com/stretchr/testify/require" + testcontract "github.com/zeta-chain/node/e2e/contracts/reverter" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - testcontract "github.com/zeta-chain/node/testutil/contracts" cctypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/e2etests/test_zevm_to_evm_call_through_contract.go b/e2e/e2etests/test_zevm_to_evm_call_through_contract.go index 1417513836..05f2cab5f3 100644 --- a/e2e/e2etests/test_zevm_to_evm_call_through_contract.go +++ b/e2e/e2etests/test_zevm_to_evm_call_through_contract.go @@ -6,9 +6,9 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/e2e/contracts/gatewayzevmcaller" "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" - gatewayzevmcaller "github.com/zeta-chain/node/pkg/contracts/gatewayzevmcaller" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" ) diff --git a/e2e/runner/admin_evm.go b/e2e/runner/admin_evm.go index e9dad09a1d..5baf4a124d 100644 --- a/e2e/runner/admin_evm.go +++ b/e2e/runner/admin_evm.go @@ -7,6 +7,7 @@ import ( "github.com/zeta-chain/node/e2e/utils" ) +// UpdateTSSAddressForConnector updates the TSS address for the connector contract func (r *E2ERunner) UpdateTSSAddressForConnector() { require.NoError(r, r.SetTSSAddresses()) @@ -21,6 +22,7 @@ func (r *E2ERunner) UpdateTSSAddressForConnector() { require.Equal(r, r.TSSAddress, tssAddressOnConnector) } +// UpdateTSSAddressForERC20custody updates the TSS address for the ERC20 custody contract func (r *E2ERunner) UpdateTSSAddressForERC20custody() { require.NoError(r, r.SetTSSAddresses()) @@ -35,3 +37,18 @@ func (r *E2ERunner) UpdateTSSAddressForERC20custody() { require.NoError(r, err) require.Equal(r, r.TSSAddress, tssAddressOnCustody) } + +// UpdateTSSAddressForGateway updates the TSS address for the gateway contract +func (r *E2ERunner) UpdateTSSAddressForGateway() { + require.NoError(r, r.SetTSSAddresses()) + + tx, err := r.GatewayEVM.UpdateTSSAddress(r.EVMAuth, r.TSSAddress) + require.NoError(r, err) + r.Logger.Info("TSS Gateway Address Update Tx: %s", tx.Hash().String()) + receipt := utils.MustWaitForTxReceipt(r.Ctx, r.EVMClient, tx, r.Logger, r.ReceiptTimeout) + utils.RequireTxSuccessful(r, receipt) + + tssAddressOnGateway, err := r.GatewayEVM.TssAddress(&bind.CallOpts{Context: r.Ctx}) + require.NoError(r, err) + require.Equal(r, r.TSSAddress, tssAddressOnGateway) +} diff --git a/e2e/runner/contract_upgrade_solana.go b/e2e/runner/contract_upgrade_solana.go new file mode 100644 index 0000000000..e21239b661 --- /dev/null +++ b/e2e/runner/contract_upgrade_solana.go @@ -0,0 +1,129 @@ +package runner + +import ( + "crypto/sha256" + "encoding/base64" + "fmt" + "os/exec" + "time" + + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/near/borsh-go" + "github.com/stretchr/testify/require" + + solanacontracts "github.com/zeta-chain/node/pkg/contracts/solana" +) + +// VerifySolanaContractsUpgrade checks if the Solana contracts are upgraded +func (r *E2ERunner) VerifySolanaContractsUpgrade(deployerPrivateKey string) bool { + pdaComputed := r.ComputePdaAddress() + pdaInfo, err := r.SolanaClient.GetAccountInfoWithOpts(r.Ctx, pdaComputed, &rpc.GetAccountInfoOpts{ + Commitment: rpc.CommitmentConfirmed, + }) + require.NoError(r, err) + + // deserialize the PDA info + pdaDataBefore := solanacontracts.PdaInfo{} + err = borsh.Deserialize(&pdaDataBefore, pdaInfo.Bytes()) + require.NoError(r, err) + + if err := triggerSolanaUpgrade(); err != nil { + r.Logger.Error("failed to trigger Solana upgrade: %v", err) + return false + } + r.Logger.Print("⚙️ Solana upgrade completed") + + pdaInfo, err = r.SolanaClient.GetAccountInfoWithOpts(r.Ctx, pdaComputed, &rpc.GetAccountInfoOpts{ + Commitment: rpc.CommitmentConfirmed, + }) + require.NoError(r, err) + + // deserialize the PDA info + pdaDataAfter := solanacontracts.PdaInfo{} + err = borsh.Deserialize(&pdaDataAfter, pdaInfo.Bytes()) + require.NoError(r, err) + + // Verify that data does not change + require.Equal(r, pdaDataBefore.Nonce, pdaDataAfter.Nonce) + require.Equal( + r, + ethcommon.BytesToAddress(pdaDataBefore.TssAddress[:]), + ethcommon.BytesToAddress(pdaDataAfter.TssAddress[:]), + ) + require.Equal(r, pdaDataBefore.Authority, pdaDataAfter.Authority) + require.Equal(r, pdaDataBefore.ChainID, pdaDataAfter.ChainID) + require.Equal(r, pdaDataBefore.DepositPaused, pdaDataAfter.DepositPaused) + return r.VerifyUpgradedInstruction(deployerPrivateKey) +} + +func (r *E2ERunner) VerifyUpgradedInstruction(deployerPrivateKey string) bool { + privkey, err := solana.PrivateKeyFromBase58(deployerPrivateKey) + require.NoError(r, err) + // Calculate the instruction discriminator for "upgraded" + // Anchor uses the first 8 bytes of the sha256 hash of "global:upgraded" + // Manually generating the discriminator as there is just one extra function in the new program + discriminator := getAnchorDiscriminator("upgraded") + // Build instruction + data := append(discriminator, []byte{}...) + + var instConnected solana.GenericInstruction + accountSliceConnected := []*solana.AccountMeta{} + accountSliceConnected = append(accountSliceConnected, solana.Meta(privkey.PublicKey()).WRITE().SIGNER()) + instConnected.ProgID = r.GatewayProgram + instConnected.AccountValues = accountSliceConnected + instConnected.DataBytes = data + + // create and sign the transaction + signedTx := r.CreateSignedTransaction([]solana.Instruction{&instConnected}, privkey, []solana.PrivateKey{}) + + // broadcast the transaction and wait for finalization + _, out := r.BroadcastTxSync(signedTx) + r.Logger.Info("upgrade logs: %v", out.Meta.LogMessages) + + decoded, err := base64.StdEncoding.DecodeString(out.Meta.ReturnData.Data.String()) + require.NoError(r, err) + return decoded[0] == 1 +} + +func getAnchorDiscriminator(methodName string) []byte { + // In Anchor, the namespace is "global" + method name + namespace := fmt.Sprintf("global:%s", methodName) + + // Calculate SHA256 + hash := sha256.Sum256([]byte(namespace)) + + // Return first 8 bytes + return hash[:8] +} + +// triggerSolanaUpgrade triggers the Solana upgrade by creating a file `execute-update` on the Solana container +// The shell script on the Solana container will remove the file after completing the upgrade +// Refer: contrib/localnet/solana/start-solana.sh +func triggerSolanaUpgrade() error { + // Create the execute-update file on Solana container + createCmd := exec.Command("ssh", "root@solana", "touch", "/data/execute-update") + if err := createCmd.Run(); err != nil { + return fmt.Errorf("failed to create execute-update file: %w", err) + } + + // Start checking for file removal with timeout + timeout := time.After(2 * time.Minute) + tick := time.Tick(2 * time.Second) + + for { + select { + case <-timeout: + return fmt.Errorf("timeout waiting for Solana upgrade to complete") + + case <-tick: + // Check if file still exists + checkCmd := exec.Command("ssh", "root@solana", "test", "-f", "/data/execute-update") + if err := checkCmd.Run(); err != nil { + // If the command fails, it means the file doesn't exist (upgrade completed) + return nil + } + } + } +} diff --git a/e2e/runner/logger.go b/e2e/runner/logger.go index b96c2f3009..dd19e9a9e4 100644 --- a/e2e/runner/logger.go +++ b/e2e/runner/logger.go @@ -130,7 +130,7 @@ func (l *Logger) CCTX(cctx crosschaintypes.CrossChainTx, name string) { l.Info(" TxHeight: %d", cctx.InboundParams.ObservedExternalHeight) l.Info(" BallotIndex: %s", cctx.InboundParams.BallotIndex) l.Info(" Amount: %s", cctx.InboundParams.Amount.String()) - l.Info(" CoinType: %s", cctx.InboundParams.CoinType.String()) + l.Info(" FungibleTokenCoinType: %s", cctx.InboundParams.CoinType.String()) l.Info(" SenderChainID: %d", cctx.InboundParams.SenderChainId) l.Info(" Origin: %s", cctx.InboundParams.TxOrigin) if cctx.InboundParams.Sender != "" { @@ -160,7 +160,7 @@ func (l *Logger) CCTX(cctx crosschaintypes.CrossChainTx, name string) { l.Info(" EffectiveGasPrice: %s", outTxParam.EffectiveGasPrice.String()) l.Info(" EffectiveGasLimit: %d", outTxParam.EffectiveGasLimit) l.Info(" Amount: %s", outTxParam.Amount.String()) - l.Info(" CoinType: %s", outTxParam.CoinType.String()) + l.Info(" FungibleTokenCoinType: %s", outTxParam.CoinType.String()) l.Info(" Receiver: %s", outTxParam.Receiver) l.Info(" ReceiverChainID: %d", outTxParam.ReceiverChainId) } diff --git a/e2e/runner/runner.go b/e2e/runner/runner.go index bfb0027fe7..b114795059 100644 --- a/e2e/runner/runner.go +++ b/e2e/runner/runner.go @@ -34,12 +34,13 @@ import ( "github.com/zeta-chain/node/e2e/config" "github.com/zeta-chain/node/e2e/contracts/contextapp" "github.com/zeta-chain/node/e2e/contracts/erc20" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" "github.com/zeta-chain/node/e2e/contracts/zevmswap" tonrunner "github.com/zeta-chain/node/e2e/runner/ton" "github.com/zeta-chain/node/e2e/txserver" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/constant" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" + "github.com/zeta-chain/node/pkg/contracts/sui" toncontracts "github.com/zeta-chain/node/pkg/contracts/ton" "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-core/contracts/uniswapv2factory.sol" uniswapv2router "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-periphery/contracts/uniswapv2router02.sol" @@ -116,6 +117,15 @@ type E2ERunner struct { GatewayProgram solana.PublicKey SPLAddr solana.PublicKey + // contract Sui + SuiGateway *sui.Gateway + + // SuiTokenCoinType is the coin type identifying the fungible token for SUI + SuiTokenCoinType string + + // SuiTokenTreasuryCap is the treasury cap for the SUI token that allows minting, only using in local tests + SuiTokenTreasuryCap string + // contracts evm ZetaEthAddr ethcommon.Address ZetaEth *zetaeth.ZetaEth @@ -132,18 +142,25 @@ type E2ERunner struct { TestDAppV2EVM *testdappv2.TestDAppV2 // contracts zevm - ERC20ZRC20Addr ethcommon.Address - ERC20ZRC20 *zrc20.ZRC20 - SPLZRC20Addr ethcommon.Address - SPLZRC20 *zrc20.ZRC20 - ETHZRC20Addr ethcommon.Address - ETHZRC20 *zrc20.ZRC20 - BTCZRC20Addr ethcommon.Address - BTCZRC20 *zrc20.ZRC20 - SOLZRC20Addr ethcommon.Address - SOLZRC20 *zrc20.ZRC20 - TONZRC20Addr ethcommon.Address - TONZRC20 *zrc20.ZRC20 + // zrc20 contracts + ERC20ZRC20Addr ethcommon.Address + ERC20ZRC20 *zrc20.ZRC20 + SPLZRC20Addr ethcommon.Address + SPLZRC20 *zrc20.ZRC20 + ETHZRC20Addr ethcommon.Address + ETHZRC20 *zrc20.ZRC20 + BTCZRC20Addr ethcommon.Address + BTCZRC20 *zrc20.ZRC20 + SOLZRC20Addr ethcommon.Address + SOLZRC20 *zrc20.ZRC20 + TONZRC20Addr ethcommon.Address + TONZRC20 *zrc20.ZRC20 + SUIZRC20Addr ethcommon.Address + SUIZRC20 *zrc20.ZRC20 + SuiTokenZRC20Addr ethcommon.Address + SuiTokenZRC20 *zrc20.ZRC20 + + // other contracts UniswapV2FactoryAddr ethcommon.Address UniswapV2Factory *uniswapv2factory.UniswapV2Factory UniswapV2RouterAddr ethcommon.Address @@ -243,6 +260,8 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { r.BTCZRC20Addr = other.BTCZRC20Addr r.SOLZRC20Addr = other.SOLZRC20Addr r.TONZRC20Addr = other.TONZRC20Addr + r.SUIZRC20Addr = other.SUIZRC20Addr + r.SuiTokenZRC20Addr = other.SuiTokenZRC20Addr r.UniswapV2FactoryAddr = other.UniswapV2FactoryAddr r.UniswapV2RouterAddr = other.UniswapV2RouterAddr r.ConnectorZEVMAddr = other.ConnectorZEVMAddr @@ -255,6 +274,10 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { r.GatewayProgram = other.GatewayProgram + r.SuiGateway = other.SuiGateway + r.SuiTokenCoinType = other.SuiTokenCoinType + r.SuiTokenTreasuryCap = other.SuiTokenTreasuryCap + // create instances of contracts r.ZetaEth, err = zetaeth.NewZetaEth(r.ZetaEthAddr, r.EVMClient) if err != nil { @@ -292,6 +315,15 @@ func (r *E2ERunner) CopyAddressesFrom(other *E2ERunner) (err error) { if err != nil { return err } + r.SUIZRC20, err = zrc20.NewZRC20(r.SUIZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + r.SuiTokenZRC20, err = zrc20.NewZRC20(r.SuiTokenZRC20Addr, r.ZEVMClient) + if err != nil { + return err + } + r.UniswapV2Factory, err = uniswapv2factory.NewUniswapV2Factory(r.UniswapV2FactoryAddr, r.ZEVMClient) if err != nil { return err @@ -365,7 +397,15 @@ func (r *E2ERunner) Unlock() { func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print(" --- 📜Solana addresses ---") r.Logger.Print("GatewayProgram: %s", r.GatewayProgram.String()) - r.Logger.Print("SPL: %s", r.SPLAddr.String()) + r.Logger.Print("SPL: %s", r.SPLAddr.String()) + + r.Logger.Print(" --- 📜Sui addresses ---") + if r.SuiGateway != nil { + r.Logger.Print("GatewayPackageID: %s", r.SuiGateway.PackageID()) + r.Logger.Print("GatewayObjectID: %s", r.SuiGateway.ObjectID()) + } else { + r.Logger.Print("💤 Sui tests disabled") + } // zevm contracts r.Logger.Print(" --- 📜zEVM contracts ---") @@ -376,6 +416,8 @@ func (r *E2ERunner) PrintContractAddresses() { r.Logger.Print("SOLZRC20: %s", r.SOLZRC20Addr.Hex()) r.Logger.Print("SPLZRC20: %s", r.SPLZRC20Addr.Hex()) r.Logger.Print("TONZRC20: %s", r.TONZRC20Addr.Hex()) + r.Logger.Print("SUIZRC20: %s", r.SUIZRC20Addr.Hex()) + r.Logger.Print("SuiTokenZRC20: %s", r.SuiTokenZRC20Addr.Hex()) r.Logger.Print("UniswapFactory: %s", r.UniswapV2FactoryAddr.Hex()) r.Logger.Print("UniswapRouter: %s", r.UniswapV2RouterAddr.Hex()) r.Logger.Print("ConnectorZEVM: %s", r.ConnectorZEVMAddr.Hex()) diff --git a/e2e/runner/setup_evm.go b/e2e/runner/setup_evm.go index 91923a845c..651e9b83d6 100644 --- a/e2e/runner/setup_evm.go +++ b/e2e/runner/setup_evm.go @@ -10,10 +10,10 @@ import ( erc20custodyv2 "github.com/zeta-chain/protocol-contracts/pkg/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/gatewayevm.sol" + "github.com/zeta-chain/node/e2e/contracts/erc1967proxy" "github.com/zeta-chain/node/e2e/contracts/erc20" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" "github.com/zeta-chain/node/e2e/utils" - "github.com/zeta-chain/node/pkg/contracts/erc1967proxy" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" ) // SetupEVM setup contracts on EVM with v2 contracts diff --git a/e2e/runner/setup_sui.go b/e2e/runner/setup_sui.go index ad855dbc6c..29c4a7d9f8 100644 --- a/e2e/runner/setup_sui.go +++ b/e2e/runner/setup_sui.go @@ -2,14 +2,38 @@ package runner import ( "fmt" + "strings" + "time" + "cosmossdk.io/math" "github.com/block-vision/sui-go-sdk/models" "github.com/block-vision/sui-go-sdk/sui" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" "github.com/stretchr/testify/require" + "github.com/zeta-chain/protocol-contracts/pkg/zrc20.sol" suicontract "github.com/zeta-chain/node/e2e/contracts/sui" + "github.com/zeta-chain/node/e2e/txserver" + "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/constant" + zetasui "github.com/zeta-chain/node/pkg/contracts/sui" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" + observertypes "github.com/zeta-chain/node/x/observer/types" ) +const changeTypeCreated = "created" + +// RequestSuiFromFaucet requests SUI tokens from the faucet for the runner account +func (r *E2ERunner) RequestSuiFromFaucet(faucetURL, recipient string) { + header := map[string]string{} + err := sui.RequestSuiFromFaucet(faucetURL, recipient, header) + require.NoError(r, err, "sui faucet request to %s", faucetURL) +} + +// SetupSui initializes the gateway package on Sui and initialize the chain params on ZetaChain func (r *E2ERunner) SetupSui(faucetURL string) { r.Logger.Print("⚙️ initializing gateway package on Sui") @@ -23,7 +47,7 @@ func (r *E2ERunner) SetupSui(faucetURL string) { client := r.Clients.Sui - publishReq, err := client.Publish(r.Ctx, models.PublishRequest{ + publishTx, err := client.Publish(r.Ctx, models.PublishRequest{ Sender: deployerAddress, CompiledModules: []string{suicontract.GatewayBytecodeBase64()}, Dependencies: []string{ @@ -34,11 +58,11 @@ func (r *E2ERunner) SetupSui(faucetURL string) { }) require.NoError(r, err, "create publish tx") - signature, err := deployerSigner.SignTransactionBlock(publishReq.TxBytes) + signature, err := deployerSigner.SignTxBlock(publishTx) require.NoError(r, err, "sign transaction") resp, err := client.SuiExecuteTransactionBlock(r.Ctx, models.SuiExecuteTransactionBlockRequest{ - TxBytes: publishReq.TxBytes, + TxBytes: publishTx.TxBytes, Signature: []string{signature}, Options: models.SuiTransactionBlockOptions{ ShowEffects: true, @@ -50,7 +74,8 @@ func (r *E2ERunner) SetupSui(faucetURL string) { }) require.NoError(r, err) - var packageID, objectID string + // find packageID + var packageID, gatewayID, whitelistID string for _, change := range resp.ObjectChanges { if change.Type == "published" { packageID = change.PackageId @@ -58,13 +83,221 @@ func (r *E2ERunner) SetupSui(faucetURL string) { } require.NotEmpty(r, packageID, "find packageID") + // find gateway objectID gatewayType := fmt.Sprintf("%s::gateway::Gateway", packageID) for _, change := range resp.ObjectChanges { - if change.Type == "created" && change.ObjectType == gatewayType { - objectID = change.ObjectId + if change.Type == changeTypeCreated && change.ObjectType == gatewayType { + gatewayID = change.ObjectId + } + } + require.NotEmpty(r, gatewayID, "find gatewayID") + + // find whitelist objectID + whitelistType := fmt.Sprintf("%s::gateway::WhitelistCap", packageID) + for _, change := range resp.ObjectChanges { + if change.Type == changeTypeCreated && change.ObjectType == whitelistType { + whitelistID = change.ObjectId + } + } + + // Set sui gateway + r.SuiGateway = zetasui.NewGateway(packageID, gatewayID) + + // deploy SUI zrc20 + r.deploySUIZRC20() + + // deploy fake USDC + fakeUSDCCoinType, treasuryCap := r.deployFakeUSDC() + r.whitelistSuiFakeUSDC(deployerSigner, fakeUSDCCoinType, whitelistID) + + r.SuiTokenCoinType = fakeUSDCCoinType + r.SuiTokenTreasuryCap = treasuryCap + + // set the chain params + err = r.setSuiChainParams() + require.NoError(r, err) +} + +// deploySUIZRC20 deploys the SUI zrc20 on ZetaChain +func (r *E2ERunner) deploySUIZRC20() { + // send message to deploy SUI zrc20 + liqCap := math.NewUint(10e18) + adminAddr := r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName) + _, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, fungibletypes.NewMsgDeployFungibleCoinZRC20( + adminAddr, + "", + chains.SuiLocalnet.ChainId, + 9, + "SUI", + "SUI", + coin.CoinType_Gas, + 100000, + &liqCap, + )) + require.NoError(r, err) + + // set the address in the store + r.SetupSUIZRC20() +} + +// deployFakeUSDC deploys the FakeUSDC contract on Sui +// it returns the coinType to be used as asset value for zrc20 and treasuryCap object ID that allows to mint tokens +func (r *E2ERunner) deployFakeUSDC() (string, string) { + client := r.Clients.Sui + deployerSigner, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + deployerAddress := deployerSigner.Address() + + publishReq, err := client.Publish(r.Ctx, models.PublishRequest{ + Sender: deployerAddress, + CompiledModules: []string{suicontract.FakeUSDCBytecodeBase64()}, + Dependencies: []string{ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + }, + GasBudget: "5000000000", + }) + require.NoError(r, err, "create publish tx") + + signature, err := deployerSigner.SignTxBlock(publishReq) + require.NoError(r, err, "sign transaction") + + resp, err := client.SuiExecuteTransactionBlock(r.Ctx, models.SuiExecuteTransactionBlockRequest{ + TxBytes: publishReq.TxBytes, + Signature: []string{signature}, + Options: models.SuiTransactionBlockOptions{ + ShowEffects: true, + ShowBalanceChanges: true, + ShowEvents: true, + ShowObjectChanges: true, + }, + RequestType: "WaitForLocalExecution", + }) + require.NoError(r, err) + + var packageID, treasuryCap string + for _, change := range resp.ObjectChanges { + if change.Type == "published" { + packageID = change.PackageId + } + } + require.NotEmpty(r, packageID, "find packageID") + + for _, change := range resp.ObjectChanges { + if change.Type == changeTypeCreated && strings.Contains(change.ObjectType, "TreasuryCap") { + treasuryCap = change.ObjectId } } - require.NotEmpty(r, objectID, "find objectID") + require.NotEmpty(r, treasuryCap, "find objectID") + + coinType := packageID + "::fake_usdc::FAKE_USDC" + + // strip 0x from packageID + coinType = coinType[2:] + + return coinType, treasuryCap +} + +// whitelistSuiFakeUSDC deploys the FakeUSDC zrc20 on ZetaChain and whitelist it +func (r *E2ERunner) whitelistSuiFakeUSDC(signer *zetasui.SignerSecp256k1, fakeUSDCCoinType, whitelistCap string) { + // we use DeployFungibleCoinZRC20 and whitelist manually because whitelist cctx are currently not supported for Sui + // TODO: change this logic and use MsgWhitelistERC20 once it's supported + // https://github.com/zeta-chain/node/issues/3569 + + // deploy zrc20 + liqCap := math.NewUint(10e18) + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, fungibletypes.NewMsgDeployFungibleCoinZRC20( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + fakeUSDCCoinType, + chains.SuiLocalnet.ChainId, + 6, + "Sui's FakeUSDC", + "USDC.SUI", + coin.CoinType_ERC20, + 100000, + &liqCap, + )) + require.NoError(r, err) + + // extract the zrc20 address from event and set the erc20 address in the runner + deployedEvent, ok := txserver.EventOfType[*fungibletypes.EventZRC20Deployed](res.Events) + require.True(r, ok, "unable to find deployed zrc20 event") + + r.SuiTokenZRC20Addr = ethcommon.HexToAddress(deployedEvent.Contract) + require.NotEqualValues(r, ethcommon.Address{}, r.SuiTokenZRC20Addr) + r.SuiTokenZRC20, err = zrc20.NewZRC20(r.SuiTokenZRC20Addr, r.ZEVMClient) + require.NoError(r, err) + + // whitelist zrc20 + tx, err := r.Clients.Sui.MoveCall(r.Ctx, models.MoveCallRequest{ + Signer: signer.Address(), + PackageObjectId: r.SuiGateway.PackageID(), + Module: "gateway", + Function: "whitelist", + TypeArguments: []any{"0x" + fakeUSDCCoinType}, + Arguments: []any{r.SuiGateway.ObjectID(), whitelistCap}, + GasBudget: "5000000000", + }) + require.NoError(r, err) + + r.suiExecuteTx(signer, tx) +} + +// set the chain params for Sui +func (r *E2ERunner) setSuiChainParams() error { + if r.ZetaTxServer == nil { + return errors.New("ZetaTxServer is not initialized") + } + + creator := r.ZetaTxServer.MustGetAccountAddressFromName(utils.OperationalPolicyName) + + chainID := chains.SuiLocalnet.ChainId + + chainParams := &observertypes.ChainParams{ + ChainId: chainID, + ZetaTokenContractAddress: constant.EVMZeroAddress, + ConnectorContractAddress: constant.EVMZeroAddress, + Erc20CustodyContractAddress: constant.EVMZeroAddress, + GasPriceTicker: 5, + WatchUtxoTicker: 0, + InboundTicker: 2, + OutboundTicker: 2, + OutboundScheduleInterval: 2, + OutboundScheduleLookahead: 5, + BallotThreshold: observertypes.DefaultBallotThreshold, + MinObserverDelegation: observertypes.DefaultMinObserverDelegation, + IsSupported: true, + GatewayAddress: fmt.Sprintf("%s,%s", r.SuiGateway.PackageID(), r.SuiGateway.ObjectID()), + ConfirmationParams: &observertypes.ConfirmationParams{ + SafeInboundCount: 1, + SafeOutboundCount: 1, + FastInboundCount: 1, + FastOutboundCount: 1, + }, + ConfirmationCount: 1, // still need to be provided for now + } + if err := r.ZetaTxServer.UpdateChainParams(chainParams); err != nil { + return errors.Wrap(err, "unable to broadcast solana chain params tx") + } + + resetMsg := observertypes.NewMsgResetChainNonces(creator, chainID, 0, 0) + if _, err := r.ZetaTxServer.BroadcastTx(utils.OperationalPolicyName, resetMsg); err != nil { + return errors.Wrap(err, "unable to broadcast solana chain nonce reset tx") + } + + query := &observertypes.QueryGetChainParamsForChainRequest{ChainId: chainID} + + const duration = 2 * time.Second + + for i := 0; i < 10; i++ { + _, err := r.ObserverClient.GetChainParamsForChain(r.Ctx, query) + if err == nil { + r.Logger.Print("⚙️ Sui chain params are set") + return nil + } + + time.Sleep(duration) + } - // TODO: save IDs in config and configure chain + return errors.New("unable to set Sui chain params") } diff --git a/e2e/runner/setup_zevm.go b/e2e/runner/setup_zevm.go index 172bd54330..f6ad07be8c 100644 --- a/e2e/runner/setup_zevm.go +++ b/e2e/runner/setup_zevm.go @@ -15,11 +15,11 @@ import ( connectorzevm "github.com/zeta-chain/protocol-contracts/pkg/zetaconnectorzevm.sol" "github.com/zeta-chain/protocol-contracts/pkg/zrc20.sol" + "github.com/zeta-chain/node/e2e/contracts/erc1967proxy" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" "github.com/zeta-chain/node/e2e/txserver" e2eutils "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/contracts/erc1967proxy" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-core/contracts/uniswapv2factory.sol" uniswapv2router "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-periphery/contracts/uniswapv2router02.sol" fungibletypes "github.com/zeta-chain/node/x/fungible/types" @@ -32,8 +32,6 @@ var EmissionsPoolFunding = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(2e7)) // SetTSSAddresses set TSS addresses from information queried from ZetaChain func (r *E2ERunner) SetTSSAddresses() error { - r.Logger.Print("⚙️ setting up TSS address") - btcChainID, err := chains.GetBTCChainIDFromChainParams(r.BitcoinParams) if err != nil { return err @@ -170,6 +168,27 @@ func (r *E2ERunner) SetupTONZRC20() { r.TONZRC20 = TONZRC20 } +// SetupSUIZRC20 sets up the SUI ZRC20 in the runner from the values queried from the chain +func (r *E2ERunner) SetupSUIZRC20() { + chainID := chains.SuiLocalnet.ChainId + + // noop + if r.skipChainOperations(chainID) { + return + } + + SUIZRC20Addr, err := r.SystemContract.GasCoinZRC20ByChainId(&bind.CallOpts{}, big.NewInt(chainID)) + require.NoError(r, err) + + r.SUIZRC20Addr = SUIZRC20Addr + r.Logger.Info("SUI ZRC20 address: %s", SUIZRC20Addr.Hex()) + + SUIZRC20, err := zrc20.NewZRC20(SUIZRC20Addr, r.ZEVMClient) + require.NoError(r, err) + + r.SUIZRC20 = SUIZRC20 +} + // EnableHeaderVerification enables the header verification for the given chain IDs func (r *E2ERunner) EnableHeaderVerification(chainIDList []int64) error { r.Logger.Print("⚙️ enabling verification flags for block headers") diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 053f09a27a..3ebe67cc92 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -347,7 +347,7 @@ func (r *E2ERunner) DeploySPL(privateKey *solana.PrivateKey, whitelist bool) *so return mintAccount } -// BroadcastTxSync broadcasts a transaction once and checks if it's confirmed +// BroadcastTxSyncOnce broadcasts a transaction once and checks if it's confirmed func (r *E2ERunner) BroadcastTxSyncOnce(tx *solana.Transaction) (solana.Signature, *rpc.GetTransactionResult, bool) { // broadcast the transaction r.Logger.Info("Broadcast once start") @@ -481,6 +481,7 @@ func (r *E2ERunner) WithdrawAndCallSOLZRC20( amount *big.Int, approveAmount *big.Int, data []byte, + revertOptions gatewayzevm.RevertOptions, ) *ethtypes.Transaction { // approve tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.GatewayZEVMAddr, approveAmount) @@ -516,7 +517,7 @@ func (r *E2ERunner) WithdrawAndCallSOLZRC20( r.SOLZRC20Addr, msgEncoded, gatewayzevm.CallOptions{GasLimit: big.NewInt(250000)}, - gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + revertOptions, ) require.NoError(r, err) r.Logger.EVMTransaction(*tx, "withdraw_and_call") @@ -560,6 +561,7 @@ func (r *E2ERunner) WithdrawAndCallSPLZRC20( amount *big.Int, approveAmount *big.Int, data []byte, + revertOptions gatewayzevm.RevertOptions, ) *ethtypes.Transaction { // approve tx, err := r.SOLZRC20.Approve(r.ZEVMAuth, r.GatewayZEVMAddr, approveAmount) @@ -607,7 +609,7 @@ func (r *E2ERunner) WithdrawAndCallSPLZRC20( r.SPLZRC20Addr, msgEncoded, gatewayzevm.CallOptions{GasLimit: big.NewInt(250000)}, - gatewayzevm.RevertOptions{OnRevertGasLimit: big.NewInt(0)}, + revertOptions, ) require.NoError(r, err) r.Logger.EVMTransaction(*tx, "withdraw_and_call") diff --git a/e2e/runner/sui.go b/e2e/runner/sui.go new file mode 100644 index 0000000000..10b1058550 --- /dev/null +++ b/e2e/runner/sui.go @@ -0,0 +1,254 @@ +package runner + +import ( + "fmt" + "strings" + + "cosmossdk.io/math" + "github.com/block-vision/sui-go-sdk/models" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/node/pkg/contracts/sui" +) + +// SuiDepositSUI calls Deposit on Sui +func (r *E2ERunner) SuiDepositSUI( + receiver ethcommon.Address, + amount math.Uint, +) models.SuiTransactionBlockResponse { + signer, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + + // retrieve SUI coin object to deposit + coinObjectID := r.suiSplitSUI(signer, amount) + + // create the tx + return r.suiExecuteDeposit(signer, string(sui.SUI), coinObjectID, receiver) +} + +// SuiDepositAndCallSUI calls DepositAndCall on Sui +func (r *E2ERunner) SuiDepositAndCallSUI( + receiver ethcommon.Address, + amount math.Uint, + payload []byte, +) models.SuiTransactionBlockResponse { + signer, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + + // retrieve SUI coin object to deposit + coinObjectID := r.suiSplitSUI(signer, amount) + + // create the tx + return r.suiExecuteDepositAndCall(signer, string(sui.SUI), coinObjectID, receiver, payload) +} + +// SuiDepositFungibleToken calls Deposit with fungible token on Sui +func (r *E2ERunner) SuiDepositFungibleToken( + receiver ethcommon.Address, + amount math.Uint, +) models.SuiTransactionBlockResponse { + signer, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + + // retrieve SUI coin object to deposit + coinObjectID := r.suiSplitUSDC(signer, amount) + + // create the tx + return r.suiExecuteDeposit(signer, "0x"+r.SuiTokenCoinType, coinObjectID, receiver) +} + +// SuiFungibleTokenDepositAndCall calls DepositAndCall with fungible token on Sui +func (r *E2ERunner) SuiFungibleTokenDepositAndCall( + receiver ethcommon.Address, + amount math.Uint, + payload []byte, +) models.SuiTransactionBlockResponse { + signer, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + + // retrieve SUI coin object to deposit + coinObjectID := r.suiSplitUSDC(signer, amount) + + // create the tx + return r.suiExecuteDepositAndCall(signer, "0x"+r.SuiTokenCoinType, coinObjectID, receiver, payload) +} + +// SuiMintUSDC mints FakeUSDC on Sui to a receiver +// this function requires the signer to be the owner of the trasuryCap +func (r *E2ERunner) SuiMintUSDC( + amount, + receiver string, +) models.SuiTransactionBlockResponse { + signer, err := r.Account.SuiSigner() + require.NoError(r, err, "get deployer signer") + + // extract the package ID from the coin type + splitted := strings.Split(r.SuiTokenCoinType, "::") + require.Len(r, splitted, 3, "coinType should be in format ::::") + packageID := "0x" + splitted[0] + + // create the tx + tx, err := r.Clients.Sui.MoveCall(r.Ctx, models.MoveCallRequest{ + Signer: signer.Address(), + PackageObjectId: packageID, + Module: "fake_usdc", + Function: "mint", + TypeArguments: []any{}, + Arguments: []any{r.SuiTokenTreasuryCap, amount, receiver}, + GasBudget: "5000000000", + }) + require.NoError(r, err) + + return r.suiExecuteTx(signer, tx) +} + +// suiExecuteDeposit executes a deposit on the SUI contract +func (r *E2ERunner) suiExecuteDeposit( + signer *sui.SignerSecp256k1, + coinType string, + coinObjectID string, + receiver ethcommon.Address, +) models.SuiTransactionBlockResponse { + // create the tx + tx, err := r.Clients.Sui.MoveCall(r.Ctx, models.MoveCallRequest{ + Signer: signer.Address(), + PackageObjectId: r.SuiGateway.PackageID(), + Module: "gateway", + Function: "deposit", + TypeArguments: []any{coinType}, + Arguments: []any{r.SuiGateway.ObjectID(), coinObjectID, receiver.Hex()}, + GasBudget: "5000000000", + }) + require.NoError(r, err) + + return r.suiExecuteTx(signer, tx) +} + +// suiExecuteDepositAndCall executes a depositAndCall on the SUI contract +func (r *E2ERunner) suiExecuteDepositAndCall( + signer *sui.SignerSecp256k1, + coinType string, + coinObjectID string, + receiver ethcommon.Address, + payload []byte, +) models.SuiTransactionBlockResponse { + // create the tx + tx, err := r.Clients.Sui.MoveCall(r.Ctx, models.MoveCallRequest{ + Signer: signer.Address(), + PackageObjectId: r.SuiGateway.PackageID(), + Module: "gateway", + Function: "deposit_and_call", + TypeArguments: []any{coinType}, + Arguments: []any{r.SuiGateway.ObjectID(), coinObjectID, receiver.Hex(), payload}, + GasBudget: "5000000000", + }) + require.NoError(r, err) + + return r.suiExecuteTx(signer, tx) +} + +// suiSplitUSDC splits USDC coin and obtain a USDC coin object with the wanted balance +func (r *E2ERunner) suiSplitUSDC(signer *sui.SignerSecp256k1, balance math.Uint) (objID string) { + // find the coin to split + originalCoin := r.suiFindCoinWithBalanceAbove(signer.Address(), balance, "0x"+r.SuiTokenCoinType) + + tx, err := r.Clients.Sui.SplitCoin(r.Ctx, models.SplitCoinRequest{ + Signer: signer.Address(), + CoinObjectId: originalCoin, + SplitAmounts: []string{balance.String()}, + GasBudget: "5000000000", + }) + + require.NoError(r, err) + r.suiExecuteTx(signer, tx) + + // find the split coin + return r.suiFindCoinWithBalance(signer.Address(), balance, "0x"+r.SuiTokenCoinType) +} + +// suiSplitSUI splits SUI coin and obtain a SUI coin object with the wanted balance +func (r *E2ERunner) suiSplitSUI(signer *sui.SignerSecp256k1, balance math.Uint) (objID string) { + // find the coin to split + originalCoin := r.suiFindCoinWithBalanceAbove(signer.Address(), balance, string(sui.SUI)) + + // split the coin using the PaySui API + tx, err := r.Clients.Sui.PaySui(r.Ctx, models.PaySuiRequest{ + Signer: signer.Address(), + SuiObjectId: []string{originalCoin}, + Recipient: []string{signer.Address()}, + Amount: []string{balance.String()}, + GasBudget: "5000000000", + }) + require.NoError(r, err) + + r.suiExecuteTx(signer, tx) + + // find the split coin + return r.suiFindCoinWithBalance(signer.Address(), balance, string(sui.SUI)) +} + +func (r *E2ERunner) suiFindCoinWithBalance( + address string, + balance math.Uint, + coinType string, +) (coinID string) { + return r.suiFindCoin(address, balance, coinType, func(a, b math.Uint) bool { + return a.Equal(b) + }) +} + +func (r *E2ERunner) suiFindCoinWithBalanceAbove( + address string, + balanceAbove math.Uint, + coinType string, +) (coinID string) { + return r.suiFindCoin(address, balanceAbove, coinType, func(a, b math.Uint) bool { + return a.GTE(b) + }) +} + +type compFunc func(a, b math.Uint) bool + +func (r *E2ERunner) suiFindCoin( + address string, + balance math.Uint, + coinType string, + comp compFunc, +) (coinID string) { + res, err := r.Clients.Sui.SuiXGetCoins(r.Ctx, models.SuiXGetCoinsRequest{ + Owner: address, + CoinType: coinType, + }) + require.NoError(r, err) + + for _, data := range res.Data { + coinBalance, err := math.ParseUint(data.Balance) + require.NoError(r, err) + + if comp(coinBalance, balance) { + return data.CoinObjectId + } + } + + require.FailNow(r, fmt.Sprintf("coin %s not found for address %s", coinType, address)) + return "" +} + +func (r *E2ERunner) suiExecuteTx( + signer *sui.SignerSecp256k1, + tx models.TxnMetaData, +) models.SuiTransactionBlockResponse { + // sign the tx + signature, err := signer.SignTxBlock(tx) + require.NoError(r, err, "sign transaction") + + resp, err := r.Clients.Sui.SuiExecuteTransactionBlock(r.Ctx, models.SuiExecuteTransactionBlockRequest{ + TxBytes: tx.TxBytes, + Signature: []string{signature}, + RequestType: "WaitForLocalExecution", + }) + require.NoError(r, err) + + return resp +} diff --git a/e2e/runner/testdapp.go b/e2e/runner/testdapp.go index f9c63d7c67..85f5272296 100644 --- a/e2e/runner/testdapp.go +++ b/e2e/runner/testdapp.go @@ -7,7 +7,7 @@ import ( ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" ) // AssertTestDAppZEVMCalled is a function that asserts the values of the test dapp on the ZEVM diff --git a/e2e/runner/verify.go b/e2e/runner/verify.go index d69ba2c59c..e0dc533136 100644 --- a/e2e/runner/verify.go +++ b/e2e/runner/verify.go @@ -53,9 +53,9 @@ func (r *E2ERunner) VerifySolanaWithdrawalAmountFromCCTX(cctx *crosschaintypes.C // 1st instruction is the withdraw instruction := tx.Message.Instructions[0] - instWithdrae, err := solanacontracts.ParseInstructionWithdraw(instruction) + instWithdraw, err := solanacontracts.ParseInstructionWithdraw(instruction) require.NoError(r, err) // verify the amount - require.Equal(r, amount, instWithdrae.TokenAmount(), "withdraw amount is not correct") + require.Equal(r, amount, instWithdraw.TokenAmount(), "withdraw amount is not correct") } diff --git a/e2e/runner/zevm.go b/e2e/runner/zevm.go index a42b8e2a4f..d2af986062 100644 --- a/e2e/runner/zevm.go +++ b/e2e/runner/zevm.go @@ -12,10 +12,10 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" + "github.com/zeta-chain/node/e2e/contracts/gatewayzevmcaller" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/contracts/gatewayzevmcaller" "github.com/zeta-chain/node/pkg/retry" "github.com/zeta-chain/node/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" diff --git a/e2e/txserver/authority.go b/e2e/txserver/authority.go index c79e6a788a..346ed8a4c5 100644 --- a/e2e/txserver/authority.go +++ b/e2e/txserver/authority.go @@ -4,6 +4,7 @@ import ( "fmt" e2eutils "github.com/zeta-chain/node/e2e/utils" + "github.com/zeta-chain/node/pkg/chains" authoritytypes "github.com/zeta-chain/node/x/authority/types" ) @@ -31,3 +32,27 @@ func (zts *ZetaTxServer) AddAuthorization(msgURL string) error { return nil } + +// UpdateChainInfo sets the chain info in the authority module +func (zts ZetaTxServer) UpdateChainInfo(chain chains.Chain) error { + // retrieve account + accAdmin, err := zts.clientCtx.Keyring.Key(e2eutils.AdminPolicyName) + if err != nil { + return err + } + addrAdmin, err := accAdmin.GetAddress() + if err != nil { + return err + } + + // set chain info + _, err = zts.BroadcastTx(e2eutils.AdminPolicyName, authoritytypes.NewMsgUpdateChainInfo( + addrAdmin.String(), + chain, + )) + if err != nil { + return fmt.Errorf("failed to update chain info: %w", err) + } + + return nil +} diff --git a/e2e/utils/contracts.go b/e2e/utils/contracts.go index 68893234e1..d60899ad2f 100644 --- a/e2e/utils/contracts.go +++ b/e2e/utils/contracts.go @@ -6,7 +6,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" - testcontract "github.com/zeta-chain/node/testutil/contracts" + testcontract "github.com/zeta-chain/node/e2e/contracts/example" ) const ( diff --git a/e2e/utils/sui/signer.go b/e2e/utils/sui/signer.go deleted file mode 100644 index 729abf0d2a..0000000000 --- a/e2e/utils/sui/signer.go +++ /dev/null @@ -1,114 +0,0 @@ -package sui - -import ( - "crypto/sha256" - "encoding/base64" - "encoding/hex" - "fmt" - - "github.com/block-vision/sui-go-sdk/signer" - "github.com/decred/dcrd/dcrec/secp256k1/v4" - secp256k1_ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" - "golang.org/x/crypto/blake2b" -) - -type SignerSecp256k1 struct { - privkey *secp256k1.PrivateKey -} - -func NewSignerSecp256k1FromSecretKey(secret []byte) *SignerSecp256k1 { - privKey := secp256k1.PrivKeyFromBytes(secret) - - return &SignerSecp256k1{ - privkey: privKey, - } -} - -// GetPublicKey returns the compressed public key bytes -func (s *SignerSecp256k1) GetPublicKey() []byte { - pub := s.privkey.PubKey() - - // Create compressed public key format: - // 0x02/0x03 | x-coordinate (32 bytes) - x := pub.X().Bytes() - - // Ensure x coordinate is 32 bytes with leading zeros if needed - paddedX := make([]byte, 32) - copy(paddedX[32-len(x):], x) - - // Prefix with 0x02 for even Y or 0x03 for odd Y - prefix := byte(0x02) - if pub.Y().Bit(0) == 1 { - prefix = 0x03 - } - - return append([]byte{prefix}, paddedX...) -} - -// GetFlaggedPublicKey returns GetPublicKey flagged for use with wallets -// and command line tools -func (s *SignerSecp256k1) GetFlaggedPublicKey() []byte { - return append([]byte{signer.SigntureFlagSecp256k1}, s.GetPublicKey()...) -} - -func (s *SignerSecp256k1) Address() string { - // Get the public key bytes - pubKeyBytes := s.GetPublicKey() - - // Create BLAKE2b hash - hash, err := blake2b.New256(nil) - if err != nil { - // This will never happen with nil key - panic(err) - } - - hash.Write([]byte{signer.SigntureFlagSecp256k1}) - hash.Write(pubKeyBytes) - addrBytes := hash.Sum(nil) - // convert to 0x hex - return "0x" + hex.EncodeToString(addrBytes) -} - -type SignedMessageSerializedSig struct { - Message string `json:"message"` - Signature string `json:"signature"` -} - -func ToSerializedSignature(signature, pubKey []byte) string { - signatureLen := len(signature) - pubKeyLen := len(pubKey) - serializedSignature := make([]byte, 1+signatureLen+pubKeyLen) - serializedSignature[0] = byte(signer.SigntureFlagSecp256k1) - copy(serializedSignature[1:], signature) - copy(serializedSignature[1+signatureLen:], pubKey) - return base64.StdEncoding.EncodeToString(serializedSignature) -} - -// SignTransactionBlock signs an encoded transaction block -func (s *SignerSecp256k1) SignTransactionBlock(txBytesEncoded string) (string, error) { - txBytes, err := base64.StdEncoding.DecodeString(txBytesEncoded) - if err != nil { - return "", fmt.Errorf("decode tx bytes: %w", err) - } - message := messageWithIntent(txBytes) - digest1 := blake2b.Sum256(message) - // this additional hash is required for secp256k1 but not ed25519 - digest2 := sha256.Sum256(digest1[:]) - - sigBytes := secp256k1_ecdsa.SignCompact(s.privkey, digest2[:], false) - // Take R and S, skip recovery byte - sigBytes = sigBytes[1:] - - signature := ToSerializedSignature(sigBytes, s.GetPublicKey()) - return signature, nil -} - -func messageWithIntent(message []byte) []byte { - intent := IntentBytes - intentMessage := make([]byte, len(intent)+len(message)) - copy(intentMessage, intent) - copy(intentMessage[len(intent):], message) - return intentMessage -} - -var IntentBytes = []byte{0, 0, 0} diff --git a/e2e/utils/sui/signer_test.go b/e2e/utils/sui/signer_test.go deleted file mode 100644 index 81de3b2a00..0000000000 --- a/e2e/utils/sui/signer_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package sui - -import ( - "bytes" - "encoding/base64" - "testing" - - sui_signer "github.com/block-vision/sui-go-sdk/signer" - "github.com/cosmos/cosmos-sdk/types/bech32" - "github.com/stretchr/testify/require" -) - -// test vector: -// see https://github.com/MystenLabs/sui/blob/199f06d25ce85f0270a1a5a0396156bb2b83122c/sdk/typescript/test/unit/cryptography/secp256k1-keypair.test.ts - -var VALID_SECP256K1_SECRET_KEY = []byte{ - 59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107, 94, 203, - 174, 253, 102, 39, 170, 146, 46, 252, 4, 143, 236, 12, 136, 28, -} - -var VALID_SECP256K1_PUBLIC_KEY = []byte{ - 2, 29, 21, 35, 7, 198, 183, 43, 14, 208, 65, 139, 14, 112, 205, 128, 231, 245, - 41, 91, 141, 134, 245, 114, 45, 63, 82, 19, 251, 210, 57, 79, 54, -} - -func TestSignerSecp256k1FromSecretKey(t *testing.T) { - // Create signer from secret key - signer := NewSignerSecp256k1FromSecretKey(VALID_SECP256K1_SECRET_KEY) - - // Get public key bytes - pubKey := signer.GetPublicKey() - - // Compare with expected public key - if !bytes.Equal(pubKey, VALID_SECP256K1_PUBLIC_KEY) { - t.Errorf("Public key mismatch\nexpected: %v\ngot: %v", VALID_SECP256K1_PUBLIC_KEY, pubKey) - } -} - -// Test keypairs generated and exported with -// -// sui client new-address secp256k1 -// sui keytool export -// -// See https://github.com/sui-foundation/sips/blob/main/sips/sip-15.md for encoding info -func TestSuiSecp256k1Keypair(t *testing.T) { - tests := []struct { - name string - privKey string - expectedAddress string - expectedPubkeyBase64 string - }{ - { - name: "example 1", - privKey: "suiprivkey1q8h7ejwfcdn6gc2x9kddtd9vld3kvuvtr9gdtj9qcf7nqnw94tvl79cwfq4", - expectedAddress: "0x68f6d05fd44366bd431405e8ea32e2d2f8e330d98e0089c9447ecfbbdf6e236f", - expectedPubkeyBase64: "AQOhmtkY2bTZGXRZmXZLo495i5Dz+FgJvM7bbnUCWlL2hg==", - }, - { - name: "example 2", - privKey: "suiprivkey1qxghtp2vncr94s8h7ctvgf58gy27l9nch75ka2jh37qr90xuyxhlwk5khxc", - expectedAddress: "0x8ec6f13affbf48c73550567f2a1cb8f05474c0133ebf745f91a2f3b971c1ec9a", - expectedPubkeyBase64: "AQIgu/14lUhVMEWjIB0RQ80ARJiH/xQIw4KJTEhDhHcjEQ==", - }, - { - name: "example 3", - privKey: "suiprivkey1q99wkv3fj352cn97yu5r9jwqhcvyyk6t9scwrftyjgqgflandfgc70r74hg", - expectedAddress: "0xa0f6b839f7945065ebdd030cec8e6e30d01a74c8cb31b1945909fd426c2cef80", - expectedPubkeyBase64: "AQIRTpSIUP2GOAWTEHOTYbyUjlfxpHKwPvsgwZw3G6h/RQ==", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _, data, err := bech32.DecodeAndConvert(tt.privKey) - require.NoError(t, err) - - // assert this is a secp256k1 private key - require.Equal(t, byte(sui_signer.SigntureFlagSecp256k1), data[0]) - - flagLessData := data[1:] - - signer := NewSignerSecp256k1FromSecretKey(flagLessData) - - address := signer.Address() - require.Equal(t, tt.expectedAddress, address) - - pubkeyBytes := signer.GetFlaggedPublicKey() - pubkeyEncoded := base64.StdEncoding.EncodeToString(pubkeyBytes) - require.Equal(t, tt.expectedPubkeyBase64, pubkeyEncoded) - - // we don't have a good way outside e2e to verify the signature is correct, but let's exercise it anyway - _, err = signer.SignTransactionBlock("ZXhhbXBsZQo=") - require.NoError(t, err) - }) - } -} diff --git a/go.mod b/go.mod index c4233dcc4a..32c5e67d91 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.10.0 - github.com/zeta-chain/ethermint v0.0.0-20250210141109-c8cb0fa0d95d + github.com/zeta-chain/ethermint v0.0.0-20250211180824-ea52413a15f3 github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20250124151021-87b63e845f1c gitlab.com/thorchain/tss/go-tss v1.6.5 go.nhat.io/grpcmock v0.25.0 @@ -311,7 +311,7 @@ require ( github.com/montanaflynn/stats v0.7.1 github.com/showa-93/go-mask v0.6.2 github.com/tonkeeper/tongo v1.9.3 - github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250211174435-9680e27af84a + github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250212192042-34095b214e51 ) require ( diff --git a/go.sum b/go.sum index 0bd54ea89a..ce7c75670f 100644 --- a/go.sum +++ b/go.sum @@ -1394,8 +1394,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeta-chain/ethermint v0.0.0-20250210141109-c8cb0fa0d95d h1:6ZVsQ41ziPjce2rOMczlwWKvhBsohjI5ChX+O/0m/YY= -github.com/zeta-chain/ethermint v0.0.0-20250210141109-c8cb0fa0d95d/go.mod h1:Pykye2Mw2eQ8/l80MTr6yNaBm6OI3TkQCmwQGUrL5VM= +github.com/zeta-chain/ethermint v0.0.0-20250211180824-ea52413a15f3 h1:Bco3vCp5owYPZrg3GcnjaTNX51U70z57TaQJBdddpvY= +github.com/zeta-chain/ethermint v0.0.0-20250211180824-ea52413a15f3/go.mod h1:Pykye2Mw2eQ8/l80MTr6yNaBm6OI3TkQCmwQGUrL5VM= github.com/zeta-chain/go-ethereum v1.13.16-0.20241022183758-422c6ef93ccc h1:FVOttT/f7QCZMkOLssLTY1cbX8pT+HS/kg81zgUAmYE= github.com/zeta-chain/go-ethereum v1.13.16-0.20241022183758-422c6ef93ccc/go.mod h1:MgO2/CmxFnj6W7v/5hrz3ypco3kHkb8856pRnFkY4xQ= github.com/zeta-chain/go-libp2p v0.0.0-20240710192637-567fbaacc2b4 h1:FmO3HfVdZ7LzxBUfg6sVzV7ilKElQU2DZm8PxJ7KcYI= @@ -1404,8 +1404,8 @@ github.com/zeta-chain/go-tss v0.0.0-20241216161449-be92b20f8102 h1:jMb9ydfDFjgdl github.com/zeta-chain/go-tss v0.0.0-20241216161449-be92b20f8102/go.mod h1:nqelgf4HKkqlXaVg8X38a61WfyYB+ivCt6nnjoTIgCc= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20250124151021-87b63e845f1c h1:iD7O6gFot1QHM6ggrt96N9eXnZ7vqkg2mFVm7OTaisw= github.com/zeta-chain/protocol-contracts v1.0.2-athens3.0.20250124151021-87b63e845f1c/go.mod h1:SjT7QirtJE8stnAe1SlNOanxtfSfijJm3MGJ+Ax7w7w= -github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250211174435-9680e27af84a h1:+TVuNWQK8j7yc9TkH+JOplMae7Ym/yADoIMnO1yIQzo= -github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250211174435-9680e27af84a/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250212192042-34095b214e51 h1:r3cG+whGN8MFwfLG2Y2Irs2x1zyMznUDJHcOgV2X4UM= +github.com/zeta-chain/protocol-contracts-solana/go-idl v0.0.0-20250212192042-34095b214e51/go.mod h1:DcDY828o773soiU/h0XpC+naxitrIMFVZqEvq/EJxMA= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901 h1:9whtN5fjYHfk4yXIuAsYP2EHxImwDWDVUOnZJ2pfL3w= github.com/zeta-chain/tss-lib v0.0.0-20240916163010-2e6b438bd901/go.mod h1:d2iTC62s9JwKiCMPhcDDXbIZmuzAyJ4lwso0H5QyRbk= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= diff --git a/pkg/chains/chain.go b/pkg/chains/chain.go index d5cb15ab0c..ca64ebc116 100644 --- a/pkg/chains/chain.go +++ b/pkg/chains/chain.go @@ -56,13 +56,14 @@ func (chain Chain) IsExternalChain() bool { // on EVM chain, it is 20Bytes // on Bitcoin chain, it is P2WPKH address, []byte(bech32 encoded string) func (chain Chain) EncodeAddress(b []byte) (string, error) { - switch chain.Consensus { - case Consensus_ethereum: + if chain.Vm == Vm_evm { addr := ethcommon.BytesToAddress(b) if addr == (ethcommon.Address{}) { return "", fmt.Errorf("invalid EVM address") } return addr.Hex(), nil + } + switch chain.Consensus { case Consensus_bitcoin: addrStr := string(b) chainParams, err := GetBTCChainParams(chain.ChainId) @@ -134,8 +135,9 @@ func DecodeAddressFromChainID(chainID int64, addr string, additionalChains []Cha if err != nil { return nil, fmt.Errorf("invalid TON address %q: %w", addr, err) } - return []byte(acc.ToRaw()), nil + case IsSuiChain(chainID, additionalChains): + return []byte(addr), nil default: return nil, fmt.Errorf("chain (%d) not supported", chainID) } diff --git a/pkg/coin/coin_test.go b/pkg/coin/coin_test.go index acb8f7187c..e08891503a 100644 --- a/pkg/coin/coin_test.go +++ b/pkg/coin/coin_test.go @@ -144,7 +144,7 @@ func TestCoinType_SupportsRefund(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.c.SupportsRefund(); got != tt.want { - t.Errorf("CoinType.SupportsRefund() = %v, want %v", got, tt.want) + t.Errorf("FungibleTokenCoinType.SupportsRefund() = %v, want %v", got, tt.want) } }) } diff --git a/pkg/contracts/solana/gateway.go b/pkg/contracts/solana/gateway.go index 6f9f9e3837..0537bf50b7 100644 --- a/pkg/contracts/solana/gateway.go +++ b/pkg/contracts/solana/gateway.go @@ -42,6 +42,9 @@ var ( // DiscriminatorExecute returns the discriminator for Solana gateway 'execute' instruction DiscriminatorExecute = idlgateway.IDLGateway.GetDiscriminator("execute") + // DiscriminatorIncrementNonce returns the discriminator for Solana gateway 'increment_nonce' instruction + DiscriminatorIncrementNonce = idlgateway.IDLGateway.GetDiscriminator("increment_nonce") + // DiscriminatorExecuteSPL returns the discriminator for Solana gateway 'execute_spl_token' instruction DiscriminatorExecuteSPL = idlgateway.IDLGateway.GetDiscriminator("execute_spl_token") diff --git a/pkg/contracts/solana/gateway_message.go b/pkg/contracts/solana/gateway_message.go index a9a65d307e..96507f907f 100644 --- a/pkg/contracts/solana/gateway_message.go +++ b/pkg/contracts/solana/gateway_message.go @@ -8,14 +8,18 @@ import ( "github.com/gagliardetto/solana-go" ) +// NOTE: Following consts and identifier are used in outbounds message hashes for consistency. +// Instruction specific byte identifiers are used to encode instruction in hash with fixed amount of space. const ( InstructionWithdraw byte = 1 InstructionWithdrawSplToken byte = 2 InstructionWhitelistSplToken byte = 3 InstructionExecute byte = 5 InstructionExecuteSPL byte = 6 + InstructionIncrementNonce byte = 7 ) +// InstructionIdentifier is used at beginning of message hash to make it project specific. var InstructionIdentifier = []byte("ZETACHAIN") // MsgWithdraw is the message for the Solana gateway withdraw instruction @@ -119,6 +123,96 @@ func (msg *MsgWithdraw) Signer() (common.Address, error) { return RecoverSigner(msgHash[:], msgSig[:]) } +// MsgIncrementNonce is the message for the Solana gateway increment_nonce instruction +type MsgIncrementNonce struct { + // chainID is the chain ID of Solana chain + chainID uint64 + + // Nonce is the nonce for the increment_nonce + nonce uint64 + + // amount is the lamports amount for the increment_nonce + amount uint64 + + // signature is the signature of the message + signature [65]byte +} + +// NewMsgIncrementNonce returns a new increment_nonce message +func NewMsgIncrementNonce(chainID, nonce, amount uint64) *MsgIncrementNonce { + return &MsgIncrementNonce{ + chainID: chainID, + nonce: nonce, + amount: amount, + } +} + +// ChainID returns the chain ID of the message +func (msg *MsgIncrementNonce) ChainID() uint64 { + return msg.chainID +} + +// Nonce returns the nonce of the message +func (msg *MsgIncrementNonce) Nonce() uint64 { + return msg.nonce +} + +// Amount returns the amount of the message +func (msg *MsgIncrementNonce) Amount() uint64 { + return msg.amount +} + +// Hash packs the increment_nonce message and computes the hash +func (msg *MsgIncrementNonce) Hash() [32]byte { + var message []byte + buff := make([]byte, 8) + + message = append(message, InstructionIdentifier...) + message = append(message, InstructionIncrementNonce) + + binary.BigEndian.PutUint64(buff, msg.chainID) + message = append(message, buff...) + + binary.BigEndian.PutUint64(buff, msg.nonce) + message = append(message, buff...) + + binary.BigEndian.PutUint64(buff, msg.amount) + message = append(message, buff...) + + return crypto.Keccak256Hash(message) +} + +// SetSignature attaches the signature to the message +func (msg *MsgIncrementNonce) SetSignature(signature [65]byte) *MsgIncrementNonce { + msg.signature = signature + return msg +} + +// SigRSV returns the full 65-byte [R+S+V] signature +func (msg *MsgIncrementNonce) SigRSV() [65]byte { + return msg.signature +} + +// SigRS returns the 64-byte [R+S] core part of the signature +func (msg *MsgIncrementNonce) SigRS() [64]byte { + var sig [64]byte + copy(sig[:], msg.signature[:64]) + return sig +} + +// SigV returns the V part (recovery ID) of the signature +func (msg *MsgIncrementNonce) SigV() uint8 { + return msg.signature[64] +} + +// Signer returns the signer of the message +func (msg *MsgIncrementNonce) Signer() (common.Address, error) { + msgHash := msg.Hash() + msgSig := msg.SigRSV() + + return RecoverSigner(msgHash[:], msgSig[:]) +} + // MsgExecute is the message for the Solana gateway execute instruction type MsgExecute struct { // chainID is the chain ID of Solana chain diff --git a/pkg/contracts/solana/gateway_message_test.go b/pkg/contracts/solana/gateway_message_test.go index 287b5cea47..fa78812387 100644 --- a/pkg/contracts/solana/gateway_message_test.go +++ b/pkg/contracts/solana/gateway_message_test.go @@ -3,6 +3,7 @@ package solana_test import ( "testing" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/gagliardetto/solana-go" @@ -76,3 +77,72 @@ func Test_MsgWithdrawSPLHash(t *testing.T) { require.EqualValues(t, hash[:], wantHashBytes) }) } + +func Test_MsgIncrementNonceHash(t *testing.T) { + t.Run("should calculate expected hash", func(t *testing.T) { + // ARRANGE + // #nosec G115 always positive + chainID := uint64(chains.SolanaLocalnet.ChainId) + nonce := uint64(0) + amount := uint64(1336000) + + wantHash := "7acd44905f89cc36b4542b103a25f2110d25ca469dee854fbf342f1f3ee8ba90" + wantHashBytes := testutil.HexToBytes(t, wantHash) + + // ACT + // create new increment nonce message + hash := contracts.NewMsgIncrementNonce(chainID, nonce, amount).Hash() + + // ASSERT + require.EqualValues(t, hash[:], wantHashBytes) + }) +} + +func Test_MsgExecuteHash(t *testing.T) { + t.Run("should calculate expected hash", func(t *testing.T) { + // ARRANGE + // #nosec G115 always positive + chainID := uint64(chains.SolanaLocalnet.ChainId) + nonce := uint64(0) + amount := uint64(1336000) + to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") + sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") + + wantHash := "7391cf357fd80e7cb3d2a9758932fdea4988d03c87210a2632f03b467728d199" + wantHashBytes := testutil.HexToBytes(t, wantHash) + + // ACT + // create new execute message + hash := contracts.NewMsgExecute(chainID, nonce, amount, to, sender, []byte("hello"), []*solana.AccountMeta{}). + Hash() + + // ASSERT + require.EqualValues(t, hash[:], wantHashBytes) + }) +} + +func Test_MsgExecuteSPLHash(t *testing.T) { + t.Run("should calculate expected hash", func(t *testing.T) { + // ARRANGE + // #nosec G115 always positive + chainID := uint64(chains.SolanaLocalnet.ChainId) + nonce := uint64(0) + amount := uint64(1336000) + mintAccount := solana.MustPublicKeyFromBase58("AS48jKNQsDGkEdDvfwu1QpqjtqbCadrAq9nGXjFmdX3Z") + to := solana.MustPublicKeyFromBase58("37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ") + toAta, _, err := solana.FindAssociatedTokenAddress(to, mintAccount) + require.NoError(t, err) + sender := common.HexToAddress("0x42bd6E2ce4CDb2F58Ed0A0E427F011A0645D5E33") + + wantHash := "d90f9640faecd76509b4e88fa7d18f130918130b8666179f1597f185a828d3a5" + wantHashBytes := testutil.HexToBytes(t, wantHash) + + // ACT + // create new execute message + hash := contracts.NewMsgExecuteSPL(chainID, nonce, amount, 8, mintAccount, to, toAta, sender, []byte("hello"), []*solana.AccountMeta{}). + Hash() + + // ASSERT + require.EqualValues(t, hash[:], wantHashBytes) + }) +} diff --git a/pkg/contracts/solana/instruction.go b/pkg/contracts/solana/instruction.go index 1c8fdb22bc..67aa0e73dd 100644 --- a/pkg/contracts/solana/instruction.go +++ b/pkg/contracts/solana/instruction.go @@ -88,6 +88,76 @@ type OutboundInstruction interface { // TokenAmount returns the amount of the instruction TokenAmount() uint64 + + // InstructionDiscriminator returns the discriminator of the instruction + InstructionDiscriminator() [8]byte +} + +var _ OutboundInstruction = (*IncrementNonceInstructionParams)(nil) + +// IncrementNonceInstructionParams contains the parameters for a gateway increment_nonce instruction +type IncrementNonceInstructionParams struct { + // Discriminator is the unique identifier for the increment_nonce instruction + Discriminator [8]byte + + // Amount is the lamports amount for the increment_nonce + Amount uint64 + + // Signature is the ECDSA signature (by TSS) for the increment_nonce + Signature [64]byte + + // RecoveryID is the recovery ID used to recover the public key from ECDSA signature + RecoveryID uint8 + + // MessageHash is the hash of the message signed by TSS + MessageHash [32]byte + + // Nonce is the nonce for the increment_nonce + Nonce uint64 +} + +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *IncrementNonceInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + +// Signer returns the signer of the signature contained +func (inst *IncrementNonceInstructionParams) Signer() (signer common.Address, err error) { + var signature [65]byte + copy(signature[:], inst.Signature[:64]) + signature[64] = inst.RecoveryID + + return RecoverSigner(inst.MessageHash[:], signature[:]) +} + +// GatewayNonce returns the nonce of the instruction +func (inst *IncrementNonceInstructionParams) GatewayNonce() uint64 { + return inst.Nonce +} + +// TokenAmount returns the amount of the instruction +func (inst *IncrementNonceInstructionParams) TokenAmount() uint64 { + return inst.Amount +} + +// ParseInstructionIncrementNonce tries to parse the instruction as a 'increment_nonce'. +// It returns nil if the instruction can't be parsed as a 'increment_nonce'. +func ParseInstructionIncrementNonce( + instruction solana.CompiledInstruction, +) (*IncrementNonceInstructionParams, error) { + // try deserializing instruction as a 'increment_nonce' + inst := &IncrementNonceInstructionParams{} + err := borsh.Deserialize(inst, instruction.Data) + if err != nil { + return nil, errors.Wrap(err, "error deserializing instruction") + } + + // check the discriminator to ensure it's a 'increment_nonce' instruction + if inst.Discriminator != DiscriminatorIncrementNonce { + return nil, fmt.Errorf("not an increment_nonce instruction: %v", inst.Discriminator) + } + + return inst, nil } var _ OutboundInstruction = (*WithdrawInstructionParams)(nil) @@ -113,6 +183,11 @@ type WithdrawInstructionParams struct { Nonce uint64 } +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *WithdrawInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + // Signer returns the signer of the signature contained func (inst *WithdrawInstructionParams) Signer() (signer common.Address, err error) { var signature [65]byte @@ -179,6 +254,11 @@ type ExecuteInstructionParams struct { Nonce uint64 } +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *ExecuteInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + // Signer returns the signer of the signature contained func (inst *ExecuteInstructionParams) Signer() (signer common.Address, err error) { var signature [65]byte @@ -241,6 +321,11 @@ type WithdrawSPLInstructionParams struct { Nonce uint64 } +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *WithdrawSPLInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + // Signer returns the signer of the signature contained func (inst *WithdrawSPLInstructionParams) Signer() (signer common.Address, err error) { var signature [65]byte @@ -309,6 +394,11 @@ type ExecuteSPLInstructionParams struct { Nonce uint64 } +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *ExecuteSPLInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + // Signer returns the signer of the signature contained func (inst *ExecuteSPLInstructionParams) Signer() (signer common.Address, err error) { var signature [65]byte @@ -377,6 +467,11 @@ type WhitelistInstructionParams struct { Nonce uint64 } +// InstructionDiscriminator returns the discriminator of the instruction +func (inst *WhitelistInstructionParams) InstructionDiscriminator() [8]byte { + return inst.Discriminator +} + // Signer returns the signer of the signature contained func (inst *WhitelistInstructionParams) Signer() (signer common.Address, err error) { var signature [65]byte diff --git a/pkg/contracts/solana/pda.go b/pkg/contracts/solana/pda.go index 0452d23a15..6a9f3e6fc2 100644 --- a/pkg/contracts/solana/pda.go +++ b/pkg/contracts/solana/pda.go @@ -16,4 +16,7 @@ type PdaInfo struct { // ChainId is the Solana chain id ChainID uint64 + + // DepositPaused is the flag to indicate if the deposit is paused + DepositPaused bool } diff --git a/pkg/contracts/sui/crypto.go b/pkg/contracts/sui/crypto.go new file mode 100644 index 0000000000..c9f9eb67f3 --- /dev/null +++ b/pkg/contracts/sui/crypto.go @@ -0,0 +1,165 @@ +package sui + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + + "github.com/block-vision/sui-go-sdk/models" + "github.com/decred/dcrd/dcrec/secp256k1/v4" + secp256k1signer "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "golang.org/x/crypto/blake2b" +) + +const flagSecp256k1 = 0x01 + +// Digest calculates tx digest (hash) for further signing by TSS. +func Digest(tx models.TxnMetaData) ([32]byte, error) { + txBytes, err := base64.StdEncoding.DecodeString(tx.TxBytes) + if err != nil { + return [32]byte{}, errors.Wrap(err, "failed to decode tx bytes") + } + + message := messageWithIntentPrefix(txBytes) + + // "When invoking the signing API, you must first hash the intent message of the tx + // data to 32 bytes using Blake2b ... For ECDSA Secp256k1 and Secp256r1, + // you must use SHA256 as the internal hash function" + // https://docs.sui.io/concepts/cryptography/transaction-auth/signatures#signature-requirements + return blake2b.Sum256(message), nil +} + +// https://github.com/MystenLabs/sui/blob/0dc1a38f800fc2d8fabe11477fdef702058cf00d/crates/sui-types/src/intent.rs +// #1 = IntentScope(transactionData=0) +// #2 = Version(0) +// #3 = AppId(Sui=0) +var defaultIntent = []byte{0, 0, 0} + +// Constructs binary message with intent prefix. +// https://docs.sui.io/concepts/cryptography/transaction-auth/intent-signing#structs +func messageWithIntentPrefix(message []byte) []byte { + glued := make([]byte, len(defaultIntent)+len(message)) + copy(glued, defaultIntent) + copy(glued[len(defaultIntent):], message) + + return glued +} + +// AddressFromPubKeyECDSA converts ECDSA public key to Sui address. +// https://docs.sui.io/concepts/cryptography/transaction-auth/keys-addresses +// https://docs.sui.io/concepts/cryptography/transaction-auth/signatures +func AddressFromPubKeyECDSA(pk *ecdsa.PublicKey) string { + pubBytes := elliptic.MarshalCompressed(pk.Curve, pk.X, pk.Y) + + raw := make([]byte, 1+len(pubBytes)) + raw[0] = flagSecp256k1 + copy(raw[1:], pubBytes) + + addrBytes := blake2b.Sum256(raw) + + return "0x" + hex.EncodeToString(addrBytes[:]) +} + +// SerializeSignatureECDSA serializes secp256k1 sig (R|S|V) and a publicKey into base64 string +// https://docs.sui.io/concepts/cryptography/transaction-auth/signatures +func SerializeSignatureECDSA(signature [65]byte, pubKey *ecdsa.PublicKey) (string, error) { + // we don't need the last V byte for recovery + const sigLen = 64 + const pubKeyLen = 33 + + pubKeyBytes := crypto.CompressPubkey(pubKey) + + // should not happen + if len(pubKeyBytes) != pubKeyLen { + return "", errors.Errorf("invalid pubKey length (got %d, want %d)", len(pubKeyBytes), pubKeyLen) + } + + serialized := make([]byte, 1+sigLen+pubKeyLen) + serialized[0] = flagSecp256k1 + copy(serialized[1:], signature[:sigLen]) + copy(serialized[1+sigLen:], pubKeyBytes) + + return base64.StdEncoding.EncodeToString(serialized), nil +} + +// DeserializeSignatureECDSA deserializes SUI secp256k1 signature. +// Returns ECDSA public key and signature. +// Sequence: `flag(1b) + sig(64b) + compressedPubKey(33b)` +func DeserializeSignatureECDSA(sigBase64 string) (*ecdsa.PublicKey, [64]byte, error) { + const ( + flagLen = 1 + sigLen = 64 + pubLen = 33 + expectedLen = flagLen + sigLen + pubLen + pubOffset = flagLen + sigLen + ) + + sigBytes, err := base64.StdEncoding.DecodeString(sigBase64) + switch { + case err != nil: + return nil, [64]byte{}, errors.Wrap(err, "failed to decode signature") + case len(sigBytes) != expectedLen: + return nil, [64]byte{}, errors.Errorf("invalid sig length (got %d, want %d)", len(sigBytes), expectedLen) + case sigBytes[0] != flagSecp256k1: + return nil, [64]byte{}, errors.Errorf("invalid sig flag (got %d, want %d)", sigBytes[0], flagSecp256k1) + case len(sigBytes[pubOffset:]) != pubLen: + return nil, [64]byte{}, errors.Errorf( + "invalid pubKey length (got %d, want %d)", + len(sigBytes[pubOffset:]), + pubLen, + ) + } + + pk, err := crypto.DecompressPubkey(sigBytes[pubOffset:]) + if err != nil { + return nil, [64]byte{}, errors.Wrap(err, "failed to decompress public key") + } + + var sig [64]byte + copy(sig[:], sigBytes[flagLen:pubOffset]) + + return pk, sig, nil +} + +// SignerSecp256k1 represents Sui Secp256k1 signer. +type SignerSecp256k1 struct { + pk *secp256k1.PrivateKey + address string +} + +// NewSignerSecp256k1 creates new SignerSecp256k1. +func NewSignerSecp256k1(privateKeyBytes []byte) *SignerSecp256k1 { + pk := secp256k1.PrivKeyFromBytes(privateKeyBytes) + address := AddressFromPubKeyECDSA(pk.PubKey().ToECDSA()) + + return &SignerSecp256k1{pk: pk, address: address} +} + +func (s *SignerSecp256k1) Address() string { + return s.address +} + +func (s *SignerSecp256k1) SignTxBlock(tx models.TxnMetaData) (string, error) { + digest, err := Digest(tx) + if err != nil { + return "", errors.Wrap(err, "unable to get digest") + } + + // Another hashing is required for ECDSA. + // https://docs.sui.io/concepts/cryptography/transaction-auth/signatures#signature-requirements + digestWrapped := sha256.Sum256(digest[:]) + + // returns V[1b] | R[32b] | S[32b], But we need R | S | V + sig := secp256k1signer.SignCompact(s.pk, digestWrapped[:], false) + + var sigReordered [65]byte + copy(sigReordered[0:32], sig[1:33]) // Copy R[32] + copy(sigReordered[32:64], sig[33:65]) // Copy S[32] + sigReordered[64] = sig[0] // Move V[1] to the end + + return SerializeSignatureECDSA(sigReordered, &s.pk.ToECDSA().PublicKey) +} diff --git a/pkg/contracts/sui/crypto_test.go b/pkg/contracts/sui/crypto_test.go new file mode 100644 index 0000000000..34a471b78f --- /dev/null +++ b/pkg/contracts/sui/crypto_test.go @@ -0,0 +1,160 @@ +package sui + +import ( + "encoding/base64" + "encoding/hex" + "testing" + + "github.com/block-vision/sui-go-sdk/models" + "github.com/cosmos/cosmos-sdk/types/bech32" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCrypto(t *testing.T) { + t.Run("Digest", func(t *testing.T) { + // ARRANGE + // Given a tx (imagine client.MoveCall(...) result) + // + // Data was generated using sui cli: + // sui client transfer-sui --to "0xac5bceec1b789ff840d7d4e6ce4ce61c90d190a7f8c4f4ddf0bff6ee2413c33c" \ + // --sui-coin-object-id "0x0466a9a57add505b7b85ac485054f9b71f574f4504d9c70acd8f73ef11e0dc30" \ + // --gas-budget 500000 --serialize-unsigned-transaction + // + // https://docs.sui.io/concepts/cryptography/transaction-auth/offline-signing#sign + tx := models.TxnMetaData{ + TxBytes: "AAABACCsW87sG3if+EDX1ObOTOYckNGQp/jE9N3wv/buJBPDPAEBAQABAACsW87sG3if+EDX1ObOTOYckNGQp/jE9N3wv/buJBPDPAEEZqmlet1QW3uFrEhQVPm3H1dPRQTZxwrNj3PvEeDcMPCkyhwAAAAAICNNoyg5v4obnoVYDWw0XhxB6Tq/b+OPXnJKPc2+QM5QrFvO7Bt4n/hA19TmzkzmHJDRkKf4xPTd8L/27iQTwzzuAgAAAAAAACChBwAAAAAAAA==", + } + + // Given expected digest based on SUI cli: + // https://docs.sui.io/concepts/cryptography/transaction-auth/offline-signing#sign + // sui keytool sign --address "..." --data "$txBytesBase64" --json | jq ".digest" + const expectedDigestBase64 = "A1NY74R1IScWR/GPtOMNHVY/RwTNzWHlUbOkwp3911g=" + + // ACT + digest, err := Digest(tx) + + digestBase64 := base64.StdEncoding.EncodeToString(digest[:]) + + // ASSERT + require.NoError(t, err) + require.Equal(t, expectedDigestBase64, digestBase64) + }) + + t.Run("AddressFromPubKeyECDSA", func(t *testing.T) { + // `$> sui keytool generate secp256k1` + for _, tt := range []struct { + pubKey string + address string + }{ + { + pubKey: "AQJz6a5yi6Wtf55atMWlW/ZA4Xdd6lJKC22u3Xi/h9yeBw==", + address: "0xccf49bfb6c8159f5e53c80f5b6ecf748e4af89c8c10c27d24302207b2bc97744", + }, + { + pubKey: "AQKUgO1kyhheTjbzYYhP67nxDD1UZwEhqkLyX1KRBm1xTQ==", + address: "0x2dc141f8a8d8a3fe397054f538dcc8207fd5edf4a1415c54b7d5a4dc124d9b3e", + }, + { + pubKey: "AQIgwiNQwm529+fvaKW/n5ITbaQVUToZq+ZIpNjjOw7Spw==", + address: "0x17012be22c34ad1396f8af272b2e7b0edb529b3441912bd532faf874bf2c9262", + }, + } { + // ARRANGE + pubKeyBytes, err := base64.StdEncoding.DecodeString(tt.pubKey) + require.NoError(t, err) + + // type_flag + compression_flag + 32bytes + require.Equal(t, 1+1+32, len(pubKeyBytes)) + + pk, err := crypto.DecompressPubkey(pubKeyBytes[1:]) + require.NoError(t, err) + + // ACT + addr := AddressFromPubKeyECDSA(pk) + + // ASSERT + assert.Equal(t, tt.address, addr) + } + }) + + t.Run("SerializeSignatureECDSA", func(t *testing.T) { + // ARRANGE + // Given a pubKey + enc, _ := hex.DecodeString( + "04760c4460e5336ac9bbd87952a3c7ec4363fc0a97bd31c86430806e287b437fd1b01abc6e1db640cf3106b520344af1d58b00b57823db3e1407cbc433e1b6d04d", + ) + pubKey, err := crypto.UnmarshalPubkey(enc) + require.NoError(t, err) + + // Given signature + signature := [65]byte{0, 1, 3} + + // ACT + res, err := SerializeSignatureECDSA(signature, pubKey) + + // ASSERT + require.NoError(t, err) + + // Check signature + resBin, err := base64.StdEncoding.DecodeString(res) + require.NoError(t, err) + require.Equal(t, (1 + 64 + 33), len(resBin)) + + // ACT 2 + pubKey2, signature2, err := DeserializeSignatureECDSA(res) + + // ASSERT 2 + require.NoError(t, err) + assert.True(t, pubKey2.Equal(pubKey)) + assert.Equal(t, signature[:64], signature2[:]) + }) + + t.Run("SignerSecp256k1", func(t *testing.T) { + for _, tt := range []struct { + privKey string + address string + }{ + { + privKey: "suiprivkey1q8h7ejwfcdn6gc2x9kddtd9vld3kvuvtr9gdtj9qcf7nqnw94tvl79cwfq4", + address: "0x68f6d05fd44366bd431405e8ea32e2d2f8e330d98e0089c9447ecfbbdf6e236f", + }, + { + privKey: "suiprivkey1qxghtp2vncr94s8h7ctvgf58gy27l9nch75ka2jh37qr90xuyxhlwk5khxc", + address: "0x8ec6f13affbf48c73550567f2a1cb8f05474c0133ebf745f91a2f3b971c1ec9a", + }, + { + privKey: "suiprivkey1q99wkv3fj352cn97yu5r9jwqhcvyyk6t9scwrftyjgqgflandfgc70r74hg", + address: "0xa0f6b839f7945065ebdd030cec8e6e30d01a74c8cb31b1945909fd426c2cef80", + }, + } { + t.Run(tt.privKey, func(t *testing.T) { + // ARRANGE + // Given a private key + _, privateKeyBytes, err := bech32.DecodeAndConvert(tt.privKey) + require.NoError(t, err) + require.Equal(t, byte(flagSecp256k1), privateKeyBytes[0]) + + // Given signer (pk imported w/o flag byte) + signer := NewSignerSecp256k1(privateKeyBytes[1:]) + + // ACT + // Check signer's Sui address + address := signer.Address() + + // Sign some stub tx + // We don't have a good way outside e2e to verify the signature is correct, + // but let's exercise it anyway + const exampleBase64 = "ZXhhbXBsZQo=" + _, errSign := signer.SignTxBlock(models.TxnMetaData{ + TxBytes: exampleBase64, + }) + + // ASSERT + require.Equal(t, tt.address, address) + require.NoError(t, errSign) + }) + } + }) +} diff --git a/pkg/contracts/sui/inbound.go b/pkg/contracts/sui/deposit.go similarity index 67% rename from pkg/contracts/sui/inbound.go rename to pkg/contracts/sui/deposit.go index 7727bca719..59fbad3faf 100644 --- a/pkg/contracts/sui/inbound.go +++ b/pkg/contracts/sui/deposit.go @@ -7,9 +7,8 @@ import ( "github.com/pkg/errors" ) -// Inbound represents data for a Sui inbound, -// it is parsed from a deposit/depositAndCall event -type Inbound struct { +// Deposit represents data for a Sui deposit/depositAndCall event +type Deposit struct { // Note: CoinType is what is used as Asset field in the ForeignCoin object CoinType CoinType Amount math.Uint @@ -19,61 +18,61 @@ type Inbound struct { IsCrossChainCall bool } -func (d *Inbound) IsGasDeposit() bool { +func (d *Deposit) IsGas() bool { return d.CoinType == SUI } -func parseInbound(event models.SuiEventResponse, eventType EventType) (Inbound, error) { +func parseDeposit(event models.SuiEventResponse, eventType EventType) (Deposit, error) { parsedJSON := event.ParsedJson coinType, err := extractStr(parsedJSON, "coin_type") if err != nil { - return Inbound{}, err + return Deposit{}, err } amountRaw, err := extractStr(parsedJSON, "amount") if err != nil { - return Inbound{}, err + return Deposit{}, err } amount, err := math.ParseUint(amountRaw) if err != nil { - return Inbound{}, errors.Wrap(err, "unable to parse amount") + return Deposit{}, errors.Wrap(err, "unable to parse amount") } sender, err := extractStr(parsedJSON, "sender") if err != nil { - return Inbound{}, err + return Deposit{}, err } receiverRaw, err := extractStr(parsedJSON, "receiver") if err != nil { - return Inbound{}, err + return Deposit{}, err } receiver := ethcommon.HexToAddress(receiverRaw) if receiver == (ethcommon.Address{}) { - return Inbound{}, errors.Errorf("invalid receiver address %q", receiverRaw) + return Deposit{}, errors.Errorf("invalid receiver address %q", receiverRaw) } var isCrosschainCall bool var payload []byte - if eventType == DepositAndCall { + if eventType == DepositAndCallEvent { isCrosschainCall = true payloadRaw, ok := parsedJSON["payload"].([]any) if !ok { - return Inbound{}, errors.New("invalid payload") + return Deposit{}, errors.New("invalid payload") } payload, err = convertPayload(payloadRaw) if err != nil { - return Inbound{}, errors.Wrap(err, "unable to convert payload") + return Deposit{}, errors.Wrap(err, "unable to convert payload") } } - return Inbound{ + return Deposit{ CoinType: CoinType(coinType), Amount: amount, Sender: sender, diff --git a/pkg/contracts/sui/gateway.go b/pkg/contracts/sui/gateway.go index 0122c0432a..198b75fbfc 100644 --- a/pkg/contracts/sui/gateway.go +++ b/pkg/contracts/sui/gateway.go @@ -1,8 +1,11 @@ package sui import ( + "encoding/base64" + "fmt" "strconv" "strings" + "sync" "github.com/block-vision/sui-go-sdk/models" "github.com/pkg/errors" @@ -16,7 +19,13 @@ type EventType string // Gateway contains the API to read inbounds and sign outbounds to the Sui gateway type Gateway struct { + // packageID is the package ID of the gateway packageID string + + // gatewayObjectID is the object ID of the gateway struct + objectID string + + mu sync.RWMutex } // SUI is the coin type for SUI, native gas token @@ -24,8 +33,9 @@ const SUI CoinType = "0000000000000000000000000000000000000000000000000000000000 // Event types const ( - Deposit EventType = "DepositEvent" - DepositAndCall EventType = "DepositAndCallEvent" + DepositEvent EventType = "DepositEvent" + DepositAndCallEvent EventType = "DepositAndCallEvent" + WithdrawEvent EventType = "WithdrawEvent" ) const moduleName = "gateway" @@ -33,11 +43,20 @@ const moduleName = "gateway" // ErrParseEvent event parse error var ErrParseEvent = errors.New("event parse error") -// NewGateway creates a new Sui gateway -// Note: packageID is the equivalent for gateway address or program ID on Solana -// It's what will be set in gateway chain params -func NewGateway(packageID string) *Gateway { - return &Gateway{packageID: packageID} +// NewGatewayFromPairID creates a new Sui Gateway +// from pair of `$packageID,$gatewayObjectID` +func NewGatewayFromPairID(pair string) (*Gateway, error) { + packageID, gatewayObjectID, err := parsePair(pair) + if err != nil { + return nil, err + } + + return NewGateway(packageID, gatewayObjectID), nil +} + +// NewGateway creates a new Sui Gateway. +func NewGateway(packageID string, gatewayObjectID string) *Gateway { + return &Gateway{packageID: packageID, objectID: gatewayObjectID} } // Event represents generic event wrapper @@ -47,29 +66,76 @@ type Event struct { EventType EventType content any - inbound bool } -// IsInbound checks whether event is Inbound. -func (e *Event) IsInbound() bool { return e.inbound } +func (e *Event) IsDeposit() bool { + return e.EventType == DepositEvent || e.EventType == DepositAndCallEvent +} -// Inbound extract Inbound. -func (e *Event) Inbound() (Inbound, error) { - if !e.inbound { - return Inbound{}, errors.Errorf("not an inbound (%+v)", e.content) +// Deposit extract DepositData. +func (e *Event) Deposit() (Deposit, error) { + v, ok := e.content.(Deposit) + if !ok { + return Deposit{}, errors.Errorf("invalid content type %T", e.content) } - return e.content.(Inbound), nil + return v, nil +} + +func (e *Event) IsWithdraw() bool { + return e.EventType == WithdrawEvent } +// Withdrawal extract WithdrawData. +func (e *Event) Withdrawal() (Withdrawal, error) { + v, ok := e.content.(Withdrawal) + if !ok { + return Withdrawal{}, errors.Errorf("invalid content type %T", e.content) + } + + return v, nil +} + +// PackageID returns object id of Gateway code func (gw *Gateway) PackageID() string { + gw.mu.RLock() + defer gw.mu.RUnlock() return gw.packageID } +// ObjectID returns Gateway's struct object id +func (gw *Gateway) ObjectID() string { + gw.mu.RLock() + defer gw.mu.RUnlock() + return gw.objectID +} + +// Module returns Gateway's module name func (gw *Gateway) Module() string { return moduleName } +// WithdrawCapType returns struct type of the WithdrawCap +func (gw *Gateway) WithdrawCapType() string { + return fmt.Sprintf("%s::%s::WithdrawCap", gw.PackageID(), moduleName) +} + +// UpdateIDs updates packageID and objectID. +func (gw *Gateway) UpdateIDs(pair string) error { + packageID, gatewayObjectID, err := parsePair(pair) + if err != nil { + return err + } + + gw.mu.Lock() + defer gw.mu.Unlock() + + gw.packageID = packageID + gw.objectID = gatewayObjectID + + return nil +} + // ParseEvent parses Event. func (gw *Gateway) ParseEvent(event models.SuiEventResponse) (Event, error) { // basic validation @@ -107,15 +173,15 @@ func (gw *Gateway) ParseEvent(event models.SuiEventResponse) (Event, error) { var ( eventType = descriptor.eventType - inbound bool content any ) // Parse specific events switch eventType { - case Deposit, DepositAndCall: - inbound = true - content, err = parseInbound(event, eventType) + case DepositEvent, DepositAndCallEvent: + content, err = parseDeposit(event, eventType) + case WithdrawEvent: + content, err = parseWithdrawal(event, eventType) default: return Event{}, errors.Wrapf(ErrParseEvent, "unknown event %q", eventType) } @@ -128,12 +194,35 @@ func (gw *Gateway) ParseEvent(event models.SuiEventResponse) (Event, error) { TxHash: txHash, EventIndex: eventID, EventType: eventType, - - content: content, - inbound: inbound, + content: content, }, nil } +// ParseTxWithdrawal a syntax sugar around ParseEvent and Withdrawal. +func (gw *Gateway) ParseTxWithdrawal(tx models.SuiTransactionBlockResponse) (event Event, w Withdrawal, err error) { + if len(tx.Events) == 0 { + err = errors.New("missing events") + return event, w, err + } + + event, err = gw.ParseEvent(tx.Events[0]) + if err != nil { + return event, w, err + } + + if !event.IsWithdraw() { + err = errors.Errorf("invalid event type %s", event.EventType) + return event, w, err + } + + w, err = event.Withdrawal() + if err != nil { + return event, w, err + } + + return event, w, err +} + type eventDescriptor struct { packageID string module string @@ -182,5 +271,20 @@ func convertPayload(data []any) ([]byte, error) { } } - return payload, nil + // Sui encode bytes in base64 + decodedPayload, err := base64.StdEncoding.DecodeString(string(payload)) + if err != nil { + return nil, errors.Wrap(err, "failed to decode payload from base64") + } + + return decodedPayload, nil +} + +func parsePair(pair string) (string, string, error) { + parts := strings.Split(pair, ",") + if len(parts) != 2 { + return "", "", errors.Errorf("invalid pair %q", pair) + } + + return parts[0], parts[1], nil } diff --git a/pkg/contracts/sui/gateway_test.go b/pkg/contracts/sui/gateway_test.go index 87cad900a8..3a8f64346c 100644 --- a/pkg/contracts/sui/gateway_test.go +++ b/pkg/contracts/sui/gateway_test.go @@ -1,6 +1,7 @@ package sui import ( + "encoding/base64" "fmt" "testing" @@ -15,6 +16,7 @@ func TestParseEvent(t *testing.T) { // stubs const ( packageID = "0x3e9fb7c01ef0d97911ccfec79306d9de2d58daa996bd3469da0f6d640cc443cf" + gatewayID = "0x444fb7c01ef0d97911ccfec79306d9de2d58daa996bd3469da0f6d640cc443aa" sender = "0x70386a9a912d9f7a603263abfbd8faae861df0ee5f8e2dbdf731fbd159f10e52" txHash = "HjxLMxMXNz8YfUc2qT4e4CrogKvGeHRbDW7Arr6ntzqq" ) @@ -23,11 +25,17 @@ func TestParseEvent(t *testing.T) { return fmt.Sprintf("%s::%s::%s", packageID, moduleName, t) } - gw := NewGateway(packageID) + gw := NewGateway(packageID, gatewayID) receiverAlice := sample.EthAddress() receiverBob := sample.EthAddress() + var payload []any + payloadBytes := []byte(base64.StdEncoding.EncodeToString([]byte{0, 1, 2})) + for _, p := range payloadBytes { + payload = append(payload, float64(p)) + } + for _, tt := range []struct { name string event models.SuiEventResponse @@ -50,19 +58,18 @@ func TestParseEvent(t *testing.T) { }, assert: func(t *testing.T, raw models.SuiEventResponse, out Event) { assert.Equal(t, txHash, out.TxHash) - assert.Equal(t, Deposit, out.EventType) + assert.Equal(t, DepositEvent, out.EventType) assert.Equal(t, uint64(0), out.EventIndex) - assert.True(t, out.IsInbound()) - - inbound, err := out.Inbound() + deposit, err := out.Deposit() require.NoError(t, err) - assert.Equal(t, SUI, inbound.CoinType) - assert.True(t, math.NewUint(100).Equal(inbound.Amount)) - assert.Equal(t, sender, inbound.Sender) - assert.Equal(t, receiverAlice, inbound.Receiver) - assert.False(t, inbound.IsCrossChainCall) + assert.Equal(t, SUI, deposit.CoinType) + assert.True(t, math.NewUint(100).Equal(deposit.Amount)) + assert.Equal(t, sender, deposit.Sender) + assert.Equal(t, receiverAlice, deposit.Receiver) + assert.False(t, deposit.IsCrossChainCall) + assert.True(t, deposit.IsGas()) }, }, { @@ -77,25 +84,23 @@ func TestParseEvent(t *testing.T) { "amount": "200", "sender": sender, "receiver": receiverBob.String(), - "payload": []any{float64(0), float64(1), float64(2)}, + "payload": payload, }, }, assert: func(t *testing.T, raw models.SuiEventResponse, out Event) { assert.Equal(t, txHash, out.TxHash) - assert.Equal(t, DepositAndCall, out.EventType) + assert.Equal(t, DepositAndCallEvent, out.EventType) assert.Equal(t, uint64(1), out.EventIndex) - assert.True(t, out.IsInbound()) - - inbound, err := out.Inbound() + deposit, err := out.Deposit() require.NoError(t, err) - assert.Equal(t, SUI, inbound.CoinType) - assert.True(t, math.NewUint(200).Equal(inbound.Amount)) - assert.Equal(t, sender, inbound.Sender) - assert.Equal(t, receiverBob, inbound.Receiver) - assert.True(t, inbound.IsCrossChainCall) - assert.Equal(t, []byte{0, 1, 2}, inbound.Payload) + assert.Equal(t, SUI, deposit.CoinType) + assert.True(t, math.NewUint(200).Equal(deposit.Amount)) + assert.Equal(t, sender, deposit.Sender) + assert.Equal(t, receiverBob, deposit.Receiver) + assert.True(t, deposit.IsCrossChainCall) + assert.Equal(t, []byte{0, 1, 2}, deposit.Payload) }, }, { @@ -115,20 +120,47 @@ func TestParseEvent(t *testing.T) { }, assert: func(t *testing.T, raw models.SuiEventResponse, out Event) { assert.Equal(t, txHash, out.TxHash) - assert.Equal(t, DepositAndCall, out.EventType) + assert.Equal(t, DepositAndCallEvent, out.EventType) assert.Equal(t, uint64(1), out.EventIndex) - assert.True(t, out.IsInbound()) + deposit, err := out.Deposit() + require.NoError(t, err) + + assert.Equal(t, SUI, deposit.CoinType) + assert.True(t, math.NewUint(200).Equal(deposit.Amount)) + assert.Equal(t, sender, deposit.Sender) + assert.Equal(t, receiverBob, deposit.Receiver) + assert.True(t, deposit.IsCrossChainCall) + assert.Equal(t, []byte{}, deposit.Payload) + }, + }, + { + name: "withdraw", + event: models.SuiEventResponse{ + Id: models.EventId{TxDigest: txHash, EventSeq: "1"}, + PackageId: packageID, + Sender: sender, + Type: eventType("WithdrawEvent"), + ParsedJson: map[string]any{ + "coin_type": string(SUI), + "amount": "200", + "sender": sender, + "receiver": receiverBob.String(), + "nonce": "123", + }, + }, + assert: func(t *testing.T, raw models.SuiEventResponse, out Event) { + assert.Equal(t, txHash, out.TxHash) + assert.Equal(t, WithdrawEvent, out.EventType) - inbound, err := out.Inbound() + wd, err := out.Withdrawal() require.NoError(t, err) - assert.Equal(t, SUI, inbound.CoinType) - assert.True(t, math.NewUint(200).Equal(inbound.Amount)) - assert.Equal(t, sender, inbound.Sender) - assert.Equal(t, receiverBob, inbound.Receiver) - assert.True(t, inbound.IsCrossChainCall) - assert.Equal(t, []byte{}, inbound.Payload) + assert.Equal(t, SUI, wd.CoinType) + assert.True(t, math.NewUint(200).Equal(wd.Amount)) + assert.Equal(t, sender, wd.Sender) + assert.Equal(t, receiverBob.String(), wd.Receiver) + assert.True(t, wd.IsGas()) }, }, // ERRORS @@ -169,7 +201,7 @@ func TestParseEvent(t *testing.T) { event: models.SuiEventResponse{ Id: models.EventId{TxDigest: txHash, EventSeq: "0"}, PackageId: packageID, - Type: fmt.Sprintf("%s::%s::%s", packageID, "not_a_gateway", Deposit), + Type: fmt.Sprintf("%s::%s::%s", packageID, "not_a_gateway", DepositEvent), TransactionModule: "foo", }, errContains: "module mismatch", diff --git a/pkg/contracts/sui/withdrawal.go b/pkg/contracts/sui/withdrawal.go new file mode 100644 index 0000000000..523b533b38 --- /dev/null +++ b/pkg/contracts/sui/withdrawal.go @@ -0,0 +1,73 @@ +package sui + +import ( + "strconv" + + "cosmossdk.io/math" + "github.com/block-vision/sui-go-sdk/models" + "github.com/pkg/errors" +) + +// Withdrawal represents data for a Sui withdraw event +type Withdrawal struct { + CoinType CoinType + Amount math.Uint + Sender string + Receiver string + Nonce uint64 +} + +func (d *Withdrawal) IsGas() bool { + return d.CoinType == SUI +} + +func parseWithdrawal(event models.SuiEventResponse, eventType EventType) (Withdrawal, error) { + if eventType != WithdrawEvent { + return Withdrawal{}, errors.Errorf("invalid event type %q", eventType) + } + + parsedJSON := event.ParsedJson + + coinType, err := extractStr(parsedJSON, "coin_type") + if err != nil { + return Withdrawal{}, err + } + + amountRaw, err := extractStr(parsedJSON, "amount") + if err != nil { + return Withdrawal{}, err + } + + amount, err := math.ParseUint(amountRaw) + if err != nil { + return Withdrawal{}, errors.Wrap(err, "unable to parse amount") + } + + sender, err := extractStr(parsedJSON, "sender") + if err != nil { + return Withdrawal{}, err + } + + receiver, err := extractStr(parsedJSON, "receiver") + if err != nil { + return Withdrawal{}, err + } + + nonceRaw, err := extractStr(parsedJSON, "nonce") + if err != nil { + return Withdrawal{}, err + } + + nonce, err := strconv.ParseUint(nonceRaw, 10, 64) + if err != nil { + return Withdrawal{}, errors.Wrap(err, "unable to parse nonce") + } + + return Withdrawal{ + CoinType: CoinType(coinType), + Amount: amount, + Sender: sender, + Receiver: receiver, + Nonce: nonce, + }, nil +} diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index 71d5490ff8..5cdc114688 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -20,9 +20,10 @@ import ( // Scheduler represents background task scheduler. type Scheduler struct { - tasks map[uuid.UUID]*Task - mu sync.RWMutex - logger zerolog.Logger + tasks map[uuid.UUID]*Task + mu sync.RWMutex + logger zerolog.Logger + defaultInterval time.Duration } // Executable arbitrary function that can be executed. @@ -68,10 +69,15 @@ type taskOpts struct { } // New Scheduler instance. -func New(logger zerolog.Logger) *Scheduler { +func New(logger zerolog.Logger, defaultInterval time.Duration) *Scheduler { + if defaultInterval <= 0 { + defaultInterval = time.Second * 10 + } + return &Scheduler{ - tasks: make(map[uuid.UUID]*Task), - logger: logger.With().Str("module", "scheduler").Logger(), + tasks: make(map[uuid.UUID]*Task), + logger: logger.With().Str("module", "scheduler").Logger(), + defaultInterval: defaultInterval, } } @@ -87,7 +93,7 @@ func (s *Scheduler) Register(ctx context.Context, exec Executable, opts ...Opt) } config := &taskOpts{ - interval: time.Second * 10, + interval: s.defaultInterval, } for _, opt := range opts { diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go index ac791953a9..d578285768 100644 --- a/pkg/scheduler/scheduler_test.go +++ b/pkg/scheduler/scheduler_test.go @@ -1,18 +1,16 @@ package scheduler import ( - "bytes" "context" "fmt" - "io" "sync/atomic" "testing" "time" cometbft "github.com/cometbft/cometbft/types" - "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/zetaclient/testutils/testlog" ) func TestScheduler(t *testing.T) { @@ -36,14 +34,14 @@ func TestScheduler(t *testing.T) { ts.scheduler.Stop() // ASSERT - // Counter should be 1 because we invoke a task once on a start, - // once after 10 second (default interval), + // Counter should be 2 because we invoke a task once on a start, + // once after 1 second // and then at T=1.5s we stop the scheduler. - assert.Equal(t, int32(1), counter) + assert.Equal(t, int32(2), counter) // Check logs - assert.Contains(t, ts.logBuffer.String(), "Stopped scheduler task") - assert.Contains(t, ts.logBuffer.String(), `"task.group":"default"`) + assert.Contains(t, ts.logger.String(), "Stopped scheduler task") + assert.Contains(t, ts.logger.String(), `"task.group":"default"`) }) t.Run("More opts", func(t *testing.T) { @@ -81,8 +79,8 @@ func TestScheduler(t *testing.T) { assert.Equal(t, int32(4), counter) // Also check that log fields are present - assert.Contains(t, ts.logBuffer.String(), `"task.name":"counter-inc","task.group":"my-custom-group"`) - assert.Contains(t, ts.logBuffer.String(), `"blockchain":"doge","validators":["alice","bob"]`) + assert.Contains(t, ts.logger.String(), `"task.name":"counter-inc","task.group":"my-custom-group"`) + assert.Contains(t, ts.logger.String(), `"blockchain":"doge","validators":["alice","bob"]`) }) t.Run("Task can stop itself", func(t *testing.T) { @@ -175,12 +173,12 @@ func TestScheduler(t *testing.T) { // ASSERT assert.Equal(t, int32(6), counter) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.001,"ticker.new_interval":0.05`) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.05,"ticker.new_interval":0.1`) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.1,"ticker.new_interval":0.15`) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.15,"ticker.new_interval":0.2`) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.2,"ticker.new_interval":0.25`) - assert.Contains(t, ts.logBuffer.String(), `"ticker.old_interval":0.25,"ticker.new_interval":0.3`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.001,"ticker.new_interval":0.05`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.05,"ticker.new_interval":0.1`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.1,"ticker.new_interval":0.15`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.15,"ticker.new_interval":0.2`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.2,"ticker.new_interval":0.25`) + assert.Contains(t, ts.logger.String(), `"ticker.old_interval":0.25,"ticker.new_interval":0.3`) }) t.Run("Multiple tasks in different groups", func(t *testing.T) { @@ -229,11 +227,11 @@ func TestScheduler(t *testing.T) { } // Make sure Alice.A and Alice.B are stopped - assert.Regexp(t, shutdownLogPattern("alice", "a"), ts.logBuffer.String()) - assert.Regexp(t, shutdownLogPattern("alice", "b"), ts.logBuffer.String()) + assert.Regexp(t, shutdownLogPattern("alice", "a"), ts.logger.String()) + assert.Regexp(t, shutdownLogPattern("alice", "b"), ts.logger.String()) // But Bob.C is still running - assert.NotRegexp(t, shutdownLogPattern("bob", "c"), ts.logBuffer.String()) + assert.NotRegexp(t, shutdownLogPattern("bob", "c"), ts.logger.String()) // ACT #2 time.Sleep(200 * time.Millisecond) @@ -241,7 +239,7 @@ func TestScheduler(t *testing.T) { // ASSERT #2 // Bob.C is not running - assert.Regexp(t, shutdownLogPattern("bob", "c"), ts.logBuffer.String()) + assert.Regexp(t, shutdownLogPattern("bob", "c"), ts.logger.String()) }) t.Run("Block tick: tick is faster than the block", func(t *testing.T) { @@ -274,8 +272,8 @@ func TestScheduler(t *testing.T) { // ASSERT assert.Equal(t, int64(21), counter) - assert.Contains(t, ts.logBuffer.String(), "Stopped scheduler task") - assert.Contains(t, ts.logBuffer.String(), `"task.type":"block_ticker"`) + assert.Contains(t, ts.logger.String(), "Stopped scheduler task") + assert.Contains(t, ts.logger.String(), `"task.type":"block_ticker"`) }) t.Run("Block tick: tick is slower than the block", func(t *testing.T) { @@ -349,7 +347,7 @@ func TestScheduler(t *testing.T) { // ASSERT // zero indicates that Stop() waits for current iteration to finish (graceful shutdown) assert.Equal(t, int64(0), counter) - assert.Contains(t, ts.logBuffer.String(), "Block channel closed") + assert.Contains(t, ts.logger.String(), "Block channel closed") }) } @@ -357,19 +355,19 @@ type testSuite struct { ctx context.Context scheduler *Scheduler - logger zerolog.Logger - logBuffer *bytes.Buffer + logger *testlog.Log } func newTestSuite(t *testing.T) *testSuite { - logBuffer := &bytes.Buffer{} - logger := zerolog.New(io.MultiWriter(zerolog.NewTestWriter(t), logBuffer)) + logger := testlog.New(t) + + scheduler := New(logger.Logger, time.Second) + t.Cleanup(scheduler.Stop) return &testSuite{ ctx: context.Background(), - scheduler: New(logger), + scheduler: scheduler, logger: logger, - logBuffer: logBuffer, } } diff --git a/precompiles/bank/method_test.go b/precompiles/bank/method_test.go index 7d2a26b4bd..cde8597971 100644 --- a/precompiles/bank/method_test.go +++ b/precompiles/bank/method_test.go @@ -1,6 +1,7 @@ package bank import ( + "github.com/zeta-chain/node/e2e/contracts/erc1967proxy" "math/big" "testing" @@ -18,7 +19,6 @@ import ( evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper" "github.com/zeta-chain/ethermint/x/evm/statedb" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/contracts/erc1967proxy" "github.com/zeta-chain/node/pkg/ptr" precompiletypes "github.com/zeta-chain/node/precompiles/types" "github.com/zeta-chain/node/testutil/keeper" diff --git a/precompiles/staking/staking_test.go b/precompiles/staking/staking_test.go index 7773490e1d..bd86951579 100644 --- a/precompiles/staking/staking_test.go +++ b/precompiles/staking/staking_test.go @@ -2,6 +2,7 @@ package staking import ( "encoding/json" + "github.com/zeta-chain/node/e2e/contracts/erc1967proxy" "testing" "math/big" @@ -28,7 +29,6 @@ import ( "github.com/zeta-chain/ethermint/x/evm/statedb" "github.com/zeta-chain/node/cmd/zetacored/config" "github.com/zeta-chain/node/pkg/chains" - "github.com/zeta-chain/node/pkg/contracts/erc1967proxy" "github.com/zeta-chain/node/pkg/ptr" "github.com/zeta-chain/node/precompiles/prototype" "github.com/zeta-chain/node/testutil/keeper" diff --git a/proto/buf.openapi.yaml b/proto/buf.openapi.yaml index 0a2666f814..f478fe587e 100644 --- a/proto/buf.openapi.yaml +++ b/proto/buf.openapi.yaml @@ -6,4 +6,4 @@ plugins: - name: openapiv2 out: . strategy: all - opt: allow_merge=true,merge_file_name=zetachain,output_format=yaml,json_names_for_fields=false + opt: allow_merge=true,merge_file_name=zetachain,output_format=yaml,openapi_naming_strategy=fqn,simple_operation_ids=true,enable_rpc_deprecation=true diff --git a/proto/zetachain/zetacore/crosschain/genesis.proto b/proto/zetachain/zetacore/crosschain/genesis.proto index 97aa1d8dba..da3c9806fa 100644 --- a/proto/zetachain/zetacore/crosschain/genesis.proto +++ b/proto/zetachain/zetacore/crosschain/genesis.proto @@ -26,4 +26,5 @@ message GenesisState { ZetaAccounting zeta_accounting = 12 [ (gogoproto.nullable) = false ]; repeated string FinalizedInbounds = 16; RateLimiterFlags rate_limiter_flags = 17 [ (gogoproto.nullable) = false ]; + uint64 counter = 18; } diff --git a/proto/zetachain/zetacore/crosschain/query.proto b/proto/zetachain/zetacore/crosschain/query.proto index bfbd89ea8e..23e4b0d218 100644 --- a/proto/zetachain/zetacore/crosschain/query.proto +++ b/proto/zetachain/zetacore/crosschain/query.proto @@ -166,12 +166,14 @@ service Query { returns (QueryGetOutboundTrackerResponse) { option (google.api.http).get = "/zeta-chain/crosschain/outTxTracker/{chainID}/{nonce}"; + option deprecated = true; } // Deprecated(v17): use OutboundTrackerAll rpc OutTxTrackerAll(QueryAllOutboundTrackerRequest) returns (QueryAllOutboundTrackerResponse) { option (google.api.http).get = "/zeta-chain/crosschain/outTxTracker"; + option deprecated = true; } // Deprecated(v17): use OutboundTrackerAllByChain @@ -179,6 +181,7 @@ service Query { returns (QueryAllOutboundTrackerByChainResponse) { option (google.api.http).get = "/zeta-chain/crosschain/outTxTrackerByChain/{chain}"; + option deprecated = true; } // Deprecated(v17): use InboundTrackerAllByChain @@ -186,12 +189,14 @@ service Query { returns (QueryAllInboundTrackerByChainResponse) { option (google.api.http).get = "/zeta-chain/crosschain/inTxTrackerByChain/{chain_id}"; + option deprecated = true; } // Deprecated(v17): use InboundTrackerAll rpc InTxTrackerAll(QueryAllInboundTrackersRequest) returns (QueryAllInboundTrackersResponse) { option (google.api.http).get = "/zeta-chain/crosschain/inTxTracker"; + option deprecated = true; } // Deprecated(v17): use InboundHashToCctx @@ -199,6 +204,7 @@ service Query { returns (QueryGetInboundHashToCctxResponse) { option (google.api.http).get = "/zeta-chain/crosschain/inTxHashToCctx/{inboundHash}"; + option deprecated = true; } // Deprecated(v17): use InboundHashToCctxData @@ -206,12 +212,14 @@ service Query { returns (QueryInboundHashToCctxDataResponse) { option (google.api.http).get = "/zeta-chain/crosschain/inTxHashToCctxData/{inboundHash}"; + option deprecated = true; } // Deprecated(v17): use InboundHashToCctxAll rpc InTxHashToCctxAll(QueryAllInboundHashToCctxRequest) returns (QueryAllInboundHashToCctxResponse) { option (google.api.http).get = "/zeta-chain/crosschain/inTxHashToCctx"; + option deprecated = true; } } @@ -325,6 +333,9 @@ message QueryGetCctxResponse { CrossChainTx CrossChainTx = 1; } message QueryAllCctxRequest { cosmos.base.query.v1beta1.PageRequest pagination = 1; + // Bulk dump all CCTX without ordering. + // This is useful for initializing a CCTX indexer. + bool unordered = 2; } message QueryAllCctxResponse { diff --git a/proto/zetachain/zetacore/lightclient/query.proto b/proto/zetachain/zetacore/lightclient/query.proto index 77f2fdeb1d..e07c0077ab 100644 --- a/proto/zetachain/zetacore/lightclient/query.proto +++ b/proto/zetachain/zetacore/lightclient/query.proto @@ -17,39 +17,46 @@ service Query { rpc BlockHeaderAll(QueryAllBlockHeaderRequest) returns (QueryAllBlockHeaderResponse) { option (google.api.http).get = "/zeta-chain/lightclient/block_headers"; + option deprecated = true; } rpc BlockHeader(QueryGetBlockHeaderRequest) returns (QueryGetBlockHeaderResponse) { option (google.api.http).get = "/zeta-chain/lightclient/block_headers/{block_hash}"; + option deprecated = true; } rpc ChainStateAll(QueryAllChainStateRequest) returns (QueryAllChainStateResponse) { option (google.api.http).get = "/zeta-chain/lightclient/chain_state"; + option deprecated = true; } rpc ChainState(QueryGetChainStateRequest) returns (QueryGetChainStateResponse) { option (google.api.http).get = "/zeta-chain/lightclient/chain_state/{chain_id}"; + option deprecated = true; } rpc Prove(QueryProveRequest) returns (QueryProveResponse) { option (google.api.http).get = "/zeta-chain/lightclient/prove"; + option deprecated = true; } rpc HeaderSupportedChains(QueryHeaderSupportedChainsRequest) returns (QueryHeaderSupportedChainsResponse) { option (google.api.http).get = "/zeta-chain/lightclient/header_supported_chains"; + option deprecated = true; } rpc HeaderEnabledChains(QueryHeaderEnabledChainsRequest) returns (QueryHeaderEnabledChainsResponse) { option (google.api.http).get = "/zeta-chain/lightclient/header_enabled_chains"; + option deprecated = true; } } diff --git a/scripts/protoc-gen-openapi.sh b/scripts/protoc-gen-openapi.sh index 3a4a40e097..d668b93443 100755 --- a/scripts/protoc-gen-openapi.sh +++ b/scripts/protoc-gen-openapi.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash -go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.16.2 +set -eo pipefail + +go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@v2.26.1 go mod download @@ -54,12 +56,19 @@ keys=$(yq e '.paths | keys' $OUTPUT_DIR/$MERGED_SWAGGER_FILE) for key in $keys; do # Check if key starts with '/cosmos/NAME' if [[ $key == "/cosmos/"* ]]; then + # Exclude paths starting with /cosmos/gov/v1beta1 + # these endpoints are broken post v0.47 upgrade + if [[ $key == "/cosmos/gov/v1beta1"* ]]; then + yq e "del(.paths.\"$key\")" -i $OUTPUT_DIR/$MERGED_SWAGGER_FILE + continue + fi + # Extract NAME name=$(echo $key | cut -d '/' -f 3) # Check if the standard module is not imported in the app.go if ! grep -q "github.com/cosmos/cosmos-sdk/x/$name" $APP_GO; then - # Keep the standard "base" and "tx" endpoints - if [[ $name == "base" || $name == "tx" ]]; then + # Keep the standard "base", "tx", and "upgrade" endpoints + if [[ $name == "base" || $name == "tx" || $name == "upgrade" ]]; then continue fi # If not found, delete the key from the YAML file in-place diff --git a/testutil/contracts/bindings.go b/testutil/contracts/bindings.go deleted file mode 100644 index 70db08dca4..0000000000 --- a/testutil/contracts/bindings.go +++ /dev/null @@ -1,38 +0,0 @@ -// Example -//go:generate sh -c "solc --evm-version paris Example.sol --combined-json abi,bin | jq '.contracts.\"Example.sol:Example\"' > Example.json" -//go:generate sh -c "cat Example.json | jq .abi > Example.abi" -//go:generate sh -c "cat Example.json | jq .bin | tr -d '\"' > Example.bin" -//go:generate sh -c "abigen --abi Example.abi --bin Example.bin --pkg contracts --type Example --out Example.go" - -// Reverter -//go:generate sh -c "solc --evm-version paris Reverter.sol --combined-json abi,bin | jq '.contracts.\"Reverter.sol:Reverter\"' > Reverter.json" -//go:generate sh -c "cat Reverter.json | jq .abi > Reverter.abi" -//go:generate sh -c "cat Reverter.json | jq .bin | tr -d '\"' > Reverter.bin" -//go:generate sh -c "abigen --abi Reverter.abi --bin Reverter.bin --pkg contracts --type Reverter --out Reverter.go" - -// Depositor -//go:generate sh -c "solc --evm-version paris Depositor.sol --combined-json abi,bin | jq '.contracts.\"Depositor.sol:Depositor\"' > Depositor.json" -//go:generate sh -c "cat Depositor.json | jq .abi > Depositor.abi" -//go:generate sh -c "cat Depositor.json | jq .bin | tr -d '\"' > Depositor.bin" -//go:generate sh -c "abigen --abi Depositor.abi --bin Depositor.bin --pkg contracts --type Depositor --out Depositor.go" - -// Withdrawer -//go:generate sh -c "solc --evm-version paris Withdrawer.sol --combined-json abi,bin | jq '.contracts.\"Withdrawer.sol:Withdrawer\"' > Withdrawer.json" -//go:generate sh -c "cat Withdrawer.json | jq .abi > Withdrawer.abi" -//go:generate sh -c "cat Withdrawer.json | jq .bin | tr -d '\"' > Withdrawer.bin" -//go:generate sh -c "abigen --abi Withdrawer.abi --bin Withdrawer.bin --pkg contracts --type Withdrawer --out Withdrawer.go" - -// Dapp -// -//go:generate sh -c "solc --evm-version paris Dapp.sol --combined-json abi,bin | jq '.contracts.\"Dapp.sol:Dapp\"' > Dapp.json" -//go:generate sh -c "cat Dapp.json | jq .abi > Dapp.abi" -//go:generate sh -c "cat Dapp.json | jq .bin | tr -d '\"' > Dapp.bin" -//go:generate sh -c "abigen --abi Dapp.abi --bin Dapp.bin --pkg contracts --type Dapp --out Dapp.go" - -// DappReverter -//go:generate sh -c "solc --evm-version paris DappReverter.sol --combined-json abi,bin | jq '.contracts.\"DappReverter.sol:DappReverter\"' > DappReverter.json" -//go:generate sh -c "cat DappReverter.json | jq .abi > DappReverter.abi" -//go:generate sh -c "cat DappReverter.json | jq .bin | tr -d '\"' > DappReverter.bin" -//go:generate sh -c "abigen --abi DappReverter.abi --bin DappReverter.bin --pkg contracts --type DappReverter --out DappReverter.go" - -package contracts diff --git a/testutil/keeper/crosschain.go b/testutil/keeper/crosschain.go index 151c7aa6ca..7862c8549b 100644 --- a/testutil/keeper/crosschain.go +++ b/testutil/keeper/crosschain.go @@ -313,7 +313,7 @@ func MockProcessV2RevertDeposit( // inboundSender string // amount *big.Int // chainID int64 - // coinType coin.CoinType + // coinType coin.FungibleTokenCoinType // asset string // revertAddress common.Address // callOnRevert bool diff --git a/typescript/zetachain/zetacore/crosschain/genesis_pb.d.ts b/typescript/zetachain/zetacore/crosschain/genesis_pb.d.ts index 35b98487a7..6b89f5be3b 100644 --- a/typescript/zetachain/zetacore/crosschain/genesis_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/genesis_pb.d.ts @@ -64,6 +64,11 @@ export declare class GenesisState extends Message { */ rateLimiterFlags?: RateLimiterFlags; + /** + * @generated from field: uint64 counter = 18; + */ + counter: bigint; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/typescript/zetachain/zetacore/crosschain/query_pb.d.ts b/typescript/zetachain/zetacore/crosschain/query_pb.d.ts index 7b0766beca..fad553d76f 100644 --- a/typescript/zetachain/zetacore/crosschain/query_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/query_pb.d.ts @@ -769,6 +769,14 @@ export declare class QueryAllCctxRequest extends Message { */ pagination?: PageRequest; + /** + * Bulk dump all CCTX without ordering. + * This is useful for initializing a CCTX indexer. + * + * @generated from field: bool unordered = 2; + */ + unordered: boolean; + constructor(data?: PartialMessage); static readonly runtime: typeof proto3; diff --git a/x/crosschain/genesis.go b/x/crosschain/genesis.go index 0f6856d559..41999fa0e6 100644 --- a/x/crosschain/genesis.go +++ b/x/crosschain/genesis.go @@ -55,6 +55,8 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) } k.SetRateLimiterFlags(ctx, genState.RateLimiterFlags) + + k.SetCctxCounter(ctx, genState.Counter) } // ExportGenesis returns the crosschain module's exported genesis. @@ -97,5 +99,7 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { genesis.RateLimiterFlags = rateLimiterFlags } + genesis.Counter = k.GetCctxCounter(ctx) + return &genesis } diff --git a/x/crosschain/genesis_test.go b/x/crosschain/genesis_test.go index 11aa8964b9..f63810d5ab 100644 --- a/x/crosschain/genesis_test.go +++ b/x/crosschain/genesis_test.go @@ -51,6 +51,7 @@ func TestGenesis(t *testing.T) { sample.InboundHashToCctx(t, "0x2"), }, RateLimiterFlags: sample.RateLimiterFlags(), + Counter: 1, } // Init and export diff --git a/x/crosschain/keeper/cctx.go b/x/crosschain/keeper/cctx.go index aee4600b97..c0ece67848 100644 --- a/x/crosschain/keeper/cctx.go +++ b/x/crosschain/keeper/cctx.go @@ -103,7 +103,14 @@ func (k Keeper) SetCrossChainTx(ctx sdk.Context, cctx types.CrossChainTx) { p := types.KeyPrefix(fmt.Sprintf("%s", types.CCTXKey)) store := prefix.NewStore(ctx.KVStore(k.storeKey), p) b := k.cdc.MustMarshal(&cctx) - store.Set(types.KeyPrefix(cctx.Index), b) + cctxIndex := types.KeyPrefix(cctx.Index) + + isUpdate := store.Has(cctxIndex) + store.Set(cctxIndex, b) + + if !isUpdate { + k.setCctxCounterIndex(ctx, cctx) + } } // GetCrossChainTx returns a cctx from its index diff --git a/x/crosschain/keeper/cctx_counter.go b/x/crosschain/keeper/cctx_counter.go new file mode 100644 index 0000000000..979ed5e353 --- /dev/null +++ b/x/crosschain/keeper/cctx_counter.go @@ -0,0 +1,62 @@ +package keeper + +import ( + "cosmossdk.io/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/zeta-chain/node/x/crosschain/types" +) + +func (k Keeper) getCounterValueStore(ctx sdk.Context) prefix.Store { + return prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CounterValueKey)) +} + +func (k Keeper) getCounterIndexStore(ctx sdk.Context) prefix.Store { + return prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.CounterIndexKey)) +} + +// GetCctxCounter retrieves the current counter value +// +// Will return 0 if the counter has never been set +func (k Keeper) GetCctxCounter(ctx sdk.Context) uint64 { + store := k.getCounterValueStore(ctx) + storedCounter := store.Get([]byte(types.CounterValueKey)) + + return sdk.BigEndianToUint64(storedCounter) +} + +// SetCctxCounter updates the current counter value +// +// This should only be used in setCctxCounterIndex and in state import +func (k Keeper) SetCctxCounter(ctx sdk.Context, val uint64) { + store := k.getCounterValueStore(ctx) + store.Set([]byte(types.CounterValueKey), sdk.Uint64ToBigEndian(val)) +} + +// getNextCctxCounter retrieves and increments the counter for ordering +func (k Keeper) getNextCctxCounter(ctx sdk.Context) uint64 { + storedCounter := k.GetCctxCounter(ctx) + nextCounter := storedCounter + 1 + k.SetCctxCounter(ctx, nextCounter) + return nextCounter +} + +// setCctxCounterIndex sets a new CCTX in the counter index +// +// note that we use the raw bytes in the index rather than the hex encoded bytes +// like in the main store +func (k Keeper) setCctxCounterIndex(ctx sdk.Context, cctx types.CrossChainTx) { + counterIndexStore := k.getCounterIndexStore(ctx) + nextCounter := k.getNextCctxCounter(ctx) + + cctxIndex, err := cctx.GetCCTXIndexBytes() + if err != nil { + k.Logger(ctx).Error("get cctx index bytes", "err", err) + return + } + + // must use big endian so most significant bytes are first for sortability + // we binary OR the counter so it sorts in descending rather than ascending order by default + nextCounterBytes := sdk.Uint64ToBigEndian(^nextCounter) + counterIndexStore.Set(nextCounterBytes, cctxIndex[:]) +} diff --git a/x/crosschain/keeper/cctx_counter_test.go b/x/crosschain/keeper/cctx_counter_test.go new file mode 100644 index 0000000000..8e8174bb2b --- /dev/null +++ b/x/crosschain/keeper/cctx_counter_test.go @@ -0,0 +1,23 @@ +package keeper_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + testkeeper "github.com/zeta-chain/node/testutil/keeper" +) + +func TestCounter(t *testing.T) { + keeper, ctx, _, _ := testkeeper.CrosschainKeeper(t) + initialCounter := keeper.GetCctxCounter(ctx) + require.Zero(t, initialCounter) + + nextVal := keeper.GetNextCctxCounter(ctx) + require.Greater(t, nextVal, initialCounter) + require.Equal(t, nextVal, keeper.GetCctxCounter(ctx)) + + // also test direct set + nextVal += 1 + keeper.SetCctxCounter(ctx, nextVal) + require.Equal(t, nextVal, keeper.GetCctxCounter(ctx)) +} diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go index 3ae74fe45c..3aaacf2c24 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_inbound.go @@ -1,6 +1,7 @@ package keeper import ( + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/zeta-chain/node/pkg/chains" @@ -20,9 +21,8 @@ func (k Keeper) ValidateInbound( if !tssFound { return nil, types.ErrCannotFindTSSKeys } - err := k.CheckIfTSSMigrationTransfer(ctx, msg) - if err != nil { - return nil, err + if err := k.CheckIfTSSMigrationTransfer(ctx, msg); err != nil { + return nil, errors.Wrap(err, "tss migration transfer check failed") } // Do not process if inbound is disabled @@ -33,7 +33,7 @@ func (k Keeper) ValidateInbound( // create a new CCTX from the inbound message. The status of the new CCTX is set to PendingInbound. cctx, err := types.NewCCTX(ctx, *msg, tss.TssPubkey) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to create new CCTX") } // Initiate outbound, the process function manages the state commit and cctx status change. @@ -43,7 +43,7 @@ func (k Keeper) ValidateInbound( ShouldPayGas: shouldPayGas, }) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to initiate outbound") } inCctxIndex, ok := ctx.Value(InCCTXIndexKey).(string) diff --git a/x/crosschain/keeper/cctx_orchestrator_validate_outbound_test.go b/x/crosschain/keeper/cctx_orchestrator_validate_outbound_test.go index bd4a986344..02f5c3f0d9 100644 --- a/x/crosschain/keeper/cctx_orchestrator_validate_outbound_test.go +++ b/x/crosschain/keeper/cctx_orchestrator_validate_outbound_test.go @@ -3,20 +3,20 @@ package keeper_test import ( "encoding/base64" "errors" - "github.com/ethereum/go-ethereum/core/vm" - evmtypes "github.com/zeta-chain/ethermint/x/evm/types" + "github.com/zeta-chain/node/e2e/contracts/dapp" "math/big" "testing" cosmoserror "cosmossdk.io/errors" ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/zeta-chain/ethermint/x/evm/statedb" + evmtypes "github.com/zeta-chain/ethermint/x/evm/types" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/testutil/contracts" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/x/crosschain/types" @@ -408,7 +408,7 @@ func TestKeeper_ValidateFailedOutbound(t *testing.T) { cctx.RelayedMessage = base64.StdEncoding.EncodeToString([]byte("sample message")) deploySystemContracts(t, ctx, zk.FungibleKeeper, sdkk.EvmKeeper) - dAppContract, err := zk.FungibleKeeper.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := zk.FungibleKeeper.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) cctx.InboundParams.Sender = dAppContract.String() @@ -424,7 +424,7 @@ func TestKeeper_ValidateFailedOutbound(t *testing.T) { require.Equal(t, types.CctxStatus_Reverted, cctx.CctxStatus.Status) require.Equal(t, cctx.GetCurrentOutboundParam().TxFinalizationStatus, types.TxFinalizationStatus_Executed) - dappAbi, err := contracts.DappMetaData.GetAbi() + dappAbi, err := dapp.DappMetaData.GetAbi() require.NoError(t, err) res, err := zk.FungibleKeeper.CallEVM( ctx, diff --git a/x/crosschain/keeper/cctx_test.go b/x/crosschain/keeper/cctx_test.go index 29afecb6a8..ba62d0d6c2 100644 --- a/x/crosschain/keeper/cctx_test.go +++ b/x/crosschain/keeper/cctx_test.go @@ -3,6 +3,8 @@ package keeper_test import ( "fmt" "math/rand" + "slices" + "strings" "testing" "cosmossdk.io/math" @@ -85,7 +87,7 @@ func createNCctx(keeper *keeper.Keeper, ctx sdk.Context, n int, tssPubkey string items[i].InboundParams.Amount = math.OneUint() items[i].ZetaFees = math.OneUint() - items[i].Index = fmt.Sprintf("%d", i) + items[i].Index = sample.GetCctxIndexFromString(fmt.Sprintf("%d", i)) items[i].RevertOptions = types.NewEmptyRevertOptions() keeper.SaveCCTXUpdate(ctx, items[i], tssPubkey) @@ -174,17 +176,21 @@ func TestCCTXs(t *testing.T) { } } +func compareCctx(l types.CrossChainTx, r types.CrossChainTx) int { + return strings.Compare(l.Index, r.Index) +} + func TestCCTXGetAll(t *testing.T) { keeper, ctx, _, zk := keepertest.CrosschainKeeper(t) tss := sample.Tss() zk.ObserverKeeper.SetTSS(ctx, tss) items := createNCctx(keeper, ctx, 10, tss.TssPubkey) cctx := keeper.GetAllCrossChainTx(ctx) - c := make([]types.CrossChainTx, len(cctx)) - for i, val := range cctx { - c[i] = val - } - require.Equal(t, items, c) + + slices.SortFunc(items, compareCctx) + slices.SortFunc(cctx, compareCctx) + + require.Equal(t, items, cctx) } // Querier Tests @@ -247,6 +253,7 @@ func TestCCTXQueryPaginated(t *testing.T) { Offset: offset, Limit: limit, CountTotal: total, + Reverse: true, }, } } diff --git a/x/crosschain/keeper/evm_deposit_test.go b/x/crosschain/keeper/evm_deposit_test.go index b65071a13f..6ba9017ccd 100644 --- a/x/crosschain/keeper/evm_deposit_test.go +++ b/x/crosschain/keeper/evm_deposit_test.go @@ -95,7 +95,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) fungibleMock.On( "ZRC20DepositAndCallContract", mock.Anything, @@ -142,7 +142,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) fungibleMock.On( "ZRC20DepositAndCallContract", mock.Anything, @@ -206,7 +206,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) fungibleMock.On( "ZRC20DepositAndCallContract", mock.Anything, @@ -296,7 +296,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) errDeposit := errors.New("deposit failed") fungibleMock.On( "ZRC20DepositAndCallContract", @@ -342,7 +342,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) errDeposit := errors.New("deposit failed") fungibleMock.On( "ZRC20DepositAndCallContract", @@ -388,7 +388,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) fungibleMock.On( "ZRC20DepositAndCallContract", mock.Anything, @@ -433,7 +433,7 @@ func TestMsgServer_HandleEVMDeposit(t *testing.T) { amount := big.NewInt(42) // expect DepositCoinZeta to be called - // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.CoinType, msg.Asset) + // ZRC20DepositAndCallContract(ctx, from, to, msg.Amount.BigInt(), senderChain, msg.Message, contract, data, msg.FungibleTokenCoinType, msg.Asset) fungibleMock.On( "ZRC20DepositAndCallContract", mock.Anything, diff --git a/x/crosschain/keeper/export_private_functions_test.go b/x/crosschain/keeper/export_private_functions_test.go index 0611ed995d..7d79695d13 100644 --- a/x/crosschain/keeper/export_private_functions_test.go +++ b/x/crosschain/keeper/export_private_functions_test.go @@ -18,3 +18,7 @@ func (k Keeper) UpdateInboundHashToCCTX(ctx sdk.Context, cctx types.CrossChainTx func (k Keeper) SetNonceToCCTX(ctx sdk.Context, cctx types.CrossChainTx, tssPubkey string) { k.setNonceToCCTX(ctx, cctx, tssPubkey) } + +func (k Keeper) GetNextCctxCounter(ctx sdk.Context) uint64 { + return k.getNextCctxCounter(ctx) +} diff --git a/x/crosschain/keeper/grpc_query_cctx.go b/x/crosschain/keeper/grpc_query_cctx.go index 80d27c7aea..01ee36c458 100644 --- a/x/crosschain/keeper/grpc_query_cctx.go +++ b/x/crosschain/keeper/grpc_query_cctx.go @@ -22,6 +22,7 @@ const ( MaxLookbackNonce = 1000 DefaultPageSize = 100 + MaxPageSize = 1000 ) func (k Keeper) ZetaAccounting( @@ -42,11 +43,11 @@ func (k Keeper) CctxAll(c context.Context, req *types.QueryAllCctxRequest) (*typ if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } - var sends []*types.CrossChainTx ctx := sdk.UnwrapSDKContext(c) store := ctx.KVStore(k.storeKey) - sendStore := prefix.NewStore(store, types.KeyPrefix(types.CCTXKey)) + cctxStore := prefix.NewStore(store, types.KeyPrefix(types.CCTXKey)) + counterStore := k.getCounterIndexStore(ctx) if req.Pagination == nil { req.Pagination = &query.PageRequest{} @@ -54,15 +55,37 @@ func (k Keeper) CctxAll(c context.Context, req *types.QueryAllCctxRequest) (*typ if req.Pagination.Limit == 0 { req.Pagination.Limit = DefaultPageSize } + if req.Pagination.Limit > MaxPageSize { + req.Pagination.Limit = MaxPageSize + } - pageRes, err := query.Paginate(sendStore, req.Pagination, func(_ []byte, value []byte) error { - var send types.CrossChainTx - if err := k.cdc.Unmarshal(value, &send); err != nil { - return err - } - sends = append(sends, &send) - return nil - }) + var sends []*types.CrossChainTx + var pageRes *query.PageResponse + var err error + if req.Unordered { + pageRes, err = query.Paginate(cctxStore, req.Pagination, func(_ []byte, value []byte) error { + var send types.CrossChainTx + if err := k.cdc.Unmarshal(value, &send); err != nil { + return err + } + sends = append(sends, &send) + return nil + }) + } else { + pageRes, err = query.Paginate(counterStore, req.Pagination, func(_ []byte, value []byte) error { + cctxIndex, err := types.GetCctxIndexFromArbitraryBytes(value) + if err != nil { + return err + } + var cctx types.CrossChainTx + cctxBytes := cctxStore.Get(types.KeyPrefix(cctxIndex)) + if err := k.cdc.Unmarshal(cctxBytes, &cctx); err != nil { + return err + } + sends = append(sends, &cctx) + return nil + }) + } if err != nil { return nil, status.Error(codes.Internal, err.Error()) diff --git a/x/crosschain/keeper/grpc_query_cctx_test.go b/x/crosschain/keeper/grpc_query_cctx_test.go index 8d7043d9f8..cbda6252b7 100644 --- a/x/crosschain/keeper/grpc_query_cctx_test.go +++ b/x/crosschain/keeper/grpc_query_cctx_test.go @@ -2,6 +2,8 @@ package keeper_test import ( "fmt" + "slices" + "strings" "testing" sdkmath "cosmossdk.io/math" @@ -291,6 +293,18 @@ func TestKeeper_CctxByNonce(t *testing.T) { }) } +func assertCctxIndexEqual(t *testing.T, expectedCctxs []*types.CrossChainTx, cctxs []*types.CrossChainTx) { + t.Helper() + require.Equal(t, len(expectedCctxs), len(cctxs), "slice lengths not equal") + for i, expectedCctx := range expectedCctxs { + require.Equal(t, expectedCctx.Index, cctxs[i].Index, "index missmatch at %v", i) + } +} + +func sortByIndex(l *types.CrossChainTx, r *types.CrossChainTx) int { + return strings.Compare(l.Index, r.Index) +} + func TestKeeper_CctxAll(t *testing.T) { t.Run("empty request", func(t *testing.T) { k, ctx, _, _ := keepertest.CrosschainKeeper(t) @@ -326,4 +340,47 @@ func TestKeeper_CctxAll(t *testing.T) { require.NoError(t, err) require.Len(t, res.CrossChainTx, testPageSize) }) + + t.Run("basic descending ordering", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID() + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + createdCctx := createCctxWithNonceRange(t, ctx, *k, 0, 10, chainID, tss, zk) + + res, err := k.CctxAll(ctx, &types.QueryAllCctxRequest{}) + require.NoError(t, err) + slices.Reverse(createdCctx) + assertCctxIndexEqual(t, createdCctx, res.CrossChainTx) + + // also assert unordered query return same number of results + resUnordered, err := k.CctxAll(ctx, &types.QueryAllCctxRequest{ + Unordered: true, + }) + require.NoError(t, err) + require.Len(t, res.CrossChainTx, len(resUnordered.CrossChainTx)) + }) + + t.Run("basic ascending ordering", func(t *testing.T) { + k, ctx, _, zk := keepertest.CrosschainKeeper(t) + chainID := getValidEthChainID() + tss := sample.Tss() + zk.ObserverKeeper.SetTSS(ctx, tss) + createdCctx := createCctxWithNonceRange(t, ctx, *k, 0, 10, chainID, tss, zk) + + res, err := k.CctxAll(ctx, &types.QueryAllCctxRequest{ + Pagination: &query.PageRequest{ + Reverse: true, + }, + }) + require.NoError(t, err) + assertCctxIndexEqual(t, createdCctx, res.CrossChainTx) + + // also assert unordered query return same number of results + resUnordered, err := k.CctxAll(ctx, &types.QueryAllCctxRequest{ + Unordered: true, + }) + require.NoError(t, err) + require.Len(t, res.CrossChainTx, len(resUnordered.CrossChainTx)) + }) } diff --git a/x/crosschain/keeper/msg_server_vote_inbound_tx.go b/x/crosschain/keeper/msg_server_vote_inbound_tx.go index 0c1b875479..ebdd507575 100644 --- a/x/crosschain/keeper/msg_server_vote_inbound_tx.go +++ b/x/crosschain/keeper/msg_server_vote_inbound_tx.go @@ -75,7 +75,7 @@ func (k msgServer) VoteInbound( msg.InboundHash, ) if err != nil { - return nil, sdkerrors.Wrap(err, voteInboundID) + return nil, sdkerrors.Wrap(err, "failed to vote on inbound ballot") } // If it is a new ballot, check if an inbound with the same hash, sender chain and event index has already been finalized @@ -102,7 +102,7 @@ func (k msgServer) VoteInbound( cctx, err := k.ValidateInbound(ctx, msg, true) if err != nil { - return nil, sdkerrors.Wrap(err, voteInboundID) + return nil, sdkerrors.Wrap(err, "failed to validate inbound") } // Save the inbound CCTX to the store. This is called irrespective of the status of the CCTX or the outcome of the process function. k.SaveObservedInboundInformation(ctx, cctx, msg.EventIndex) diff --git a/x/crosschain/keeper/utils_test.go b/x/crosschain/keeper/utils_test.go index 0c4ee9bfe1..1b277aad25 100644 --- a/x/crosschain/keeper/utils_test.go +++ b/x/crosschain/keeper/utils_test.go @@ -2,6 +2,7 @@ package keeper_test import ( + "github.com/zeta-chain/node/e2e/contracts/erc1967proxy" "math/big" "testing" @@ -12,7 +13,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" evmkeeper "github.com/zeta-chain/ethermint/x/evm/keeper" - "github.com/zeta-chain/node/pkg/contracts/erc1967proxy" "github.com/zeta-chain/node/pkg/contracts/uniswap/v2-periphery/contracts/uniswapv2router02.sol" "github.com/zeta-chain/node/pkg/ptr" fungibletypes "github.com/zeta-chain/node/x/fungible/types" diff --git a/x/crosschain/types/cctx.go b/x/crosschain/types/cctx.go index 86f371e594..e2ca13f273 100644 --- a/x/crosschain/types/cctx.go +++ b/x/crosschain/types/cctx.go @@ -180,7 +180,7 @@ func (m *CrossChainTx) AddRevertOutbound(gasLimit uint64) error { ConfirmationMode: m.GetCurrentOutboundParam().ConfirmationMode, } - // TODO : Refactor to move CoinType field to the CCTX object directly : https://github.com/zeta-chain/node/issues/1943 + // TODO : Refactor to move FungibleTokenCoinType field to the CCTX object directly : https://github.com/zeta-chain/node/issues/1943 if m.InboundParams != nil { revertTxParams.CoinType = m.InboundParams.CoinType } @@ -269,6 +269,18 @@ func GetCctxIndexFromBytes(sendHash [32]byte) string { return fmt.Sprintf("0x%s", hex.EncodeToString(sendHash[:])) } +// GetCctxIndexFromArbitraryBytes converts an arbitrary byte slice to a CCTX index string. +// Returns an error if the input slice is less than 32 bytes. +func GetCctxIndexFromArbitraryBytes(sendHash []byte) (string, error) { + if len(sendHash) < 32 { + return "", fmt.Errorf("input byte slice length %d is less than required 32 bytes", len(sendHash)) + } + + var indexBytes [32]byte + copy(indexBytes[:], sendHash[:32]) + return GetCctxIndexFromBytes(indexBytes), nil +} + // NewCCTX creates a new CCTX from a MsgVoteInbound message and a TSS pubkey. // It also validates the created cctx func NewCCTX(ctx sdk.Context, msg MsgVoteInbound, tssPubkey string) (CrossChainTx, error) { diff --git a/x/crosschain/types/genesis.pb.go b/x/crosschain/types/genesis.pb.go index d1dbc488e1..27b71e60ce 100644 --- a/x/crosschain/types/genesis.pb.go +++ b/x/crosschain/types/genesis.pb.go @@ -34,6 +34,7 @@ type GenesisState struct { ZetaAccounting ZetaAccounting `protobuf:"bytes,12,opt,name=zeta_accounting,json=zetaAccounting,proto3" json:"zeta_accounting"` FinalizedInbounds []string `protobuf:"bytes,16,rep,name=FinalizedInbounds,proto3" json:"FinalizedInbounds,omitempty"` RateLimiterFlags RateLimiterFlags `protobuf:"bytes,17,opt,name=rate_limiter_flags,json=rateLimiterFlags,proto3" json:"rate_limiter_flags"` + Counter uint64 `protobuf:"varint,18,opt,name=counter,proto3" json:"counter,omitempty"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -132,6 +133,13 @@ func (m *GenesisState) GetRateLimiterFlags() RateLimiterFlags { return RateLimiterFlags{} } +func (m *GenesisState) GetCounter() uint64 { + if m != nil { + return m.Counter + } + return 0 +} + func init() { proto.RegisterType((*GenesisState)(nil), "zetachain.zetacore.crosschain.GenesisState") } @@ -141,40 +149,41 @@ func init() { } var fileDescriptor_547615497292ea23 = []byte{ - // 515 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6f, 0xd3, 0x30, - 0x14, 0xc7, 0x5b, 0x06, 0x83, 0x79, 0x05, 0x36, 0x6f, 0x48, 0x51, 0x25, 0x42, 0xc5, 0x85, 0x49, - 0xa3, 0x09, 0xda, 0x00, 0x71, 0x65, 0x95, 0xd6, 0x21, 0x2a, 0x01, 0xa1, 0xa7, 0x09, 0xc9, 0xb8, - 0xae, 0x97, 0x58, 0xcb, 0xe2, 0x2a, 0x7e, 0x95, 0x4a, 0x3f, 0x05, 0x9f, 0x88, 0xf3, 0x8e, 0x3b, - 0x72, 0x42, 0xa8, 0xfd, 0x22, 0xc8, 0x8e, 0x57, 0x9a, 0xb6, 0x4a, 0x7a, 0x7b, 0x7a, 0x79, 0xff, - 0xff, 0xef, 0x29, 0x7f, 0x3f, 0x74, 0x38, 0xe6, 0x40, 0x59, 0x44, 0x45, 0xe2, 0x9b, 0x4a, 0xa6, - 0xdc, 0x67, 0xa9, 0x54, 0x2a, 0xeb, 0x85, 0x3c, 0xe1, 0x4a, 0x28, 0x6f, 0x90, 0x4a, 0x90, 0xf8, - 0xe9, 0x6c, 0xd8, 0xbb, 0x1d, 0xf6, 0xfe, 0x0f, 0xd7, 0x8f, 0x8a, 0xbd, 0x4c, 0x49, 0x4c, 0x4d, - 0x60, 0x94, 0x59, 0xd6, 0x9b, 0x25, 0x7c, 0xaa, 0xc8, 0x20, 0x15, 0x8c, 0xdb, 0xf1, 0x77, 0xc5, - 0xe3, 0x22, 0xe9, 0xc9, 0x61, 0xd2, 0x27, 0x11, 0x55, 0x11, 0x01, 0x49, 0x18, 0x9b, 0x81, 0x8e, - 0xd7, 0x53, 0x42, 0x4a, 0xd9, 0x25, 0x4f, 0xad, 0xe8, 0x4d, 0xb1, 0x28, 0xa6, 0x0a, 0x48, 0x2f, - 0x96, 0xec, 0x92, 0x44, 0x5c, 0x84, 0x11, 0x58, 0xd9, 0xeb, 0x62, 0x99, 0x1c, 0xc2, 0x2a, 0xd8, - 0xdb, 0x62, 0x55, 0x4a, 0x81, 0x93, 0x58, 0x5c, 0x09, 0xe0, 0x29, 0xb9, 0x88, 0x69, 0x68, 0x53, - 0xa9, 0xef, 0x87, 0x32, 0x94, 0xa6, 0xf4, 0x75, 0x95, 0x75, 0x9f, 0xff, 0xda, 0x44, 0xb5, 0x76, - 0x96, 0xde, 0x57, 0xa0, 0xc0, 0xf1, 0x05, 0xda, 0xbb, 0x05, 0x77, 0x33, 0x6e, 0x47, 0x28, 0x70, - 0xee, 0x34, 0x36, 0x0e, 0xb6, 0x8f, 0x3c, 0xaf, 0x30, 0x5a, 0xef, 0x53, 0x5e, 0x79, 0x72, 0xf7, - 0xfa, 0xcf, 0xb3, 0x4a, 0xb0, 0xca, 0x10, 0x7f, 0x44, 0xb5, 0x90, 0xaa, 0xcf, 0x3a, 0x34, 0x03, - 0xb8, 0x67, 0x00, 0x2f, 0x4a, 0x00, 0x6d, 0x2b, 0x09, 0x72, 0x62, 0xfc, 0x05, 0x3d, 0x6c, 0xe9, - 0xa1, 0x96, 0x1e, 0xea, 0x8e, 0x94, 0x73, 0xdf, 0xb8, 0x1d, 0x96, 0xb8, 0xcd, 0x6b, 0x82, 0xbc, - 0x03, 0xfe, 0x8e, 0xf6, 0x74, 0x6e, 0x27, 0x3a, 0xb6, 0x33, 0x93, 0x9a, 0x59, 0xf3, 0xc1, 0x5a, - 0xff, 0xa1, 0x93, 0x57, 0x06, 0xab, 0xac, 0x70, 0x8c, 0x9e, 0xd8, 0xe7, 0x74, 0x46, 0x55, 0xd4, - 0x95, 0x2d, 0x06, 0x23, 0xc3, 0xd8, 0x32, 0x8c, 0x57, 0x25, 0x8c, 0x0f, 0x8b, 0x5a, 0xfb, 0xb7, - 0x57, 0x9b, 0x62, 0x8e, 0xf6, 0x17, 0x1e, 0x2f, 0x89, 0x35, 0x6c, 0xdb, 0xc0, 0x9a, 0xeb, 0xc1, - 0xf2, 0xb9, 0x62, 0x91, 0x2c, 0xc5, 0xfa, 0x0d, 0x3d, 0xd6, 0x7a, 0x42, 0x19, 0x93, 0xc3, 0x04, - 0x44, 0x12, 0x3a, 0xb5, 0x46, 0x75, 0x0d, 0xc2, 0x39, 0x07, 0xfa, 0x7e, 0x26, 0xb2, 0x84, 0x47, - 0xe3, 0x5c, 0x17, 0xbf, 0x44, 0xbb, 0xa7, 0x22, 0xa1, 0xb1, 0x18, 0xf3, 0xbe, 0x5d, 0x49, 0x39, - 0x3b, 0x8d, 0x8d, 0x83, 0xad, 0x60, 0xf9, 0x03, 0x66, 0x08, 0x2f, 0x5f, 0x83, 0xb3, 0x6b, 0xd6, - 0xf1, 0x4b, 0xd6, 0x09, 0x28, 0xf0, 0x4e, 0xa6, 0x3b, 0xd5, 0x32, 0xbb, 0xd0, 0x4e, 0xba, 0xd8, - 0x6f, 0x5f, 0x4f, 0xdc, 0xea, 0xcd, 0xc4, 0xad, 0xfe, 0x9d, 0xb8, 0xd5, 0x9f, 0x53, 0xb7, 0x72, - 0x33, 0x75, 0x2b, 0xbf, 0xa7, 0x6e, 0xe5, 0xbc, 0x19, 0x0a, 0x88, 0x86, 0x3d, 0x8f, 0xc9, 0x2b, - 0x73, 0xa9, 0xcd, 0xec, 0x40, 0x13, 0xd9, 0xe7, 0xfe, 0x68, 0xfe, 0x64, 0xe1, 0xc7, 0x80, 0xab, - 0xde, 0xa6, 0x39, 0xc8, 0xe3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xde, 0x67, 0x86, 0x4a, 0x6b, - 0x05, 0x00, 0x00, + // 529 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6f, 0x12, 0x41, + 0x14, 0xc7, 0xc1, 0xd6, 0xd6, 0x4e, 0xab, 0xb6, 0xd3, 0x9a, 0x6c, 0x48, 0x5c, 0x89, 0x17, 0x49, + 0x2a, 0x8b, 0x69, 0xd5, 0x78, 0x15, 0x92, 0x52, 0x23, 0x89, 0xba, 0x72, 0x6a, 0x4c, 0xc6, 0x61, + 0x98, 0xee, 0x4e, 0xba, 0xdd, 0x21, 0x33, 0x8f, 0x04, 0xf9, 0x14, 0x7e, 0xac, 0x1e, 0x7b, 0xec, + 0xc9, 0x18, 0xf8, 0x22, 0x66, 0x67, 0x07, 0xec, 0x02, 0xd9, 0xe5, 0xf6, 0x78, 0xbc, 0xff, 0xff, + 0xf7, 0xb2, 0xff, 0x79, 0xe8, 0x78, 0xcc, 0x81, 0xb2, 0x90, 0x8a, 0xb8, 0x61, 0x2a, 0xa9, 0x78, + 0x83, 0x29, 0xa9, 0x75, 0xda, 0x0b, 0x78, 0xcc, 0xb5, 0xd0, 0xde, 0x40, 0x49, 0x90, 0xf8, 0xf9, + 0x7c, 0xd8, 0x9b, 0x0d, 0x7b, 0xff, 0x87, 0x2b, 0x27, 0xf9, 0x5e, 0xa6, 0x24, 0xa6, 0x26, 0x30, + 0x4a, 0x2d, 0x2b, 0xf5, 0x02, 0x3e, 0xd5, 0x64, 0xa0, 0x04, 0xe3, 0x76, 0xfc, 0x43, 0xfe, 0xb8, + 0x88, 0x7b, 0x72, 0x18, 0xf7, 0x49, 0x48, 0x75, 0x48, 0x40, 0x12, 0xc6, 0xe6, 0xa0, 0xd3, 0xf5, + 0x94, 0xa0, 0x28, 0xbb, 0xe2, 0xca, 0x8a, 0xde, 0xe5, 0x8b, 0x22, 0xaa, 0x81, 0xf4, 0x22, 0xc9, + 0xae, 0x48, 0xc8, 0x45, 0x10, 0x82, 0x95, 0xbd, 0xcd, 0x97, 0xc9, 0x21, 0xac, 0x82, 0xbd, 0xcf, + 0x57, 0x29, 0x0a, 0x9c, 0x44, 0xe2, 0x5a, 0x00, 0x57, 0xe4, 0x32, 0xa2, 0x81, 0x4d, 0xa5, 0x72, + 0x14, 0xc8, 0x40, 0x9a, 0xb2, 0x91, 0x54, 0x69, 0xf7, 0xe5, 0xdd, 0x16, 0xda, 0x6b, 0xa7, 0xe9, + 0x7d, 0x07, 0x0a, 0x1c, 0x5f, 0xa2, 0xc3, 0x19, 0xb8, 0x9b, 0x72, 0x3b, 0x42, 0x83, 0xf3, 0xa0, + 0xba, 0x51, 0xdb, 0x3d, 0xf1, 0xbc, 0xdc, 0x68, 0xbd, 0x2f, 0x59, 0x65, 0x73, 0xf3, 0xe6, 0xcf, + 0x8b, 0x92, 0xbf, 0xca, 0x10, 0x7f, 0x46, 0x7b, 0x01, 0xd5, 0x5f, 0x93, 0xd0, 0x0c, 0xe0, 0xa1, + 0x01, 0xbc, 0x2a, 0x00, 0xb4, 0xad, 0xc4, 0xcf, 0x88, 0xf1, 0x37, 0xf4, 0xb8, 0x95, 0x0c, 0xb5, + 0x92, 0xa1, 0xee, 0x48, 0x3b, 0xdb, 0xc6, 0xed, 0xb8, 0xc0, 0xed, 0xbe, 0xc6, 0xcf, 0x3a, 0xe0, + 0x9f, 0xe8, 0x30, 0xc9, 0xad, 0x99, 0xc4, 0x76, 0x6e, 0x52, 0x33, 0x6b, 0x3e, 0x5a, 0xeb, 0x3b, + 0x74, 0xb2, 0x4a, 0x7f, 0x95, 0x15, 0x8e, 0xd0, 0x33, 0xfb, 0x9c, 0xce, 0xa9, 0x0e, 0xbb, 0xb2, + 0xc5, 0x60, 0x64, 0x18, 0x3b, 0x86, 0xf1, 0xa6, 0x80, 0xf1, 0x69, 0x51, 0x6b, 0xbf, 0xf6, 0x6a, + 0x53, 0xcc, 0xd1, 0xd1, 0xc2, 0xe3, 0x25, 0x51, 0x02, 0xdb, 0x35, 0xb0, 0xfa, 0x7a, 0xb0, 0x6c, + 0xae, 0x58, 0xc4, 0x4b, 0xb1, 0xfe, 0x40, 0x4f, 0x13, 0x3d, 0xa1, 0x8c, 0xc9, 0x61, 0x0c, 0x22, + 0x0e, 0x9c, 0xbd, 0x6a, 0x79, 0x0d, 0xc2, 0x05, 0x07, 0xfa, 0x71, 0x2e, 0xb2, 0x84, 0x27, 0xe3, + 0x4c, 0x17, 0xbf, 0x46, 0x07, 0x67, 0x22, 0xa6, 0x91, 0x18, 0xf3, 0xbe, 0x5d, 0x49, 0x3b, 0xfb, + 0xd5, 0x8d, 0xda, 0x8e, 0xbf, 0xfc, 0x07, 0x66, 0x08, 0x2f, 0x5f, 0x83, 0x73, 0x60, 0xd6, 0x69, + 0x14, 0xac, 0xe3, 0x53, 0xe0, 0x9d, 0x54, 0x77, 0x96, 0xc8, 0xec, 0x42, 0xfb, 0x6a, 0xa1, 0x8f, + 0x1d, 0xb4, 0x6d, 0xd6, 0xe3, 0xca, 0xc1, 0xd5, 0x72, 0x6d, 0xd3, 0x9f, 0xfd, 0x6c, 0xb6, 0x6f, + 0x26, 0x6e, 0xf9, 0x76, 0xe2, 0x96, 0xff, 0x4e, 0xdc, 0xf2, 0xef, 0xa9, 0x5b, 0xba, 0x9d, 0xba, + 0xa5, 0xbb, 0xa9, 0x5b, 0xba, 0xa8, 0x07, 0x02, 0xc2, 0x61, 0xcf, 0x63, 0xf2, 0xda, 0xdc, 0x70, + 0x3d, 0x3d, 0xdd, 0x58, 0xf6, 0x79, 0x63, 0x74, 0xff, 0x98, 0xe1, 0xd7, 0x80, 0xeb, 0xde, 0x96, + 0x39, 0xd5, 0xd3, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5a, 0x17, 0x12, 0x52, 0x85, 0x05, 0x00, + 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -197,6 +206,13 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Counter != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.Counter)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } { size, err := m.RateLimiterFlags.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -380,6 +396,9 @@ func (m *GenesisState) Size() (n int) { } l = m.RateLimiterFlags.Size() n += 2 + l + sovGenesis(uint64(l)) + if m.Counter != 0 { + n += 2 + sovGenesis(uint64(m.Counter)) + } return n } @@ -720,6 +739,25 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Counter", wireType) + } + m.Counter = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Counter |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/x/crosschain/types/inbound_parsing_test.go b/x/crosschain/types/inbound_parsing_test.go index 0d030fdd3d..13ad954598 100644 --- a/x/crosschain/types/inbound_parsing_test.go +++ b/x/crosschain/types/inbound_parsing_test.go @@ -33,7 +33,7 @@ func TestNewWithdrawalInbound(t *testing.T) { // _, err := types.NewWithdrawalInbound( // ctx, // sample.EthAddress().Hex(), - // fc.CoinType, + // fc.FungibleTokenCoinType, // fc.Asset, // nil, // chains.GoerliLocalnet, @@ -73,7 +73,7 @@ func TestNewWithdrawAndCallInbound(t *testing.T) { // _, err := types.NewWithdrawAndCallInbound( // ctx, // sample.EthAddress().Hex(), - // fc.CoinType, + // fc.FungibleTokenCoinType, // fc.Asset, // nil, // chains.GoerliLocalnet, diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index 26422c6101..3d75d3853c 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -43,6 +43,12 @@ const ( // NOTE: Send is the previous name of CCTX and is kept for backward compatibility CCTXKey = "Send-value-" + // CounterValueKey is a static key for storing the cctx counter key for ordering + CounterValueKey = "ctr-value" + + // CounterIndexKey is the prefix to use for the counter index + CounterIndexKey = "ctr-idx-" + LastBlockHeightKey = "LastBlockHeight-value-" FinalizedInboundsKey = "FinalizedInbounds-value-" diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index 42423f9381..f911313fdc 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1391,6 +1391,9 @@ func (m *QueryGetCctxResponse) GetCrossChainTx() *CrossChainTx { type QueryAllCctxRequest struct { Pagination *query.PageRequest `protobuf:"bytes,1,opt,name=pagination,proto3" json:"pagination,omitempty"` + // Bulk dump all CCTX without ordering. + // This is useful for initializing a CCTX indexer. + Unordered bool `protobuf:"varint,2,opt,name=unordered,proto3" json:"unordered,omitempty"` } func (m *QueryAllCctxRequest) Reset() { *m = QueryAllCctxRequest{} } @@ -1433,6 +1436,13 @@ func (m *QueryAllCctxRequest) GetPagination() *query.PageRequest { return nil } +func (m *QueryAllCctxRequest) GetUnordered() bool { + if m != nil { + return m.Unordered + } + return false +} + type QueryAllCctxResponse struct { CrossChainTx []*CrossChainTx `protobuf:"bytes,1,rep,name=CrossChainTx,proto3" json:"CrossChainTx,omitempty"` Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` @@ -2366,156 +2376,157 @@ func init() { } var fileDescriptor_d00cb546ea76908b = []byte{ - // 2372 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xdb, 0x6f, 0xdc, 0xc6, - 0xf5, 0xf6, 0x68, 0x2d, 0x5f, 0x46, 0xb6, 0x64, 0x8d, 0x65, 0x4b, 0xd9, 0xd8, 0xb2, 0x43, 0xc7, - 0x96, 0x22, 0xff, 0xb4, 0x6b, 0x4b, 0x96, 0x7c, 0x4d, 0x6c, 0x5d, 0x6c, 0x59, 0x3f, 0xc8, 0xb6, - 0xb2, 0x10, 0xea, 0xc2, 0xbd, 0x10, 0x14, 0x77, 0xc2, 0x65, 0x43, 0x91, 0x9b, 0xe5, 0xac, 0xb5, - 0x8e, 0x20, 0xa0, 0x0d, 0xd0, 0x87, 0xbe, 0x15, 0x08, 0x8a, 0xbe, 0xf4, 0xb5, 0x68, 0x81, 0xf6, - 0x21, 0x0f, 0x45, 0x5e, 0x8a, 0x16, 0xe8, 0xdd, 0x68, 0x5a, 0xc0, 0x4d, 0x81, 0xa2, 0xe8, 0x43, - 0x91, 0xda, 0x45, 0xf3, 0xde, 0xbf, 0xa0, 0xe0, 0xf0, 0x70, 0x97, 0x77, 0xce, 0xae, 0xd6, 0x80, - 0xf2, 0x24, 0x92, 0x33, 0xe7, 0xcc, 0xf7, 0x9d, 0x33, 0x97, 0x33, 0xdf, 0x0a, 0xbf, 0xf1, 0x3e, - 0x65, 0x8a, 0x5a, 0x51, 0x74, 0xb3, 0xc8, 0x9f, 0xac, 0x1a, 0x2d, 0xaa, 0x35, 0xcb, 0xb6, 0xdd, - 0x6f, 0xef, 0xd5, 0x69, 0xed, 0x49, 0xa1, 0x5a, 0xb3, 0x98, 0x45, 0x4e, 0x36, 0xbb, 0x16, 0xbc, - 0xae, 0x85, 0x56, 0xd7, 0xfc, 0x84, 0x6a, 0xd9, 0x1b, 0x96, 0x5d, 0x5c, 0x57, 0x6c, 0xea, 0xda, - 0x15, 0x1f, 0x5f, 0x5c, 0xa7, 0x4c, 0xb9, 0x58, 0xac, 0x2a, 0x9a, 0x6e, 0x2a, 0x4c, 0xb7, 0x4c, - 0xd7, 0x55, 0x7e, 0x2a, 0x7d, 0x54, 0xfe, 0x28, 0xf3, 0x67, 0x99, 0x35, 0xc0, 0x66, 0x32, 0xdd, - 0x46, 0x53, 0x6c, 0xb9, 0x5a, 0xd3, 0x55, 0x0a, 0xdd, 0xaf, 0xa4, 0x77, 0xd7, 0xcd, 0x75, 0xab, - 0x6e, 0x96, 0xe5, 0x8a, 0x62, 0x57, 0x64, 0x66, 0xc9, 0xaa, 0xda, 0x1c, 0x68, 0x5a, 0xcc, 0x92, - 0xd5, 0x14, 0xf5, 0x5d, 0x5a, 0x03, 0xa3, 0x99, 0x74, 0x23, 0x43, 0xb1, 0x99, 0xbc, 0x6e, 0x58, - 0xea, 0xbb, 0x72, 0x85, 0xea, 0x5a, 0x85, 0x81, 0xd9, 0xa5, 0x74, 0x33, 0xab, 0xce, 0xe2, 0x06, - 0x9b, 0x4d, 0xb7, 0xaa, 0x29, 0x8c, 0xca, 0x86, 0xbe, 0xa1, 0x33, 0x5a, 0x93, 0xdf, 0x31, 0x14, - 0xcd, 0x06, 0xbb, 0x21, 0xcd, 0xd2, 0x2c, 0xfe, 0x58, 0x74, 0x9e, 0xe0, 0xeb, 0x09, 0xcd, 0xb2, - 0x34, 0x83, 0x16, 0x95, 0xaa, 0x5e, 0x54, 0x4c, 0xd3, 0x62, 0x3c, 0x53, 0x9e, 0xcd, 0x30, 0xa4, - 0x75, 0xc3, 0xd6, 0x8a, 0x8f, 0x2f, 0x3a, 0x7f, 0xdc, 0x06, 0xe9, 0x04, 0xce, 0xbf, 0xed, 0x64, - 0xf9, 0x11, 0x65, 0xca, 0x9c, 0xaa, 0x5a, 0x75, 0x93, 0xe9, 0xa6, 0x56, 0xa2, 0xef, 0xd5, 0xa9, - 0xcd, 0xa4, 0x7b, 0xf8, 0xd5, 0xd8, 0x56, 0xbb, 0x6a, 0x99, 0x36, 0x25, 0x05, 0x7c, 0x54, 0x59, - 0xb7, 0x6a, 0x8c, 0x96, 0x65, 0x87, 0x81, 0xac, 0x6c, 0x38, 0x3d, 0x46, 0xd0, 0x69, 0x34, 0x7e, - 0xb0, 0x34, 0x08, 0x4d, 0xdc, 0x96, 0x37, 0x48, 0xab, 0x78, 0x94, 0xbb, 0x5b, 0xa2, 0xec, 0x01, - 0xc4, 0x64, 0xcd, 0x0d, 0x09, 0x0c, 0x48, 0x46, 0xf0, 0x7e, 0xce, 0x7e, 0x79, 0x91, 0x7b, 0xc9, - 0x95, 0xbc, 0x57, 0x32, 0x84, 0x7b, 0x4d, 0xcb, 0x54, 0xe9, 0x48, 0xcf, 0x69, 0x34, 0xbe, 0xb7, - 0xe4, 0xbe, 0x48, 0xdf, 0x42, 0xf8, 0x54, 0xa2, 0x4b, 0x40, 0xf9, 0x75, 0x3c, 0x60, 0x05, 0x9b, - 0xb8, 0xef, 0xbe, 0xa9, 0x42, 0x21, 0x75, 0x2d, 0x14, 0x42, 0x0e, 0xe7, 0xf7, 0x3e, 0xfd, 0xe7, - 0xa9, 0x3d, 0xa5, 0xb0, 0x33, 0xa9, 0x02, 0xac, 0xe6, 0x0c, 0x23, 0x81, 0xd5, 0x1d, 0x8c, 0x5b, - 0x8b, 0x07, 0x06, 0x3f, 0x57, 0x70, 0x53, 0x52, 0x70, 0x56, 0x5a, 0xc1, 0x5d, 0xa1, 0xb0, 0xd2, - 0x0a, 0xab, 0x8a, 0x46, 0xc1, 0xb6, 0xe4, 0xb3, 0x94, 0xfe, 0xe8, 0xb1, 0x8d, 0x1b, 0x2a, 0x8d, - 0x6d, 0xae, 0x6b, 0x6c, 0xc9, 0x52, 0x80, 0x4b, 0x0f, 0xe7, 0x32, 0x96, 0xc9, 0xc5, 0x05, 0x17, - 0x20, 0xf3, 0x6d, 0x84, 0xcf, 0x26, 0x90, 0x99, 0x7f, 0xb2, 0xe0, 0x40, 0xf2, 0xc2, 0x37, 0x84, - 0x7b, 0x39, 0x44, 0x98, 0x12, 0xee, 0x4b, 0x28, 0xa8, 0x3d, 0x1d, 0x07, 0xf5, 0x2f, 0x08, 0x9f, - 0xcb, 0xc2, 0xf1, 0x45, 0x8b, 0xed, 0x77, 0x10, 0x7e, 0xdd, 0xe3, 0xb4, 0x6c, 0xa6, 0x84, 0xf6, - 0x15, 0x7c, 0xc0, 0xdd, 0xa0, 0xf5, 0x72, 0x70, 0xc1, 0x95, 0xbb, 0x16, 0xdf, 0x3f, 0xfb, 0xf2, - 0x9c, 0x80, 0x05, 0xc2, 0xfb, 0x15, 0xdc, 0xaf, 0x9b, 0x31, 0xd1, 0x9d, 0xcc, 0x88, 0x6e, 0xc8, - 0xab, 0x1b, 0xdc, 0x90, 0xab, 0xee, 0xc5, 0xd6, 0xb7, 0xdc, 0x83, 0x03, 0xdb, 0xdd, 0x5e, 0xee, - 0x7f, 0xf0, 0x2d, 0xf7, 0xc8, 0x50, 0x5f, 0xa8, 0x98, 0x2d, 0xe2, 0xd3, 0xde, 0x2e, 0x0d, 0x03, - 0xdf, 0x55, 0xec, 0xca, 0x9a, 0xb5, 0xa0, 0xb2, 0x86, 0x17, 0xb5, 0xd3, 0xb8, 0x4f, 0x6f, 0xb5, - 0xc1, 0x21, 0xe2, 0xff, 0xe4, 0xcc, 0xea, 0xd7, 0x52, 0xdc, 0x40, 0x44, 0xca, 0x78, 0x50, 0x0f, - 0x37, 0x42, 0x12, 0x2e, 0x88, 0x05, 0xa5, 0x65, 0x07, 0x71, 0x89, 0x3a, 0x94, 0x6e, 0x03, 0x94, - 0x88, 0xc9, 0xa2, 0xc2, 0x14, 0x71, 0x4a, 0xdb, 0x58, 0x4a, 0x73, 0x03, 0x94, 0x1e, 0xe2, 0xc3, - 0x0b, 0x0e, 0x4a, 0xbe, 0x5c, 0xd6, 0x1a, 0x36, 0xe4, 0xf8, 0x7c, 0x06, 0x1d, 0xbf, 0x0d, 0x30, - 0x09, 0xfa, 0x91, 0xbe, 0x01, 0x79, 0x69, 0x4d, 0xb0, 0x68, 0x5e, 0xba, 0x35, 0x9b, 0x3f, 0xf5, - 0xb2, 0x17, 0x3f, 0x58, 0x7a, 0xf6, 0x72, 0x5d, 0xcd, 0x5e, 0xf7, 0x26, 0x76, 0x11, 0x0f, 0x7b, - 0x33, 0x72, 0x49, 0xb1, 0x57, 0x9d, 0xca, 0xd5, 0x77, 0x6a, 0xe9, 0x66, 0x99, 0x36, 0x20, 0xed, - 0xee, 0x8b, 0x24, 0xe3, 0x91, 0xa8, 0x01, 0x70, 0x5f, 0xc0, 0x07, 0xbc, 0x6f, 0x10, 0xe7, 0xb1, - 0x0c, 0xca, 0x4d, 0x17, 0x4d, 0x43, 0x49, 0x01, 0x44, 0x73, 0x86, 0x11, 0x46, 0xd4, 0xad, 0x4c, - 0xfe, 0x18, 0x01, 0x89, 0xc0, 0x18, 0xb1, 0x24, 0x72, 0x1d, 0x91, 0xe8, 0x5e, 0x7e, 0x66, 0x5b, - 0x15, 0xe7, 0x8a, 0x62, 0xb3, 0x79, 0xa7, 0x76, 0xbf, 0xcb, 0x4b, 0xf7, 0xf4, 0x34, 0x6d, 0xb5, - 0xca, 0xca, 0x88, 0x1d, 0x10, 0xfd, 0x32, 0x1e, 0x08, 0x35, 0x09, 0x96, 0x95, 0x61, 0x87, 0x61, - 0x37, 0xfe, 0x13, 0x26, 0x01, 0x74, 0xb7, 0x32, 0xf9, 0x5b, 0xdf, 0x09, 0xd3, 0x16, 0xcf, 0x5c, - 0x17, 0x78, 0x76, 0x2f, 0xcb, 0xe7, 0xf1, 0x51, 0x2f, 0x5b, 0xfe, 0x9d, 0x2b, 0x3e, 0xb5, 0x2b, - 0x70, 0xe3, 0x81, 0xce, 0xf3, 0x4f, 0xee, 0x3b, 0x37, 0x89, 0x4e, 0x2f, 0x20, 0x1a, 0x1e, 0x0a, - 0x0e, 0x0d, 0x51, 0x7b, 0x80, 0x0f, 0xf9, 0xb7, 0x5a, 0xc8, 0x51, 0x3b, 0x3b, 0x76, 0x29, 0xe0, - 0x40, 0xfa, 0x1a, 0x70, 0x9c, 0x33, 0x8c, 0x97, 0xb1, 0x3b, 0x7f, 0x84, 0x80, 0x48, 0xd3, 0x7f, - 0x22, 0x91, 0xdc, 0x8e, 0x88, 0x74, 0x2f, 0xeb, 0xf7, 0xe1, 0x72, 0xba, 0xa2, 0xdb, 0x6c, 0x95, - 0x9a, 0x65, 0xdd, 0xd4, 0xfc, 0x91, 0x49, 0x29, 0x6d, 0x87, 0x70, 0x2f, 0xbf, 0x58, 0xf3, 0xd1, - 0x0f, 0x97, 0xdc, 0x17, 0xe9, 0x43, 0x84, 0x4f, 0xc4, 0x3b, 0x7c, 0x59, 0xa1, 0x90, 0xf0, 0x21, - 0x66, 0x31, 0xc5, 0x80, 0xc1, 0x60, 0x66, 0x05, 0xbe, 0x49, 0x2b, 0x00, 0xaa, 0xa4, 0x30, 0xba, - 0xe2, 0xaa, 0x01, 0xcb, 0x66, 0xb5, 0xee, 0xdf, 0xbf, 0x5c, 0x2e, 0xc8, 0xc7, 0x85, 0x1c, 0xc7, - 0xfb, 0x36, 0x75, 0xb3, 0x6c, 0x6d, 0x72, 0x9f, 0xb9, 0x12, 0xbc, 0x49, 0xdf, 0xcb, 0xe1, 0x93, - 0x09, 0xee, 0x80, 0xe4, 0x71, 0xbc, 0xaf, 0xd2, 0xda, 0xcd, 0x72, 0x25, 0x78, 0x23, 0xf7, 0xf1, - 0x21, 0x55, 0x65, 0x0d, 0x5b, 0xde, 0xd0, 0x6d, 0x9b, 0x96, 0x47, 0x7a, 0xda, 0x27, 0xdf, 0xc7, - 0x1d, 0xdc, 0xe3, 0xf6, 0x64, 0x15, 0x1f, 0x76, 0xfd, 0x55, 0x81, 0x7c, 0xae, 0x83, 0x68, 0x72, - 0x0f, 0x10, 0x29, 0x72, 0x06, 0x1f, 0xe6, 0x91, 0x6b, 0x7a, 0xdc, 0x1b, 0x0d, 0x27, 0x19, 0xc7, - 0x47, 0xaa, 0x8a, 0xcd, 0x64, 0x77, 0xec, 0xc7, 0x8a, 0x51, 0xa7, 0x23, 0xbd, 0x7c, 0x7b, 0xe8, - 0x77, 0xbe, 0x3b, 0xf9, 0xb6, 0xbf, 0xe4, 0x7c, 0x25, 0x05, 0x7c, 0x14, 0x1c, 0x05, 0x3a, 0xef, - 0x73, 0xc5, 0x8d, 0x6a, 0x6b, 0x7e, 0x40, 0xff, 0xeb, 0x38, 0x6f, 0x58, 0x9b, 0xd4, 0x66, 0xb2, - 0xdf, 0x0c, 0x84, 0xa2, 0x91, 0xfd, 0x3c, 0x98, 0xc3, 0x6e, 0x0f, 0xdf, 0xe4, 0x82, 0x2d, 0x7f, - 0x1e, 0x4f, 0xc4, 0x4d, 0xbd, 0x87, 0x3a, 0xab, 0xe8, 0x66, 0x33, 0x57, 0xa9, 0x39, 0x97, 0x7e, - 0xd5, 0x83, 0xcf, 0x0b, 0x39, 0x81, 0x4c, 0xbf, 0x8d, 0xfb, 0x83, 0x12, 0x5d, 0x47, 0x13, 0x5a, - 0xf5, 0x4f, 0xe8, 0x48, 0x0a, 0x62, 0x66, 0x34, 0x99, 0xc5, 0xc3, 0x6a, 0xbd, 0x56, 0xa3, 0x26, - 0x93, 0x37, 0x75, 0x56, 0x29, 0xd7, 0x94, 0x4d, 0x19, 0x26, 0x6b, 0x8e, 0x47, 0xe9, 0x18, 0x34, - 0x3f, 0x84, 0xd6, 0x87, 0xbc, 0x91, 0x4c, 0xe1, 0x63, 0x11, 0xbb, 0x9a, 0xc2, 0x28, 0xcf, 0xf3, - 0xc1, 0xd2, 0xd1, 0x90, 0x95, 0x43, 0xd8, 0x49, 0x62, 0x4b, 0x47, 0x93, 0x69, 0x43, 0xa5, 0xb4, - 0x4c, 0xcb, 0x3c, 0xe3, 0x07, 0x4a, 0x83, 0x35, 0x2f, 0x26, 0xb7, 0xa1, 0xa1, 0x29, 0x87, 0x39, - 0x47, 0xd5, 0x23, 0xca, 0x94, 0xc0, 0xb1, 0x2b, 0xcd, 0x78, 0x3b, 0x4e, 0xa8, 0xb5, 0xb5, 0x74, - 0xee, 0x06, 0x96, 0x0e, 0x24, 0x77, 0x0d, 0x96, 0xf0, 0x82, 0x65, 0x3e, 0xa6, 0x35, 0xa7, 0xee, - 0x5b, 0xb3, 0x1c, 0xf3, 0xc8, 0x99, 0x13, 0xd9, 0xa8, 0xf2, 0xf8, 0x80, 0xa6, 0xd8, 0x2b, 0xcd, - 0xbd, 0xea, 0x60, 0xa9, 0xf9, 0x2e, 0xfd, 0x10, 0xc1, 0x52, 0x8e, 0xba, 0x05, 0x3c, 0xff, 0x87, - 0x07, 0x3d, 0x85, 0x61, 0x49, 0xb1, 0x97, 0x4d, 0xa7, 0xd1, 0x13, 0xe7, 0x22, 0x0d, 0x4e, 0x6f, - 0x2e, 0x09, 0xaa, 0x96, 0x71, 0x87, 0x52, 0xe8, 0xdd, 0x03, 0xb3, 0x3d, 0xdc, 0x40, 0xc6, 0xf1, - 0x80, 0xf3, 0xd7, 0x5f, 0x15, 0xe4, 0x78, 0xae, 0xc3, 0x9f, 0xa5, 0x31, 0xb8, 0xfe, 0xdf, 0xa3, - 0xb6, 0xad, 0x68, 0x74, 0x55, 0xb1, 0x6d, 0xdd, 0xd4, 0x56, 0x5b, 0x1e, 0xbd, 0xe8, 0xde, 0x01, - 0x1d, 0x26, 0xa5, 0x23, 0x10, 0x3b, 0x81, 0x0f, 0xbe, 0xd3, 0x84, 0xe8, 0x12, 0x6a, 0x7d, 0x90, - 0x46, 0xa3, 0x3b, 0xe6, 0x1d, 0x43, 0xd1, 0xbc, 0xeb, 0xb9, 0xf4, 0x01, 0x8a, 0xee, 0x81, 0xd0, - 0x01, 0xfc, 0x2b, 0xf8, 0x48, 0x2d, 0xd4, 0x06, 0x47, 0x6b, 0x31, 0x63, 0x6d, 0x84, 0x5d, 0xc2, - 0x15, 0x24, 0xe2, 0x4e, 0x5a, 0x85, 0x89, 0x16, 0xbc, 0x87, 0x0b, 0x9c, 0x5d, 0xc3, 0x78, 0xbf, - 0xb3, 0xab, 0x38, 0xf7, 0x49, 0x37, 0x39, 0xfb, 0x58, 0x83, 0x5f, 0x25, 0xb7, 0x60, 0x72, 0x86, - 0x3d, 0x02, 0xa7, 0xaf, 0xe2, 0x81, 0x90, 0xe6, 0x0d, 0x94, 0xba, 0xa1, 0x14, 0x4c, 0xfd, 0x64, - 0x0a, 0xf7, 0xf2, 0xd1, 0xc9, 0xa7, 0x08, 0x0f, 0x84, 0xe4, 0x2e, 0xf2, 0x66, 0xc6, 0x10, 0xe9, - 0xa2, 0x70, 0xfe, 0xad, 0x4e, 0xcd, 0x5d, 0xea, 0xd2, 0xad, 0x0f, 0xfe, 0xfa, 0xef, 0x0f, 0x7b, - 0xae, 0x91, 0x2b, 0x5c, 0x67, 0x9f, 0xf4, 0xfd, 0x3a, 0x11, 0xd4, 0xe7, 0xc1, 0xae, 0xb8, 0x05, - 0x25, 0xdf, 0x76, 0x71, 0x8b, 0x17, 0x79, 0xdb, 0xe4, 0x37, 0x08, 0x93, 0x90, 0xf7, 0x39, 0xc3, - 0x10, 0xe3, 0x95, 0x28, 0x0b, 0x8b, 0xf1, 0x4a, 0x96, 0x7a, 0xa5, 0x02, 0xe7, 0x35, 0x4e, 0xce, - 0x89, 0xf1, 0x22, 0x9f, 0x23, 0xfc, 0x4a, 0x94, 0x05, 0xa8, 0x70, 0x64, 0xb1, 0x33, 0x34, 0x41, - 0x41, 0x31, 0x7f, 0x7b, 0x87, 0x5e, 0x80, 0xda, 0x9b, 0x9c, 0xda, 0x65, 0x32, 0x23, 0x46, 0x0d, - 0xcc, 0x21, 0x73, 0xdb, 0xe4, 0x3f, 0x08, 0x8f, 0x04, 0xe7, 0xad, 0x8f, 0xe8, 0x82, 0x20, 0xc4, - 0x34, 0xe1, 0x34, 0xbf, 0xb8, 0x33, 0x27, 0x40, 0xf3, 0x26, 0xa7, 0x79, 0x95, 0x5c, 0x4e, 0xa0, - 0xa9, 0x9b, 0xc9, 0x2c, 0x65, 0xbd, 0xbc, 0x4d, 0x7e, 0x8d, 0xf0, 0x60, 0x84, 0xa8, 0xf0, 0xbc, - 0x8c, 0xd7, 0x2f, 0x85, 0xe7, 0x65, 0x82, 0x26, 0x99, 0x39, 0x2f, 0x83, 0xac, 0x6c, 0xf2, 0x09, - 0xc2, 0xfd, 0x41, 0x5f, 0xe4, 0xaa, 0x08, 0x84, 0xd8, 0xbd, 0x33, 0x7f, 0xad, 0x13, 0x53, 0x40, - 0x3e, 0xcf, 0x91, 0xdf, 0x20, 0xd7, 0x84, 0x90, 0xfb, 0x12, 0x51, 0xdc, 0x82, 0x4d, 0x79, 0x9b, - 0xfc, 0xad, 0x95, 0x12, 0x9f, 0xe2, 0x74, 0x53, 0x70, 0x0f, 0x4b, 0x92, 0xe1, 0xf2, 0xb7, 0x3a, - 0x77, 0x00, 0xe4, 0xde, 0xe2, 0xe4, 0xae, 0x90, 0xd9, 0x74, 0x72, 0x2d, 0xcb, 0xe2, 0x96, 0xef, - 0xd3, 0x36, 0xf9, 0x0c, 0xe1, 0x63, 0xb1, 0x3a, 0x25, 0xb9, 0xd5, 0x46, 0xc8, 0x63, 0x95, 0xd2, - 0xfc, 0xdc, 0x0e, 0x3c, 0xb4, 0x97, 0xbb, 0xa0, 0x75, 0x88, 0xe2, 0x27, 0x08, 0x0f, 0x45, 0x46, - 0x71, 0x56, 0xd4, 0xcd, 0xf6, 0x96, 0x44, 0x87, 0xe9, 0x4b, 0x53, 0x46, 0xa5, 0x0b, 0x9c, 0xdf, - 0x04, 0x19, 0x17, 0xe5, 0x47, 0x7e, 0x8a, 0x5a, 0x5a, 0x1c, 0x99, 0x15, 0x9c, 0x3f, 0x21, 0xd1, - 0x30, 0x7f, 0xb9, 0x6d, 0x3b, 0xc0, 0x5b, 0xe4, 0x78, 0xdf, 0x20, 0x63, 0x09, 0x78, 0x35, 0x30, - 0x70, 0x52, 0x50, 0xa6, 0x8d, 0x6d, 0xf2, 0x23, 0x84, 0xfb, 0x3c, 0x2f, 0x4e, 0xcc, 0x67, 0x05, - 0x43, 0xd6, 0x11, 0xe2, 0x18, 0xe9, 0x52, 0x1a, 0xe3, 0x88, 0x5f, 0x23, 0xa7, 0x32, 0x10, 0x93, - 0x5f, 0x22, 0x7c, 0x24, 0x5c, 0x75, 0x93, 0xeb, 0x22, 0xc3, 0x26, 0x5c, 0x01, 0xf2, 0x37, 0x3a, - 0x33, 0x16, 0x0c, 0xb5, 0x1a, 0xc6, 0xfa, 0x7b, 0x84, 0xfb, 0x7c, 0x85, 0xb5, 0xd8, 0xd9, 0x9f, - 0x55, 0xc0, 0x8b, 0x9d, 0xfd, 0x99, 0xd5, 0xbd, 0x34, 0xc1, 0xd9, 0xbc, 0x4e, 0xa4, 0x04, 0x36, - 0xbe, 0xcb, 0x08, 0x79, 0x8a, 0x22, 0xea, 0xa4, 0x70, 0xb5, 0x19, 0xaf, 0xad, 0x0a, 0x57, 0x9b, - 0x09, 0x7a, 0xa9, 0x34, 0xcb, 0xe1, 0x5f, 0x20, 0x85, 0x04, 0xf8, 0x46, 0xd0, 0xae, 0x39, 0xfd, - 0x9d, 0x1a, 0x33, 0xe4, 0xb3, 0x9d, 0xb3, 0x7c, 0x27, 0x6c, 0x92, 0xd5, 0xdf, 0xcc, 0xb3, 0x3c, - 0xc4, 0x86, 0xfc, 0x00, 0xe1, 0xbd, 0x7c, 0xf3, 0x99, 0x12, 0x0c, 0xa3, 0x7f, 0x93, 0x9c, 0x6e, - 0xcb, 0x06, 0x10, 0x9e, 0xe7, 0x08, 0xcf, 0x92, 0x33, 0x49, 0x93, 0x1f, 0x4e, 0x32, 0x1e, 0xe4, - 0x9f, 0x21, 0xdc, 0xe7, 0x53, 0x7d, 0xc5, 0xea, 0x8c, 0x58, 0xa5, 0xb8, 0x33, 0xb0, 0x33, 0x1c, - 0x6c, 0x91, 0x4c, 0xa6, 0x82, 0x8d, 0xdc, 0x3f, 0xbe, 0x8f, 0xf0, 0x7e, 0xef, 0x28, 0x9a, 0x12, - 0xcc, 0x68, 0xdb, 0x81, 0x0d, 0x29, 0xbf, 0xd2, 0x19, 0x8e, 0xf5, 0x24, 0x79, 0x35, 0x05, 0x2b, - 0xf9, 0xd8, 0x59, 0x80, 0x41, 0xbd, 0x89, 0x08, 0x55, 0x60, 0xf1, 0xaa, 0x6d, 0xfe, 0x7a, 0x47, - 0xb6, 0xa2, 0x3b, 0x87, 0x0f, 0xe4, 0x7f, 0x11, 0x1e, 0x4d, 0x17, 0xca, 0xc8, 0x72, 0x07, 0x58, - 0xe2, 0x15, 0xbb, 0xfc, 0xff, 0x77, 0xc3, 0x15, 0xb0, 0xbc, 0xca, 0x59, 0x4e, 0x93, 0x8b, 0xd9, - 0x2c, 0xc3, 0x8c, 0x3e, 0x46, 0xb8, 0x3f, 0xf8, 0xbf, 0x5c, 0x62, 0x2b, 0x20, 0xf6, 0xbf, 0xc3, - 0xc4, 0x2a, 0xed, 0xf8, 0x7f, 0x1d, 0x93, 0x26, 0x39, 0x89, 0x31, 0x72, 0x36, 0x81, 0xc4, 0xfb, - 0x41, 0x94, 0x0e, 0xf0, 0xa0, 0xea, 0x26, 0x06, 0x3c, 0x56, 0xc7, 0x13, 0x03, 0x1e, 0x2f, 0xf2, - 0x65, 0x02, 0x37, 0x82, 0x28, 0x9d, 0x52, 0x21, 0x2c, 0x0a, 0x89, 0x95, 0x0a, 0x09, 0xf2, 0x95, - 0x58, 0xa9, 0x90, 0x24, 0x6d, 0x65, 0x96, 0x0a, 0x61, 0xa1, 0x2a, 0x4c, 0x80, 0xff, 0x58, 0xd0, - 0x36, 0x01, 0xff, 0x2f, 0x16, 0x6d, 0x13, 0x08, 0xfc, 0x3e, 0xd1, 0x0e, 0x01, 0x17, 0xeb, 0x9f, - 0x10, 0x3e, 0xf4, 0xa0, 0xce, 0xd6, 0x1a, 0xbb, 0x44, 0x8d, 0x12, 0x90, 0x36, 0x9a, 0x58, 0x63, - 0x8e, 0x82, 0x5f, 0xb8, 0xfa, 0x5a, 0xb3, 0xcb, 0x2e, 0xd0, 0xa1, 0xb2, 0x4e, 0x60, 0x3f, 0x23, - 0xf2, 0x2f, 0x84, 0x8f, 0x87, 0xf0, 0xef, 0x4a, 0x05, 0xea, 0x1a, 0x27, 0x75, 0x89, 0x4c, 0x09, - 0x90, 0x0a, 0xcb, 0x4f, 0xee, 0x4d, 0x39, 0x8e, 0xe2, 0x2e, 0xd2, 0x9e, 0x6e, 0x70, 0x82, 0xb3, - 0xe4, 0x52, 0xe2, 0x7d, 0x32, 0x81, 0x1f, 0x17, 0x9e, 0x7e, 0xce, 0x35, 0x9b, 0x8e, 0x66, 0xe1, - 0x4b, 0x52, 0x9d, 0xb2, 0x0e, 0x7f, 0x1f, 0x1f, 0xf2, 0x0c, 0xd0, 0xef, 0x2e, 0x81, 0xe6, 0x3a, - 0x67, 0x30, 0x43, 0xa6, 0x53, 0x18, 0x24, 0xaa, 0x33, 0xff, 0x40, 0x98, 0x04, 0x29, 0xed, 0x1e, - 0x69, 0x26, 0x5b, 0xe6, 0x0c, 0xe3, 0x0e, 0x91, 0xfb, 0x1d, 0xd7, 0xd4, 0xfc, 0x9d, 0x76, 0x89, - 0x28, 0x93, 0x55, 0x0d, 0x04, 0x99, 0xe5, 0x7b, 0xbf, 0xf9, 0xf9, 0x47, 0x13, 0x68, 0x7e, 0xe9, - 0xe9, 0xf3, 0x51, 0xf4, 0xec, 0xf9, 0x28, 0xfa, 0xec, 0xf9, 0x28, 0xfa, 0xee, 0x8b, 0xd1, 0x3d, - 0xcf, 0x5e, 0x8c, 0xee, 0xf9, 0xfb, 0x8b, 0xd1, 0x3d, 0x8f, 0x26, 0x35, 0x9d, 0x55, 0xea, 0xeb, - 0x05, 0xd5, 0xda, 0xf0, 0x7b, 0x34, 0xad, 0x32, 0x2d, 0x36, 0xfc, 0x8e, 0xd9, 0x93, 0x2a, 0xb5, - 0xd7, 0xf7, 0xf1, 0xbb, 0xf0, 0xf4, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x9b, 0xdf, 0x71, 0x32, - 0x00, 0x32, 0x00, 0x00, + // 2394 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xcd, 0x6f, 0xdc, 0xc6, + 0x15, 0xf7, 0x68, 0x2d, 0x59, 0x7a, 0xb2, 0x25, 0x6b, 0x2c, 0x5b, 0xca, 0x46, 0x96, 0x1d, 0x3a, + 0xb6, 0x14, 0xb9, 0xda, 0xb5, 0x25, 0x4b, 0xfe, 0x4a, 0x62, 0xeb, 0xc3, 0x96, 0x55, 0xc8, 0xb6, + 0xb2, 0x10, 0xea, 0xc2, 0x2d, 0x4a, 0x50, 0xdc, 0xc9, 0x2e, 0x1b, 0x8a, 0xdc, 0x90, 0x5c, 0x6b, + 0x1d, 0x41, 0x40, 0x1b, 0xa0, 0x40, 0x7a, 0x2b, 0x10, 0x14, 0xbd, 0xf4, 0x5a, 0xb4, 0x87, 0x1e, + 0x72, 0x28, 0x72, 0x0c, 0xda, 0xa6, 0x68, 0x8d, 0x06, 0x45, 0xd3, 0x04, 0x28, 0x7a, 0x69, 0x11, + 0xd8, 0x41, 0x73, 0xef, 0x5f, 0x50, 0x70, 0xf8, 0xb8, 0xcb, 0xef, 0xe5, 0xae, 0xd6, 0x80, 0x7c, + 0x12, 0xc9, 0x99, 0xf7, 0xe6, 0xfd, 0xde, 0x7b, 0xf3, 0xe6, 0xcd, 0x6f, 0x05, 0xaf, 0xbd, 0xc7, + 0x2c, 0x49, 0x2e, 0x4b, 0x8a, 0x96, 0xe7, 0x4f, 0xba, 0xc1, 0xf2, 0xb2, 0xa1, 0x9b, 0xa6, 0xf3, + 0xed, 0xdd, 0x2a, 0x33, 0x1e, 0xe7, 0x2a, 0x86, 0x6e, 0xe9, 0xf4, 0x64, 0x7d, 0x6a, 0xce, 0x9d, + 0x9a, 0x6b, 0x4c, 0xcd, 0x4e, 0xc9, 0xba, 0xb9, 0xa5, 0x9b, 0xf9, 0x4d, 0xc9, 0x64, 0x8e, 0x5c, + 0xfe, 0xd1, 0xc5, 0x4d, 0x66, 0x49, 0x17, 0xf3, 0x15, 0xa9, 0xa4, 0x68, 0x92, 0xa5, 0xe8, 0x9a, + 0xa3, 0x2a, 0x3b, 0x93, 0xbc, 0x2a, 0x7f, 0x14, 0xf9, 0xb3, 0x68, 0xd5, 0x50, 0x66, 0x3a, 0x59, + 0xa6, 0x24, 0x99, 0x62, 0xc5, 0x50, 0x64, 0x86, 0xd3, 0xaf, 0x24, 0x4f, 0x57, 0xb4, 0x4d, 0xbd, + 0xaa, 0x15, 0xc5, 0xb2, 0x64, 0x96, 0x45, 0x4b, 0x17, 0x65, 0xb9, 0xbe, 0xd0, 0x6c, 0x3a, 0x49, + 0xcb, 0x90, 0xe4, 0x77, 0x98, 0x81, 0x42, 0x73, 0xc9, 0x42, 0xaa, 0x64, 0x5a, 0xe2, 0xa6, 0xaa, + 0xcb, 0xef, 0x88, 0x65, 0xa6, 0x94, 0xca, 0x16, 0x8a, 0x5d, 0x4a, 0x16, 0xd3, 0xab, 0x56, 0xd4, + 0x62, 0xf3, 0xc9, 0x52, 0x86, 0x64, 0x31, 0x51, 0x55, 0xb6, 0x14, 0x8b, 0x19, 0xe2, 0xdb, 0xaa, + 0x54, 0x32, 0x51, 0x6e, 0xb8, 0xa4, 0x97, 0x74, 0xfe, 0x98, 0xb7, 0x9f, 0xf0, 0xeb, 0x58, 0x49, + 0xd7, 0x4b, 0x2a, 0xcb, 0x4b, 0x15, 0x25, 0x2f, 0x69, 0x9a, 0x6e, 0xf1, 0x48, 0xb9, 0x32, 0x23, + 0x18, 0xd6, 0x2d, 0xb3, 0x94, 0x7f, 0x74, 0xd1, 0xfe, 0xe3, 0x0c, 0x08, 0x63, 0x90, 0x7d, 0xcb, + 0x8e, 0xf2, 0x43, 0x66, 0x49, 0x0b, 0xb2, 0xac, 0x57, 0x35, 0x4b, 0xd1, 0x4a, 0x05, 0xf6, 0x6e, + 0x95, 0x99, 0x96, 0x70, 0x17, 0x5e, 0x8e, 0x1c, 0x35, 0x2b, 0xba, 0x66, 0x32, 0x9a, 0x83, 0x63, + 0xd2, 0xa6, 0x6e, 0x58, 0xac, 0x28, 0xda, 0x08, 0x44, 0x69, 0xcb, 0x9e, 0x31, 0x4a, 0x4e, 0x93, + 0xc9, 0xbe, 0xc2, 0x10, 0x0e, 0x71, 0x59, 0x3e, 0x20, 0xac, 0xc3, 0x38, 0x57, 0xb7, 0xc2, 0xac, + 0xfb, 0xe8, 0x93, 0x0d, 0xc7, 0x25, 0xb8, 0x20, 0x1d, 0x85, 0x43, 0x1c, 0xfd, 0xea, 0x32, 0xd7, + 0x92, 0x29, 0xb8, 0xaf, 0x74, 0x18, 0xba, 0x35, 0x5d, 0x93, 0xd9, 0x68, 0xd7, 0x69, 0x32, 0x79, + 0xb0, 0xe0, 0xbc, 0x08, 0x3f, 0x26, 0x70, 0x2a, 0x56, 0x25, 0x5a, 0xf9, 0x03, 0x18, 0xd4, 0xfd, + 0x43, 0x5c, 0x77, 0xff, 0x4c, 0x2e, 0x97, 0xb8, 0x17, 0x72, 0x01, 0x85, 0x8b, 0x07, 0x9f, 0xfc, + 0xe7, 0xd4, 0x81, 0x42, 0x50, 0x99, 0x50, 0x46, 0x54, 0x0b, 0xaa, 0x1a, 0x83, 0xea, 0x36, 0x40, + 0x63, 0xf3, 0xe0, 0xe2, 0xe7, 0x72, 0x4e, 0x48, 0x72, 0xf6, 0x4e, 0xcb, 0x39, 0x3b, 0x14, 0x77, + 0x5a, 0x6e, 0x5d, 0x2a, 0x31, 0x94, 0x2d, 0x78, 0x24, 0x85, 0xbf, 0xba, 0x68, 0xa3, 0x96, 0x4a, + 0x42, 0x9b, 0xe9, 0x18, 0x5a, 0xba, 0xe2, 0xc3, 0xd2, 0xc5, 0xb1, 0x4c, 0x34, 0xc5, 0xe2, 0x18, + 0xe7, 0x03, 0xf3, 0x13, 0x02, 0x67, 0x63, 0xc0, 0x2c, 0x3e, 0x5e, 0xb2, 0x4d, 0x72, 0xdd, 0x37, + 0x0c, 0xdd, 0xdc, 0x44, 0x4c, 0x09, 0xe7, 0x25, 0xe0, 0xd4, 0xae, 0xb6, 0x9d, 0xfa, 0x0f, 0x02, + 0xe7, 0x9a, 0xd9, 0xf1, 0xa2, 0xf9, 0xf6, 0xa7, 0x04, 0x5e, 0x75, 0x31, 0xad, 0x6a, 0x09, 0xae, + 0x7d, 0x09, 0x7a, 0x9d, 0x02, 0xad, 0x14, 0xfd, 0x1b, 0xae, 0xd8, 0x31, 0xff, 0xfe, 0xcd, 0x13, + 0xe7, 0x18, 0x5b, 0xd0, 0xbd, 0xdf, 0x83, 0x01, 0x45, 0x8b, 0xf0, 0xee, 0x74, 0x13, 0xef, 0x06, + 0xb4, 0x3a, 0xce, 0x0d, 0xa8, 0xea, 0x9c, 0x6f, 0x3d, 0xdb, 0xdd, 0xbf, 0xb0, 0xd9, 0xe9, 0xed, + 0xfe, 0x17, 0xcf, 0x76, 0x0f, 0x2d, 0xf5, 0x42, 0xf9, 0x6c, 0x19, 0x4e, 0xbb, 0x55, 0x1a, 0x17, + 0xbe, 0x23, 0x99, 0xe5, 0x0d, 0x7d, 0x49, 0xb6, 0x6a, 0xae, 0xd7, 0x4e, 0x43, 0xbf, 0xd2, 0x18, + 0xc3, 0x43, 0xc4, 0xfb, 0xc9, 0xce, 0xea, 0x57, 0x12, 0xd4, 0xa0, 0x47, 0x8a, 0x30, 0xa4, 0x04, + 0x07, 0x31, 0x08, 0x17, 0xd2, 0x39, 0xa5, 0x21, 0x87, 0x7e, 0x09, 0x2b, 0x14, 0x6e, 0xa1, 0x29, + 0x21, 0x91, 0x65, 0xc9, 0x92, 0xd2, 0x43, 0xda, 0x05, 0x21, 0x49, 0x0d, 0x42, 0x7a, 0x00, 0x47, + 0x96, 0x6c, 0x2b, 0xf9, 0x76, 0xd9, 0xa8, 0x99, 0x18, 0xe3, 0xf3, 0x4d, 0xe0, 0x78, 0x65, 0x10, + 0x89, 0x5f, 0x8f, 0xf0, 0x43, 0x8c, 0x4b, 0x23, 0xc1, 0xc2, 0x71, 0xe9, 0x54, 0x36, 0x7f, 0xe1, + 0x46, 0x2f, 0x7a, 0xb1, 0xe4, 0xe8, 0x65, 0x3a, 0x1a, 0xbd, 0xce, 0x25, 0x76, 0x1e, 0x46, 0xdc, + 0x8c, 0x5c, 0x91, 0xcc, 0x75, 0xbb, 0x73, 0xf5, 0x9c, 0x5a, 0x8a, 0x56, 0x64, 0x35, 0x0c, 0xbb, + 0xf3, 0x22, 0x88, 0x30, 0x1a, 0x16, 0x40, 0xec, 0x4b, 0xd0, 0xeb, 0x7e, 0x43, 0x3f, 0x4f, 0x34, + 0x81, 0x5c, 0x57, 0x51, 0x17, 0x14, 0x24, 0xb4, 0x68, 0x41, 0x55, 0x83, 0x16, 0x75, 0x2a, 0x92, + 0xbf, 0x21, 0x08, 0xc2, 0xb7, 0x46, 0x24, 0x88, 0x4c, 0x5b, 0x20, 0x3a, 0x17, 0x9f, 0xf9, 0x46, + 0xc7, 0xb9, 0x26, 0x99, 0xd6, 0xa2, 0xdd, 0xbb, 0xdf, 0xe1, 0xad, 0x7b, 0x72, 0x98, 0x76, 0x1a, + 0x6d, 0x65, 0x48, 0x0e, 0x81, 0x7e, 0x17, 0x06, 0x03, 0x43, 0x29, 0xdb, 0xca, 0xa0, 0xc2, 0xa0, + 0x1a, 0xef, 0x09, 0x13, 0x63, 0x74, 0xa7, 0x22, 0xf9, 0x27, 0xcf, 0x09, 0xd3, 0x12, 0xce, 0x4c, + 0x07, 0x70, 0x76, 0x2e, 0xca, 0xe7, 0xe1, 0x98, 0x1b, 0x2d, 0x6f, 0xe5, 0x8a, 0x0e, 0xed, 0x1a, + 0xde, 0x78, 0x70, 0xf2, 0xe2, 0xe3, 0x7b, 0xf6, 0x4d, 0xa2, 0xdd, 0x0b, 0x48, 0x09, 0x86, 0xfd, + 0x4b, 0xa3, 0xd7, 0xee, 0xc3, 0x61, 0x6f, 0xa9, 0xc5, 0x18, 0xb5, 0x52, 0xb1, 0x0b, 0x3e, 0x05, + 0xc2, 0x0e, 0x62, 0x5c, 0x50, 0xd5, 0xe7, 0x50, 0x9d, 0xe9, 0x18, 0xf4, 0x55, 0x35, 0xdd, 0x28, + 0x32, 0x83, 0x15, 0x39, 0xc2, 0xde, 0x42, 0xe3, 0x83, 0xf0, 0x11, 0x41, 0x98, 0xf5, 0xd5, 0x63, + 0x61, 0x66, 0xf6, 0x04, 0xb3, 0x73, 0x39, 0x71, 0x0f, 0xaf, 0xae, 0x6b, 0x8a, 0x69, 0xad, 0x33, + 0xad, 0xa8, 0x68, 0x25, 0xaf, 0xdf, 0x12, 0x1a, 0xdf, 0x61, 0xe8, 0xe6, 0xd7, 0x6e, 0xbe, 0xfa, + 0x91, 0x82, 0xf3, 0x22, 0x7c, 0x48, 0x60, 0x2c, 0x5a, 0xe1, 0xf3, 0x72, 0x85, 0x00, 0x87, 0x2d, + 0xdd, 0x92, 0x54, 0x5c, 0x0c, 0xf3, 0xce, 0xf7, 0x4d, 0x58, 0x43, 0xa3, 0x0a, 0x92, 0xc5, 0xd6, + 0x1c, 0xae, 0x60, 0x55, 0xab, 0x54, 0xbd, 0xd5, 0xcd, 0xc1, 0x42, 0x3c, 0x58, 0xe8, 0x09, 0xe8, + 0xd9, 0x56, 0xb4, 0xa2, 0xbe, 0xcd, 0x75, 0x66, 0x0a, 0xf8, 0x26, 0xfc, 0x3c, 0x03, 0x27, 0x63, + 0xd4, 0x21, 0xc8, 0x13, 0xd0, 0x53, 0x6e, 0xd4, 0xba, 0x4c, 0x01, 0xdf, 0xe8, 0x3d, 0x38, 0x2c, + 0xcb, 0x56, 0xcd, 0x14, 0xb7, 0x14, 0xd3, 0xe4, 0x19, 0xd4, 0x32, 0xf8, 0x7e, 0xae, 0xe0, 0x2e, + 0x97, 0xa7, 0xeb, 0x70, 0xc4, 0xd1, 0x57, 0x41, 0xf0, 0x99, 0x36, 0xbc, 0xc9, 0x35, 0xa0, 0xa7, + 0xe8, 0x19, 0x38, 0xc2, 0x3d, 0x57, 0xd7, 0x78, 0x30, 0xec, 0x4e, 0x3a, 0x09, 0x47, 0x2b, 0x92, + 0x69, 0x89, 0xce, 0xda, 0x8f, 0x24, 0xb5, 0xca, 0x46, 0xbb, 0x79, 0xf1, 0x18, 0xb0, 0xbf, 0xdb, + 0xf1, 0x36, 0xbf, 0x63, 0x7f, 0xa5, 0x39, 0x38, 0x86, 0x8a, 0x7c, 0x93, 0x7b, 0x1c, 0xea, 0xa3, + 0xd2, 0xc8, 0x0f, 0x9c, 0x7f, 0x1d, 0xb2, 0xaa, 0xbe, 0xcd, 0x4c, 0x4b, 0xf4, 0x8a, 0x21, 0x8d, + 0x34, 0x7a, 0x88, 0x3b, 0x73, 0xc4, 0x99, 0xe1, 0x49, 0x2e, 0x3c, 0x10, 0x16, 0x61, 0x2a, 0x2a, + 0xf5, 0x1e, 0x28, 0x56, 0x59, 0xd1, 0xea, 0xb1, 0x4a, 0x8c, 0xb9, 0xf0, 0x87, 0x2e, 0x38, 0x9f, + 0x4a, 0x09, 0x46, 0xfa, 0x2d, 0x18, 0xf0, 0x13, 0x78, 0x6d, 0x25, 0xb4, 0xec, 0x4d, 0xe8, 0x50, + 0x08, 0x22, 0x32, 0x9a, 0xce, 0xc3, 0x88, 0x5c, 0x35, 0x0c, 0xa6, 0x59, 0xe2, 0xb6, 0x62, 0x95, + 0x8b, 0x86, 0xb4, 0x2d, 0x62, 0xb2, 0x66, 0xb8, 0x97, 0x8e, 0xe3, 0xf0, 0x03, 0x1c, 0x7d, 0xc0, + 0x07, 0xe9, 0x0c, 0x1c, 0x0f, 0xc9, 0x19, 0x92, 0xc5, 0x78, 0x9c, 0xfb, 0x0a, 0xc7, 0x02, 0x52, + 0x36, 0x60, 0x3b, 0x88, 0x0d, 0x96, 0x4d, 0x64, 0x35, 0x99, 0xb1, 0x22, 0x2b, 0xf2, 0x88, 0xf7, + 0x16, 0x86, 0x0c, 0xd7, 0x27, 0xb7, 0x70, 0xa0, 0x4e, 0x96, 0xd9, 0x07, 0xd9, 0x43, 0x66, 0x49, + 0xbe, 0x43, 0x59, 0x98, 0x73, 0x2b, 0x4e, 0x60, 0xb4, 0xb1, 0x75, 0xee, 0xf8, 0xb6, 0x0e, 0x06, + 0x77, 0x03, 0xb7, 0xf0, 0x92, 0xae, 0x3d, 0x62, 0x86, 0xdd, 0x15, 0x6e, 0xe8, 0xb6, 0x78, 0xe8, + 0x44, 0x0a, 0x15, 0xaa, 0x2c, 0xf4, 0x96, 0x24, 0x73, 0xad, 0x5e, 0xab, 0xfa, 0x0a, 0xf5, 0x77, + 0xe1, 0x57, 0x04, 0xb7, 0x72, 0x58, 0x2d, 0xda, 0xf3, 0x2d, 0x18, 0x72, 0xf9, 0x87, 0x15, 0xc9, + 0x5c, 0xd5, 0xec, 0x41, 0x97, 0xba, 0x0b, 0x0d, 0xd8, 0xb3, 0x39, 0x61, 0x28, 0xeb, 0xea, 0x6d, + 0xc6, 0x70, 0x76, 0x17, 0x66, 0x7b, 0x70, 0x80, 0x4e, 0xc2, 0xa0, 0xfd, 0xd7, 0xdb, 0x33, 0x64, + 0x78, 0xac, 0x83, 0x9f, 0x85, 0x09, 0x24, 0x07, 0xee, 0x32, 0xd3, 0x94, 0x4a, 0x6c, 0x5d, 0x32, + 0x4d, 0x45, 0x2b, 0xad, 0x37, 0x34, 0xba, 0xde, 0xbd, 0x8d, 0x2c, 0x4d, 0xc2, 0x44, 0x04, 0x36, + 0x06, 0x7d, 0x6f, 0xd7, 0x4d, 0x74, 0x00, 0x35, 0x3e, 0x08, 0xe3, 0xe1, 0x8a, 0x79, 0x5b, 0x95, + 0x4a, 0xee, 0xe5, 0x5d, 0x78, 0x9f, 0x84, 0x6b, 0x20, 0x4e, 0x40, 0xfd, 0x12, 0x1c, 0x35, 0x02, + 0x63, 0x78, 0xf0, 0xe6, 0x9b, 0xec, 0x8d, 0xa0, 0x4a, 0xbc, 0xa0, 0x84, 0xd4, 0x09, 0xeb, 0x98, + 0x68, 0xfe, 0x5b, 0x7a, 0x8a, 0xb3, 0x6b, 0x04, 0x0e, 0xd9, 0x55, 0xc5, 0xbe, 0x6d, 0x3a, 0xc1, + 0xe9, 0xb1, 0x6a, 0xfc, 0xa2, 0xb9, 0x83, 0xc9, 0x19, 0xd4, 0x88, 0x98, 0xbe, 0x0f, 0x83, 0x01, + 0x46, 0x1c, 0x21, 0x75, 0x82, 0x47, 0x98, 0xf9, 0x74, 0x06, 0xba, 0xf9, 0xea, 0xf4, 0x0b, 0x02, + 0x83, 0x01, 0x32, 0x8c, 0xbe, 0xd1, 0x64, 0x89, 0x64, 0xca, 0x38, 0xfb, 0x66, 0xbb, 0xe2, 0x0e, + 0x74, 0xe1, 0xe6, 0xfb, 0x5f, 0x7e, 0xfd, 0x61, 0xd7, 0x35, 0x7a, 0x85, 0xb3, 0xf0, 0xd3, 0x9e, + 0xdf, 0x2e, 0xfc, 0xec, 0x3d, 0xca, 0xe5, 0x77, 0xb0, 0x21, 0xdc, 0xcd, 0xef, 0xf0, 0x16, 0x70, + 0x97, 0x7e, 0x4a, 0x80, 0x06, 0xb4, 0x2f, 0xa8, 0x6a, 0x3a, 0x5c, 0xb1, 0xa4, 0x71, 0x3a, 0x5c, + 0xf1, 0x44, 0xb0, 0x90, 0xe3, 0xb8, 0x26, 0xe9, 0xb9, 0x74, 0xb8, 0xe8, 0x37, 0x04, 0x5e, 0x0a, + 0xa3, 0x40, 0x8e, 0x8e, 0x2e, 0xb7, 0x67, 0x8d, 0x9f, 0x6e, 0xcc, 0xde, 0xda, 0xa3, 0x16, 0x84, + 0xf6, 0x06, 0x87, 0x76, 0x99, 0xce, 0xa5, 0x83, 0x86, 0xe2, 0x18, 0xb9, 0x5d, 0xfa, 0x5f, 0x02, + 0xa3, 0xfe, 0xbc, 0xf5, 0x00, 0x5d, 0x4a, 0x69, 0x62, 0x12, 0xad, 0x9a, 0x5d, 0xde, 0x9b, 0x12, + 0x84, 0x79, 0x83, 0xc3, 0xbc, 0x4a, 0x2f, 0xc7, 0xc0, 0x54, 0xb4, 0x78, 0x94, 0xa2, 0x52, 0xdc, + 0xa5, 0x7f, 0x24, 0x30, 0x14, 0x02, 0x9a, 0x3a, 0x2f, 0xa3, 0xd9, 0xcd, 0xd4, 0x79, 0x19, 0xc3, + 0x58, 0x36, 0xcd, 0x4b, 0x3f, 0x2a, 0x93, 0x7e, 0x46, 0x60, 0xc0, 0xaf, 0x8b, 0x5e, 0x4d, 0x63, + 0x42, 0x64, 0xed, 0xcc, 0x5e, 0x6b, 0x47, 0x14, 0x2d, 0x5f, 0xe4, 0x96, 0xbf, 0x4e, 0xaf, 0xa5, + 0xb2, 0xdc, 0x13, 0x88, 0xfc, 0x0e, 0x16, 0xe5, 0x5d, 0xfa, 0xcf, 0x46, 0x48, 0x3c, 0x7c, 0xd4, + 0x8d, 0x94, 0x35, 0x2c, 0x8e, 0xa4, 0xcb, 0xde, 0x6c, 0x5f, 0x01, 0x82, 0x7b, 0x93, 0x83, 0xbb, + 0x42, 0xe7, 0x93, 0xc1, 0x35, 0x24, 0xf3, 0x3b, 0x9e, 0x4f, 0xbb, 0xf4, 0x2b, 0x02, 0xc7, 0x23, + 0x59, 0x4c, 0x7a, 0xb3, 0x05, 0x97, 0x47, 0xf2, 0xa8, 0xd9, 0x85, 0x3d, 0x68, 0x68, 0x2d, 0x76, + 0x7e, 0xe9, 0x00, 0xc4, 0xcf, 0x08, 0x0c, 0x87, 0x56, 0xb1, 0x77, 0xd4, 0x8d, 0xd6, 0xb6, 0x44, + 0x9b, 0xe1, 0x4b, 0xe2, 0x4d, 0x85, 0x0b, 0x1c, 0xdf, 0x14, 0x9d, 0x4c, 0x8b, 0x8f, 0xfe, 0x96, + 0x34, 0x98, 0x3a, 0x3a, 0x9f, 0x32, 0x7f, 0x02, 0x94, 0x62, 0xf6, 0x72, 0xcb, 0x72, 0x68, 0x6f, + 0x9e, 0xdb, 0xfb, 0x1a, 0x9d, 0x88, 0xb1, 0xb7, 0x84, 0x02, 0x76, 0x08, 0x8a, 0xac, 0xb6, 0x4b, + 0x7f, 0x4d, 0xa0, 0xdf, 0xd5, 0x62, 0xfb, 0x7c, 0x3e, 0xa5, 0xcb, 0xda, 0xb2, 0x38, 0x82, 0xd8, + 0x14, 0x26, 0xb8, 0xc5, 0xaf, 0xd0, 0x53, 0x4d, 0x2c, 0xa6, 0x9f, 0x10, 0x38, 0x1a, 0xec, 0xba, + 0xe9, 0xf5, 0x34, 0xcb, 0xc6, 0x5c, 0x01, 0xb2, 0xaf, 0xb7, 0x27, 0x9c, 0xd2, 0xd5, 0x72, 0xd0, + 0xd6, 0x3f, 0x13, 0xe8, 0xf7, 0x34, 0xd6, 0xe9, 0xce, 0xfe, 0x66, 0x0d, 0x7c, 0xba, 0xb3, 0xbf, + 0x69, 0x77, 0x2f, 0x4c, 0x71, 0x34, 0xaf, 0x52, 0x21, 0x06, 0x8d, 0xe7, 0x32, 0x42, 0x9f, 0x90, + 0x10, 0x77, 0x99, 0xba, 0xdb, 0x8c, 0x66, 0x5e, 0x53, 0x77, 0x9b, 0x31, 0x6c, 0xaa, 0x30, 0xcf, + 0xcd, 0xbf, 0x40, 0x73, 0x31, 0xe6, 0xab, 0x7e, 0xb9, 0x7a, 0xfa, 0xdb, 0x3d, 0x66, 0x40, 0x67, + 0x2b, 0x67, 0xf9, 0x5e, 0xd0, 0xc4, 0x73, 0xc3, 0x4d, 0xcf, 0xf2, 0x00, 0x1a, 0xfa, 0x4b, 0x02, + 0x07, 0x79, 0xf1, 0x99, 0x49, 0xe9, 0x46, 0x6f, 0x91, 0x9c, 0x6d, 0x49, 0x06, 0x2d, 0x3c, 0xcf, + 0x2d, 0x3c, 0x4b, 0xcf, 0xc4, 0x25, 0x3f, 0x9e, 0x64, 0xdc, 0xc9, 0xbf, 0x23, 0xd0, 0xef, 0xe1, + 0x84, 0xd3, 0xf5, 0x19, 0x91, 0x3c, 0x72, 0x7b, 0xc6, 0xce, 0x71, 0x63, 0xf3, 0x74, 0x3a, 0xd1, + 0xd8, 0xd0, 0xfd, 0xe3, 0x17, 0x04, 0x0e, 0xb9, 0x47, 0xd1, 0x4c, 0xca, 0x88, 0xb6, 0xec, 0xd8, + 0x00, 0xf3, 0x2b, 0x9c, 0xe1, 0xb6, 0x9e, 0xa4, 0x2f, 0x27, 0xd8, 0x4a, 0x3f, 0xb6, 0x37, 0xa0, + 0x9f, 0x6f, 0xa2, 0xa9, 0x3a, 0xb0, 0x68, 0xd6, 0x36, 0x7b, 0xbd, 0x2d, 0xd9, 0xb4, 0x95, 0xc3, + 0x63, 0xe4, 0xff, 0x08, 0x8c, 0x27, 0x13, 0x65, 0x74, 0xb5, 0x0d, 0x5b, 0xa2, 0x19, 0xbb, 0xec, + 0xb7, 0x3b, 0xa1, 0x0a, 0x51, 0x5e, 0xe5, 0x28, 0x67, 0xe9, 0xc5, 0xe6, 0x28, 0x83, 0x88, 0x3e, + 0x26, 0x30, 0xe0, 0xff, 0x4f, 0xaf, 0x74, 0x3b, 0x20, 0xf2, 0x7f, 0xc7, 0xd2, 0x75, 0xda, 0xd1, + 0xff, 0x58, 0x26, 0x4c, 0x73, 0x10, 0x13, 0xf4, 0x6c, 0x0c, 0x88, 0xf7, 0xfc, 0x56, 0xda, 0x86, + 0xfb, 0x59, 0xb7, 0x74, 0x86, 0x47, 0xf2, 0x78, 0xe9, 0x0c, 0x8f, 0x26, 0xf9, 0x9a, 0x1a, 0xae, + 0xfa, 0xad, 0xb4, 0x5b, 0x85, 0x20, 0x29, 0x94, 0xae, 0x55, 0x88, 0xa1, 0xaf, 0xd2, 0xb5, 0x0a, + 0x71, 0xd4, 0x56, 0xd3, 0x56, 0x21, 0x48, 0x54, 0x05, 0x01, 0xf0, 0x1f, 0x0b, 0x5a, 0x06, 0xe0, + 0xfd, 0xc5, 0xa2, 0x65, 0x00, 0xbe, 0xdf, 0x27, 0x5a, 0x01, 0xe0, 0xd8, 0xfa, 0x77, 0x02, 0x87, + 0xef, 0x57, 0xad, 0x8d, 0xda, 0xbe, 0x62, 0xa3, 0x12, 0xa9, 0x8d, 0xba, 0xad, 0xe1, 0xa3, 0xe0, + 0x83, 0x2e, 0x42, 0x7f, 0xef, 0x50, 0x6c, 0xf5, 0x59, 0xfb, 0x86, 0x8a, 0x8a, 0x3f, 0x84, 0xbd, + 0xa0, 0x6c, 0x08, 0x5f, 0x13, 0x38, 0x11, 0x80, 0xb0, 0x8f, 0x79, 0xa8, 0x4b, 0x74, 0x26, 0x05, + 0xae, 0x00, 0x09, 0x65, 0xc3, 0x7c, 0xc6, 0xaf, 0xcc, 0x51, 0x28, 0xf7, 0x1d, 0x09, 0x35, 0x4f, + 0x2f, 0xc5, 0x5e, 0x2c, 0x63, 0x20, 0x8a, 0x4a, 0x91, 0xa3, 0xfc, 0x84, 0xf3, 0x37, 0x6d, 0xa5, + 0xe3, 0x73, 0x62, 0xa0, 0xa6, 0x9b, 0x34, 0x02, 0x1e, 0x48, 0x36, 0x80, 0x2f, 0x11, 0xc0, 0x7e, + 0xe4, 0x6b, 0xe6, 0xe8, 0x6c, 0x02, 0x88, 0x38, 0xb2, 0xc6, 0x46, 0xf5, 0x6f, 0x02, 0xd4, 0x8f, + 0x6a, 0xbf, 0x91, 0x35, 0x49, 0xc4, 0x67, 0xd0, 0xee, 0x30, 0xbe, 0x27, 0x9c, 0x68, 0xf3, 0xce, + 0xdb, 0x57, 0x4c, 0x4d, 0x7c, 0x8b, 0xe0, 0x07, 0xf7, 0x41, 0x17, 0xc9, 0x76, 0xff, 0xe8, 0x9b, + 0x8f, 0xa6, 0xc8, 0xe2, 0xca, 0x93, 0xa7, 0xe3, 0xe4, 0xf3, 0xa7, 0xe3, 0xe4, 0xab, 0xa7, 0xe3, + 0xe4, 0x67, 0xcf, 0xc6, 0x0f, 0x7c, 0xfe, 0x6c, 0xfc, 0xc0, 0xbf, 0x9e, 0x8d, 0x1f, 0x78, 0x38, + 0x5d, 0x52, 0xac, 0x72, 0x75, 0x33, 0x27, 0xeb, 0x5b, 0x5e, 0xa5, 0x9a, 0x5e, 0x64, 0xf9, 0x9a, + 0x57, 0xb7, 0xf5, 0xb8, 0xc2, 0xcc, 0xcd, 0x1e, 0x7e, 0x47, 0x9e, 0xfd, 0x7f, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x7d, 0x8f, 0x8a, 0x3e, 0x36, 0x32, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2813,6 +2824,7 @@ func (c *queryClient) RateLimiterInput(ctx context.Context, in *QueryRateLimiter return out, nil } +// Deprecated: Do not use. func (c *queryClient) OutTxTracker(ctx context.Context, in *QueryGetOutboundTrackerRequest, opts ...grpc.CallOption) (*QueryGetOutboundTrackerResponse, error) { out := new(QueryGetOutboundTrackerResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/OutTxTracker", in, out, opts...) @@ -2822,6 +2834,7 @@ func (c *queryClient) OutTxTracker(ctx context.Context, in *QueryGetOutboundTrac return out, nil } +// Deprecated: Do not use. func (c *queryClient) OutTxTrackerAll(ctx context.Context, in *QueryAllOutboundTrackerRequest, opts ...grpc.CallOption) (*QueryAllOutboundTrackerResponse, error) { out := new(QueryAllOutboundTrackerResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/OutTxTrackerAll", in, out, opts...) @@ -2831,6 +2844,7 @@ func (c *queryClient) OutTxTrackerAll(ctx context.Context, in *QueryAllOutboundT return out, nil } +// Deprecated: Do not use. func (c *queryClient) OutTxTrackerAllByChain(ctx context.Context, in *QueryAllOutboundTrackerByChainRequest, opts ...grpc.CallOption) (*QueryAllOutboundTrackerByChainResponse, error) { out := new(QueryAllOutboundTrackerByChainResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/OutTxTrackerAllByChain", in, out, opts...) @@ -2840,6 +2854,7 @@ func (c *queryClient) OutTxTrackerAllByChain(ctx context.Context, in *QueryAllOu return out, nil } +// Deprecated: Do not use. func (c *queryClient) InTxTrackerAllByChain(ctx context.Context, in *QueryAllInboundTrackerByChainRequest, opts ...grpc.CallOption) (*QueryAllInboundTrackerByChainResponse, error) { out := new(QueryAllInboundTrackerByChainResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/InTxTrackerAllByChain", in, out, opts...) @@ -2849,6 +2864,7 @@ func (c *queryClient) InTxTrackerAllByChain(ctx context.Context, in *QueryAllInb return out, nil } +// Deprecated: Do not use. func (c *queryClient) InTxTrackerAll(ctx context.Context, in *QueryAllInboundTrackersRequest, opts ...grpc.CallOption) (*QueryAllInboundTrackersResponse, error) { out := new(QueryAllInboundTrackersResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/InTxTrackerAll", in, out, opts...) @@ -2858,6 +2874,7 @@ func (c *queryClient) InTxTrackerAll(ctx context.Context, in *QueryAllInboundTra return out, nil } +// Deprecated: Do not use. func (c *queryClient) InTxHashToCctx(ctx context.Context, in *QueryGetInboundHashToCctxRequest, opts ...grpc.CallOption) (*QueryGetInboundHashToCctxResponse, error) { out := new(QueryGetInboundHashToCctxResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/InTxHashToCctx", in, out, opts...) @@ -2867,6 +2884,7 @@ func (c *queryClient) InTxHashToCctx(ctx context.Context, in *QueryGetInboundHas return out, nil } +// Deprecated: Do not use. func (c *queryClient) InTxHashToCctxData(ctx context.Context, in *QueryInboundHashToCctxDataRequest, opts ...grpc.CallOption) (*QueryInboundHashToCctxDataResponse, error) { out := new(QueryInboundHashToCctxDataResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/InTxHashToCctxData", in, out, opts...) @@ -2876,6 +2894,7 @@ func (c *queryClient) InTxHashToCctxData(ctx context.Context, in *QueryInboundHa return out, nil } +// Deprecated: Do not use. func (c *queryClient) InTxHashToCctxAll(ctx context.Context, in *QueryAllInboundHashToCctxRequest, opts ...grpc.CallOption) (*QueryAllInboundHashToCctxResponse, error) { out := new(QueryAllInboundHashToCctxResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/InTxHashToCctxAll", in, out, opts...) @@ -4859,6 +4878,16 @@ func (m *QueryAllCctxRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Unordered { + i-- + if m.Unordered { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if m.Pagination != nil { { size, err := m.Pagination.MarshalToSizedBuffer(dAtA[:i]) @@ -5952,6 +5981,9 @@ func (m *QueryAllCctxRequest) Size() (n int) { l = m.Pagination.Size() n += 1 + l + sovQuery(uint64(l)) } + if m.Unordered { + n += 2 + } return n } @@ -9003,6 +9035,26 @@ func (m *QueryAllCctxRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Unordered", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Unordered = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/x/fungible/keeper/deposits.go b/x/fungible/keeper/deposits.go index 64d050bd79..eaea906976 100644 --- a/x/fungible/keeper/deposits.go +++ b/x/fungible/keeper/deposits.go @@ -260,7 +260,10 @@ func (k Keeper) getAndCheckZRC20( } else { foreignCoin, found = k.GetForeignCoinFromAsset(ctx, asset, chainID) if !found { - return ethcommon.Address{}, types.ForeignCoins{}, crosschaintypes.ErrForeignCoinNotFound + return ethcommon.Address{}, types.ForeignCoins{}, errors.Wrapf( + crosschaintypes.ErrForeignCoinNotFound, + "asset: %s, chainID %d", asset, chainID, + ) } } zrc20Contract = ethcommon.HexToAddress(foreignCoin.Zrc20ContractAddress) diff --git a/x/fungible/keeper/deposits_test.go b/x/fungible/keeper/deposits_test.go index e75bc5aa20..7c9f3e2a15 100644 --- a/x/fungible/keeper/deposits_test.go +++ b/x/fungible/keeper/deposits_test.go @@ -5,7 +5,9 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/mock" "github.com/zeta-chain/node/cmd/zetacored/config" - "github.com/zeta-chain/node/testutil/contracts" + "github.com/zeta-chain/node/e2e/contracts/example" + "github.com/zeta-chain/node/e2e/contracts/reverter" + "github.com/zeta-chain/node/e2e/contracts/testdappv2" crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" "math/big" "testing" @@ -15,7 +17,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" - "github.com/zeta-chain/node/pkg/contracts/testdappv2" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" fungiblekeeper "github.com/zeta-chain/node/x/fungible/keeper" @@ -414,7 +415,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chain, "foobar", "foobar") - example, err := k.DeployContract(ctx, contracts.ExampleMetaData) + example, err := k.DeployContract(ctx, example.ExampleMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, example) @@ -454,7 +455,7 @@ func TestKeeper_ZRC20DepositAndCallContract(t *testing.T) { deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chain, "foobar", "foobar") - reverter, err := k.DeployContract(ctx, contracts.ReverterMetaData) + reverter, err := k.DeployContract(ctx, reverter.ReverterMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, reverter) diff --git a/x/fungible/keeper/evm.go b/x/fungible/keeper/evm.go index d920f336e5..c027e4c5b8 100644 --- a/x/fungible/keeper/evm.go +++ b/x/fungible/keeper/evm.go @@ -40,8 +40,9 @@ import ( var ( BigIntZero = big.NewInt(0) - ZEVMGasLimitDepositAndCall = big.NewInt(1_000_000) - ZEVMGasLimitConnectorCall = big.NewInt(1_000_000) + DefaultGasLimit = big.NewInt(200_000) + ZEVMGasLimitDepositAndCall = big.NewInt(1_500_000) + ZEVMGasLimitConnectorCall = big.NewInt(1_500_000) ) // DeployContract deploys a new contract in the ZEVM @@ -258,7 +259,7 @@ func (k Keeper) DepositZRC20( types.ModuleAddressEVM, contract, BigIntZero, - nil, + DefaultGasLimit, true, false, "deposit", @@ -283,7 +284,7 @@ func (k Keeper) UpdateZRC20ProtocolFlatFee( types.ModuleAddressEVM, zrc20Addr, BigIntZero, - nil, + DefaultGasLimit, true, false, "updateProtocolFlatFee", @@ -307,7 +308,7 @@ func (k Keeper) UpdateZRC20GasLimit( types.ModuleAddressEVM, zrc20Addr, BigIntZero, - nil, + DefaultGasLimit, true, false, "updateGasLimit", diff --git a/x/fungible/keeper/evm_gateway.go b/x/fungible/keeper/evm_gateway.go index e7e21901a2..b689578cc6 100644 --- a/x/fungible/keeper/evm_gateway.go +++ b/x/fungible/keeper/evm_gateway.go @@ -16,7 +16,7 @@ import ( ) // gatewayGasLimit is the gas limit for the gateway functions -var gatewayGasLimit = big.NewInt(1_000_000) +var gatewayGasLimit = big.NewInt(1_500_000) // CallUpdateGatewayAddress calls the updateGatewayAddress function on the ZRC20 contract // function updateGatewayAddress(address addr) diff --git a/x/fungible/keeper/evm_test.go b/x/fungible/keeper/evm_test.go index c9d3d98674..e124ff42a0 100644 --- a/x/fungible/keeper/evm_test.go +++ b/x/fungible/keeper/evm_test.go @@ -3,33 +3,32 @@ package keeper_test import ( "encoding/json" "fmt" - "math/big" "testing" sdkmath "cosmossdk.io/math" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - evmtypes "github.com/zeta-chain/ethermint/x/evm/types" - "github.com/zeta-chain/protocol-contracts/pkg/erc1967proxy.sol" "github.com/zeta-chain/protocol-contracts/pkg/gatewayzevm.sol" "github.com/zeta-chain/protocol-contracts/pkg/systemcontract.sol" "github.com/zeta-chain/protocol-contracts/pkg/wzeta.sol" "github.com/zeta-chain/protocol-contracts/pkg/zrc20.sol" + "github.com/zeta-chain/node/e2e/contracts/dapp" + "github.com/zeta-chain/node/e2e/contracts/dappreverter" + "github.com/zeta-chain/node/e2e/contracts/example" + "github.com/zeta-chain/node/e2e/contracts/reverter" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/pkg/ptr" "github.com/zeta-chain/node/server/config" - "github.com/zeta-chain/node/testutil/contracts" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" fungiblekeeper "github.com/zeta-chain/node/x/fungible/keeper" @@ -202,7 +201,7 @@ func assertExampleBarValue( address common.Address, expected int64, ) { - exampleABI, err := contracts.ExampleMetaData.GetAbi() + exampleABI, err := example.ExampleMetaData.GetAbi() require.NoError(t, err) res, err := k.CallEVM( ctx, @@ -563,7 +562,7 @@ func TestKeeper_DepositZRC20AndCallContract(t *testing.T) { chainID := getValidChainID(t) - example, err := k.DeployContract(ctx, contracts.ExampleMetaData) + example, err := k.DeployContract(ctx, example.ExampleMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, example) @@ -591,9 +590,9 @@ func TestKeeper_DepositZRC20AndCallContract(t *testing.T) { deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "FOOBAR") - example, err := k.DeployContract(ctx, contracts.ExampleMetaData) + exampleContract, err := k.DeployContract(ctx, example.ExampleMetaData) require.NoError(t, err) - assertContractDeployment(t, sdkk.EvmKeeper, ctx, example) + assertContractDeployment(t, sdkk.EvmKeeper, ctx, exampleContract) res, err := k.CallDepositAndCall( ctx, @@ -603,24 +602,24 @@ func TestKeeper_DepositZRC20AndCallContract(t *testing.T) { ChainID: big.NewInt(chainID), }, zrc20, - example, + exampleContract, big.NewInt(42), []byte(""), ) require.NoError(t, err) require.False(t, types.IsContractReverted(res, err)) - balance, err := k.BalanceOfZRC4(ctx, zrc20, example) + balance, err := k.BalanceOfZRC4(ctx, zrc20, exampleContract) require.NoError(t, err) require.Equal(t, int64(42), balance.Int64()) // check onCrossChainCall has been called - exampleABI, err := contracts.ExampleMetaData.GetAbi() + exampleABI, err := example.ExampleMetaData.GetAbi() require.NoError(t, err) res, err = k.CallEVM( ctx, *exampleABI, types.ModuleAddressEVM, - example, + exampleContract, big.NewInt(0), nil, false, @@ -645,7 +644,7 @@ func TestKeeper_DepositZRC20AndCallContract(t *testing.T) { zrc20 := setupGasCoin(t, ctx, k, sdkk.EvmKeeper, chainID, "foobar", "FOOBAR") // Deploy reverter - reverter, err := k.DeployContract(ctx, contracts.ReverterMetaData) + reverter, err := k.DeployContract(ctx, reverter.ReverterMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, reverter) @@ -697,10 +696,10 @@ func TestKeeper_CallEVMWithData(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) // Deploy example - contract, err := k.DeployContract(ctx, contracts.ExampleMetaData) + contract, err := k.DeployContract(ctx, example.ExampleMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, contract) - abi, err := contracts.ExampleMetaData.GetAbi() + abi, err := example.ExampleMetaData.GetAbi() require.NoError(t, err) // doRevert make contract reverted @@ -1486,7 +1485,7 @@ func TestKeeper_CallOnReceiveZevmConnector(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -1510,7 +1509,7 @@ func TestKeeper_CallOnReceiveZevmConnector(t *testing.T) { require.NoError(t, err) - dappAbi, err := contracts.DappMetaData.GetAbi() + dappAbi, err := dapp.DappMetaData.GetAbi() require.NoError(t, err) res, err := k.CallEVM( ctx, @@ -1536,7 +1535,7 @@ func TestKeeper_CallOnReceiveZevmConnector(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -1553,7 +1552,7 @@ func TestKeeper_CallOnReceiveZevmConnector(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappReverterMetaData) + dAppContract, err := k.DeployContract(ctx, dappreverter.DappReverterMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -1572,7 +1571,7 @@ func TestKeeper_CallOnRevertZevmConnector(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) senderAddress := dAppContract @@ -1594,7 +1593,7 @@ func TestKeeper_CallOnRevertZevmConnector(t *testing.T) { ) require.NoError(t, err) - dappAbi, err := contracts.DappMetaData.GetAbi() + dappAbi, err := dapp.DappMetaData.GetAbi() require.NoError(t, err) res, err := k.CallEVM( ctx, @@ -1620,7 +1619,7 @@ func TestKeeper_CallOnRevertZevmConnector(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -1638,7 +1637,7 @@ func TestKeeper_CallOnRevertZevmConnector(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappReverterMetaData) + dAppContract, err := k.DeployContract(ctx, dappreverter.DappReverterMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) diff --git a/x/fungible/keeper/gas_coin_and_pool.go b/x/fungible/keeper/gas_coin_and_pool.go index 25794ea401..701e87e121 100644 --- a/x/fungible/keeper/gas_coin_and_pool.go +++ b/x/fungible/keeper/gas_coin_and_pool.go @@ -111,7 +111,7 @@ func (k Keeper) SetupChainGasCoinAndPool( types.ModuleAddressEVM, systemContractAddress, BigIntZero, - nil, + DefaultGasLimit, true, false, "setGasZetaPool", @@ -146,7 +146,7 @@ func (k Keeper) SetupChainGasCoinAndPool( types.ModuleAddressEVM, zrc20Addr, BigIntZero, - nil, + DefaultGasLimit, true, false, "approve", diff --git a/x/fungible/keeper/gas_price.go b/x/fungible/keeper/gas_price.go index 3d58624ad5..4aaa59dc17 100644 --- a/x/fungible/keeper/gas_price.go +++ b/x/fungible/keeper/gas_price.go @@ -34,7 +34,7 @@ func (k Keeper) SetGasPrice(ctx sdk.Context, chainid *big.Int, gasPrice *big.Int types.ModuleAddressEVM, oracle, BigIntZero, - big.NewInt(50_000), + DefaultGasLimit, true, false, "setGasPrice", @@ -70,7 +70,7 @@ func (k Keeper) SetGasCoin(ctx sdk.Context, chainid *big.Int, address ethcommon. types.ModuleAddressEVM, oracle, BigIntZero, - nil, + DefaultGasLimit, true, false, "setGasCoinZRC20", @@ -106,7 +106,7 @@ func (k Keeper) SetGasZetaPool(ctx sdk.Context, chainid *big.Int, pool ethcommon types.ModuleAddressEVM, oracle, BigIntZero, - nil, + DefaultGasLimit, true, false, "setGasZetaPool", diff --git a/x/fungible/keeper/msg_server_update_system_contract.go b/x/fungible/keeper/msg_server_update_system_contract.go index ea9f988a85..b754451683 100644 --- a/x/fungible/keeper/msg_server_update_system_contract.go +++ b/x/fungible/keeper/msg_server_update_system_contract.go @@ -58,7 +58,7 @@ func (k msgServer) UpdateSystemContract( types.ModuleAddressEVM, zrc20Addr, BigIntZero, - nil, + DefaultGasLimit, true, false, "updateSystemContractAddress", @@ -78,7 +78,7 @@ func (k msgServer) UpdateSystemContract( types.ModuleAddressEVM, newSystemContractAddr, BigIntZero, - nil, + DefaultGasLimit, true, false, "setGasCoinZRC20", @@ -98,7 +98,7 @@ func (k msgServer) UpdateSystemContract( types.ModuleAddressEVM, newSystemContractAddr, BigIntZero, - nil, + DefaultGasLimit, true, false, "setGasZetaPool", diff --git a/x/fungible/keeper/system_contract.go b/x/fungible/keeper/system_contract.go index 99c485f69e..20328c49c3 100644 --- a/x/fungible/keeper/system_contract.go +++ b/x/fungible/keeper/system_contract.go @@ -374,7 +374,7 @@ func (k *Keeper) CallUniswapV2RouterSwapExactTokensForTokens( sender, routerAddress, BigIntZero, - big.NewInt(1000_000), + big.NewInt(1_000_000), true, noEthereumTxEvent, "swapExactTokensForTokens", @@ -732,7 +732,7 @@ func (k *Keeper) CallZRC20Burn( sender, zrc20address, big.NewInt(0), - big.NewInt(100_000), + DefaultGasLimit, true, noEthereumTxEvent, "burn", @@ -764,7 +764,7 @@ func (k *Keeper) CallZRC20Deposit( sender, zrc20address, big.NewInt(0), - big.NewInt(100_000), + DefaultGasLimit, true, false, "deposit", @@ -797,7 +797,7 @@ func (k *Keeper) CallZRC20Approve( owner, zrc20address, BigIntZero, - nil, + DefaultGasLimit, true, noEthereumTxEvent, "approve", diff --git a/x/fungible/keeper/zevm_message_passing_test.go b/x/fungible/keeper/zevm_message_passing_test.go index 9b89adb2cc..a09f9f241f 100644 --- a/x/fungible/keeper/zevm_message_passing_test.go +++ b/x/fungible/keeper/zevm_message_passing_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + "github.com/zeta-chain/node/e2e/contracts/dapp" + "github.com/zeta-chain/node/e2e/contracts/dappreverter" "math/big" "testing" @@ -14,7 +16,6 @@ import ( "github.com/zeta-chain/ethermint/x/evm/statedb" "github.com/zeta-chain/node/cmd/zetacored/config" - "github.com/zeta-chain/node/testutil/contracts" keepertest "github.com/zeta-chain/node/testutil/keeper" "github.com/zeta-chain/node/testutil/sample" "github.com/zeta-chain/node/x/fungible/types" @@ -26,7 +27,7 @@ func TestKeeper_ZEVMDepositAndCallContract(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -48,7 +49,7 @@ func TestKeeper_ZEVMDepositAndCallContract(t *testing.T) { ) require.NoError(t, err) - dappAbi, err := contracts.DappMetaData.GetAbi() + dappAbi, err := dapp.DappMetaData.GetAbi() require.NoError(t, err) res, err := k.CallEVM( ctx, @@ -172,7 +173,7 @@ func TestKeeper_ZEVMRevertAndCallContract(t *testing.T) { _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) deploySystemContracts(t, ctx, k, sdkk.EvmKeeper) - dAppContract, err := k.DeployContract(ctx, contracts.DappMetaData) + dAppContract, err := k.DeployContract(ctx, dapp.DappMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) @@ -196,7 +197,7 @@ func TestKeeper_ZEVMRevertAndCallContract(t *testing.T) { ) require.NoError(t, err) - dappAbi, err := contracts.DappMetaData.GetAbi() + dappAbi, err := dapp.DappMetaData.GetAbi() require.NoError(t, err) res, err := k.CallEVM( ctx, @@ -323,7 +324,7 @@ func TestKeeper_ZEVMRevertAndCallContract(t *testing.T) { k, ctx, sdkk, _ := keepertest.FungibleKeeper(t) _ = k.GetAuthKeeper().GetModuleAccount(ctx, types.ModuleName) - dAppContract, err := k.DeployContract(ctx, contracts.DappReverterMetaData) + dAppContract, err := k.DeployContract(ctx, dappreverter.DappReverterMetaData) require.NoError(t, err) assertContractDeployment(t, sdkk.EvmKeeper, ctx, dAppContract) diff --git a/x/fungible/keeper/zrc20_methods.go b/x/fungible/keeper/zrc20_methods.go index 142ba012a7..21a11bd561 100644 --- a/x/fungible/keeper/zrc20_methods.go +++ b/x/fungible/keeper/zrc20_methods.go @@ -43,7 +43,7 @@ func (k Keeper) ZRC20SetName( fungibletypes.ModuleAddressEVM, zrc20Address, big.NewInt(0), - big.NewInt(1_000_000), + DefaultGasLimit, true, true, setName, @@ -77,7 +77,7 @@ func (k Keeper) ZRC20SetSymbol( fungibletypes.ModuleAddressEVM, zrc20Address, big.NewInt(0), - big.NewInt(1_000_000), + DefaultGasLimit, true, true, setSymbol, @@ -110,7 +110,7 @@ func (k Keeper) ZRC20Name( fungibletypes.ModuleAddressEVM, zrc20Address, big.NewInt(0), - big.NewInt(1_000_000), + nil, false, true, name, @@ -157,7 +157,7 @@ func (k Keeper) ZRC20Symbol( fungibletypes.ModuleAddressEVM, zrc20Address, big.NewInt(0), - big.NewInt(1_000_000), + nil, false, true, symbol, @@ -215,7 +215,7 @@ func (k Keeper) ZRC20Allowance( zrc20Address, big.NewInt(0), nil, - true, + false, true, allowance, args..., @@ -271,7 +271,7 @@ func (k Keeper) ZRC20BalanceOf( zrc20Address, big.NewInt(0), nil, - true, + false, true, balanceOf, owner, @@ -323,7 +323,7 @@ func (k Keeper) ZRC20TotalSupply( zrc20Address, big.NewInt(0), nil, - true, + false, true, totalSupply, ) @@ -379,7 +379,7 @@ func (k Keeper) ZRC20Transfer( from, zrc20Address, big.NewInt(0), - nil, + DefaultGasLimit, true, true, transfer, @@ -439,7 +439,7 @@ func (k Keeper) ZRC20TransferFrom( spender, zrc20Address, big.NewInt(0), - nil, + DefaultGasLimit, true, true, transferFrom, diff --git a/x/lightclient/types/query.pb.go b/x/lightclient/types/query.pb.go index b3e5d137d7..f6ee9688ec 100644 --- a/x/lightclient/types/query.pb.go +++ b/x/lightclient/types/query.pb.go @@ -702,67 +702,67 @@ func init() { } var fileDescriptor_1ff0d7827c501c48 = []byte{ - // 951 bytes of a gzipped FileDescriptorProto + // 960 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x96, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0x33, 0x4d, 0xdd, 0x1f, 0xcf, 0x29, 0x12, 0xd3, 0x56, 0x49, 0x17, 0xea, 0xa4, 0x5b, - 0x42, 0x2b, 0xa3, 0xec, 0xd4, 0xa6, 0x14, 0xe1, 0x4a, 0x40, 0x82, 0x20, 0xad, 0x10, 0x52, 0xba, - 0x15, 0x17, 0x2e, 0xd6, 0x7a, 0x3d, 0xdd, 0x5d, 0x65, 0xb3, 0xb3, 0xdd, 0x99, 0x58, 0x2e, 0x55, - 0x25, 0xc4, 0x5f, 0x80, 0x40, 0x48, 0xfc, 0x19, 0x48, 0x5c, 0x2a, 0x21, 0x2e, 0x1c, 0x50, 0x6f, - 0x54, 0xe2, 0x00, 0x27, 0x84, 0x12, 0x24, 0xfe, 0x0d, 0xb4, 0x33, 0xe3, 0xee, 0x24, 0x59, 0xc7, - 0x1b, 0x37, 0xa7, 0xfd, 0x35, 0xef, 0xbd, 0xcf, 0xf7, 0x3b, 0xcf, 0x6f, 0x0c, 0xcd, 0x2f, 0xa9, - 0xf0, 0xfc, 0xd0, 0x8b, 0x12, 0x22, 0xef, 0x58, 0x46, 0x49, 0x1c, 0x05, 0xa1, 0xf0, 0xe3, 0x88, - 0x26, 0x82, 0x3c, 0xdc, 0xa6, 0xd9, 0x23, 0x27, 0xcd, 0x98, 0x60, 0xb8, 0xf1, 0x62, 0xad, 0x33, - 0x5a, 0xeb, 0x18, 0x6b, 0xad, 0xa6, 0xcf, 0xf8, 0x16, 0xe3, 0xa4, 0xe7, 0x71, 0xaa, 0x02, 0xc9, - 0xa0, 0xd5, 0xa3, 0xc2, 0x6b, 0x91, 0xd4, 0x0b, 0xa2, 0xc4, 0x13, 0x11, 0x4b, 0x54, 0x2e, 0xeb, - 0x42, 0xc0, 0x02, 0x26, 0x6f, 0x49, 0x7e, 0xa7, 0xdf, 0xbe, 0x1e, 0x30, 0x16, 0xc4, 0x94, 0x78, - 0x69, 0x44, 0xbc, 0x24, 0x61, 0x42, 0x86, 0x70, 0xfd, 0xf5, 0xfd, 0x09, 0xac, 0xbd, 0x98, 0xf9, - 0x9b, 0xdd, 0x90, 0x7a, 0x7d, 0x9a, 0x75, 0x07, 0x34, 0x8b, 0x1e, 0x44, 0xbe, 0x59, 0xf3, 0xc6, - 0x84, 0x78, 0xf9, 0xa9, 0xcb, 0x85, 0x27, 0xa8, 0x8e, 0x28, 0x73, 0x27, 0xdd, 0x0c, 0x48, 0x9a, - 0x31, 0xf6, 0x80, 0xeb, 0x8b, 0x5e, 0x3b, 0xaf, 0xd5, 0x6f, 0xf1, 0x80, 0x0c, 0x5a, 0xf9, 0x45, - 0x7d, 0xb0, 0xfb, 0x60, 0xdd, 0xcb, 0xcd, 0x58, 0x8d, 0xe3, 0xb5, 0x9c, 0xf0, 0x8e, 0x04, 0x74, - 0xe9, 0xc3, 0x6d, 0xca, 0x05, 0xfe, 0x04, 0xa0, 0x30, 0x67, 0x01, 0x2d, 0xa1, 0xeb, 0xf5, 0xf6, - 0x9b, 0x8e, 0xca, 0xe5, 0xe4, 0x4e, 0x3a, 0x6a, 0x0b, 0xb4, 0x93, 0xce, 0x86, 0x17, 0x50, 0x1d, - 0xeb, 0x1a, 0x91, 0xf6, 0x2f, 0x08, 0x5e, 0x2b, 0x2d, 0xc3, 0x53, 0x96, 0x70, 0x8a, 0x3f, 0x87, - 0x73, 0xa6, 0x3f, 0x7c, 0x01, 0x2d, 0xcd, 0x5e, 0xaf, 0xb7, 0x9b, 0x4e, 0xc9, 0xa6, 0xa6, 0x9b, - 0x92, 0x3d, 0xd7, 0x66, 0xa4, 0x5a, 0x3b, 0xf9, 0xec, 0xef, 0xc5, 0x19, 0x77, 0xae, 0x57, 0xbc, - 0xe2, 0x78, 0x7d, 0x0f, 0xfe, 0x09, 0x89, 0x7f, 0x6d, 0x22, 0xbe, 0x62, 0xda, 0xc3, 0x7f, 0x5b, - 0xbb, 0xb4, 0x4e, 0x45, 0x89, 0x4b, 0x97, 0x01, 0x34, 0xbd, 0xc7, 0x43, 0xe9, 0xd2, 0x9c, 0x7b, - 0x56, 0x81, 0x78, 0x3c, 0xb4, 0x63, 0xad, 0x7d, 0x7f, 0xb0, 0xd6, 0xfe, 0x19, 0xcc, 0x99, 0xda, - 0xb5, 0xcb, 0x47, 0x90, 0xee, 0xd6, 0x0d, 0xd1, 0xb6, 0x0f, 0x97, 0x46, 0x4e, 0x7f, 0x94, 0x47, - 0xdf, 0xcf, 0x3b, 0xe6, 0xb8, 0xf7, 0xf3, 0x29, 0x2a, 0xda, 0xc6, 0xac, 0xa2, 0x25, 0xdd, 0x83, - 0xba, 0xd1, 0xae, 0x87, 0x6d, 0xa6, 0xd1, 0xe1, 0x4e, 0x91, 0x48, 0x6f, 0x26, 0xf8, 0x2f, 0xde, - 0x1c, 0xdf, 0x56, 0xde, 0xd2, 0xfe, 0xac, 0x53, 0x71, 0xd0, 0x9f, 0x4b, 0x70, 0x46, 0x81, 0x47, - 0x7d, 0xe9, 0xce, 0xac, 0x7b, 0x5a, 0x3e, 0xdf, 0xed, 0xdb, 0x51, 0xd1, 0x02, 0x25, 0x8a, 0x3f, - 0xdd, 0xaf, 0x18, 0x1d, 0x4d, 0xb1, 0xa9, 0x35, 0xff, 0xb5, 0xbc, 0x2a, 0x6b, 0x6d, 0x64, 0x6c, - 0x50, 0x81, 0x0d, 0xcf, 0xc3, 0x69, 0x31, 0x54, 0xdd, 0x97, 0x3b, 0x73, 0xd6, 0x3d, 0x25, 0x86, - 0x79, 0xeb, 0xe1, 0x0e, 0xd4, 0x64, 0xbf, 0x2c, 0xcc, 0x4a, 0xa0, 0x37, 0x26, 0x34, 0xd5, 0x46, - 0x7e, 0x71, 0x55, 0xc8, 0xbe, 0xae, 0x3e, 0x29, 0xf3, 0x16, 0x5d, 0x9d, 0xe3, 0x88, 0x61, 0x37, - 0x4a, 0xfa, 0x74, 0xb8, 0x50, 0x53, 0x38, 0x62, 0x78, 0x37, 0x7f, 0xb4, 0x9b, 0x80, 0x4d, 0x7c, - 0x6d, 0xd1, 0x05, 0xa8, 0x0d, 0xbc, 0x58, 0xc3, 0x9f, 0x71, 0xd5, 0x83, 0x7d, 0x15, 0xae, 0xc8, - 0xb5, 0xaa, 0x7b, 0xef, 0x6f, 0xa7, 0x29, 0xcb, 0x04, 0xed, 0x4b, 0x67, 0xb8, 0x96, 0x6e, 0xff, - 0x80, 0xc0, 0x3e, 0x6c, 0x95, 0xae, 0x90, 0xc1, 0xbc, 0x9e, 0xaf, 0x7c, 0xb4, 0xa2, 0x2b, 0xc5, - 0x8e, 0xe6, 0xc9, 0xcd, 0x49, 0x1b, 0x52, 0x96, 0x5f, 0x37, 0xe3, 0xc5, 0xb0, 0xac, 0xb6, 0x7d, - 0x05, 0x16, 0x0d, 0xb2, 0x8f, 0x13, 0xaf, 0x17, 0xef, 0xa7, 0xff, 0x16, 0xc1, 0xd2, 0xf8, 0x35, - 0x9a, 0x3d, 0x01, 0x5d, 0xa0, 0x4b, 0xd5, 0xf7, 0xe3, 0x23, 0x3f, 0x1f, 0x1e, 0xac, 0xdb, 0xfe, - 0x0d, 0xa0, 0x26, 0xa1, 0xf0, 0x53, 0x04, 0xaf, 0x18, 0xd3, 0x64, 0x35, 0x8e, 0x71, 0x67, 0x52, - 0xb5, 0xf1, 0x47, 0x86, 0x75, 0x7b, 0xaa, 0x58, 0xe5, 0x82, 0xbd, 0xf2, 0xf5, 0x1f, 0xff, 0x7e, - 0x77, 0xe2, 0x1a, 0x5e, 0x96, 0x27, 0xda, 0x8a, 0x3a, 0xdc, 0xc6, 0x9d, 0xa2, 0x1c, 0xff, 0x8a, - 0xa0, 0x6e, 0xa4, 0xa9, 0xc8, 0x5d, 0x3a, 0xc4, 0x2b, 0x72, 0x97, 0xcf, 0x70, 0xbb, 0x23, 0xb9, - 0x6f, 0xe2, 0x76, 0x25, 0x6e, 0xf2, 0xb8, 0xf8, 0x61, 0x3d, 0xc1, 0x3f, 0x21, 0x38, 0x57, 0x0c, - 0x82, 0xdc, 0xfe, 0xf7, 0xaa, 0x5a, 0x78, 0x60, 0x80, 0x59, 0x9d, 0x69, 0x42, 0xb5, 0x88, 0xb7, - 0xa4, 0x88, 0x65, 0x7c, 0x75, 0x9c, 0x08, 0x63, 0xc2, 0xe1, 0x9f, 0x11, 0x40, 0x91, 0xa3, 0x22, - 0x72, 0xd9, 0xcc, 0xb5, 0x3a, 0xd3, 0x84, 0x6a, 0xe4, 0x5b, 0x12, 0xf9, 0x06, 0x76, 0x2a, 0x20, - 0x93, 0xc7, 0xa3, 0xf1, 0xf9, 0x04, 0x7f, 0x8f, 0xa0, 0x26, 0xa7, 0x13, 0x6e, 0x55, 0xaa, 0x6e, - 0x0e, 0x62, 0xab, 0x7d, 0x94, 0x10, 0x0d, 0xba, 0x2c, 0x41, 0x17, 0xf1, 0xe5, 0x71, 0xa0, 0xa9, - 0xa4, 0xf9, 0x13, 0xc1, 0xc5, 0xd2, 0x19, 0x87, 0x57, 0x2b, 0x15, 0x3d, 0x6c, 0x8a, 0x5a, 0x6b, - 0x2f, 0x93, 0x42, 0xeb, 0x78, 0x57, 0xea, 0x68, 0x61, 0x32, 0x4e, 0xc7, 0x98, 0x01, 0x8c, 0x7f, - 0x47, 0x70, 0xbe, 0x64, 0xfe, 0xe1, 0x0f, 0x8e, 0x00, 0x55, 0x36, 0x5d, 0xad, 0x0f, 0xa7, 0x4f, - 0xa0, 0x35, 0xbd, 0x23, 0x35, 0x11, 0xbc, 0x32, 0x41, 0xd3, 0xde, 0xc1, 0x6c, 0xd5, 0xbe, 0xfa, - 0xef, 0xc7, 0x26, 0x5a, 0xbb, 0xf3, 0x6c, 0xa7, 0x81, 0x9e, 0xef, 0x34, 0xd0, 0x3f, 0x3b, 0x0d, - 0xf4, 0xcd, 0x6e, 0x63, 0xe6, 0xf9, 0x6e, 0x63, 0xe6, 0xaf, 0xdd, 0xc6, 0xcc, 0x17, 0x4e, 0x10, - 0x89, 0x70, 0xbb, 0xe7, 0xf8, 0x6c, 0xcb, 0xcc, 0x9c, 0xb0, 0x3e, 0x25, 0xc3, 0x3d, 0x05, 0xc4, - 0xa3, 0x94, 0xf2, 0xde, 0x29, 0xf9, 0x8f, 0xfc, 0xed, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x4e, - 0x2b, 0x58, 0x28, 0xf6, 0x0c, 0x00, 0x00, + 0x14, 0xc7, 0x33, 0x49, 0xdd, 0x1f, 0xcf, 0x29, 0x12, 0xd3, 0x56, 0x49, 0x17, 0xea, 0xa4, 0x5b, + 0x4a, 0x23, 0x4b, 0xdd, 0x89, 0x4d, 0x85, 0xa8, 0x2b, 0x7e, 0x24, 0x08, 0xd2, 0x0a, 0x21, 0xa5, + 0x5b, 0x71, 0xe1, 0x62, 0xad, 0xed, 0xe9, 0x7a, 0x95, 0xcd, 0xce, 0x76, 0x67, 0x62, 0xb9, 0x54, + 0x48, 0x88, 0x53, 0x8f, 0x08, 0x2e, 0xfd, 0x33, 0x38, 0x21, 0x24, 0x04, 0x37, 0xa4, 0x1e, 0x2b, + 0xb8, 0x20, 0x90, 0x10, 0x4a, 0x90, 0xf8, 0x37, 0xd0, 0xfc, 0x70, 0x76, 0x92, 0xac, 0xe3, 0x4d, + 0x9a, 0xd3, 0xfe, 0x9a, 0xf7, 0xde, 0xe7, 0xfb, 0x9d, 0xe7, 0x37, 0x86, 0xfa, 0x17, 0x54, 0x04, + 0xdd, 0x7e, 0x10, 0x25, 0x44, 0xdd, 0xb1, 0x8c, 0x92, 0x38, 0x0a, 0xfb, 0xa2, 0x1b, 0x47, 0x34, + 0x11, 0xe4, 0xd1, 0x16, 0xcd, 0x1e, 0x7b, 0x69, 0xc6, 0x04, 0xc3, 0xb5, 0xdd, 0xb5, 0xde, 0x68, + 0xad, 0x67, 0xad, 0x75, 0xea, 0x5d, 0xc6, 0x37, 0x19, 0x27, 0x9d, 0x80, 0x53, 0x1d, 0x48, 0x06, + 0x8d, 0x0e, 0x15, 0x41, 0x83, 0xa4, 0x41, 0x18, 0x25, 0x81, 0x88, 0x58, 0xa2, 0x73, 0x39, 0x17, + 0x43, 0x16, 0x32, 0x75, 0x4b, 0xe4, 0x9d, 0x79, 0xfb, 0x7a, 0xc8, 0x58, 0x18, 0x53, 0x12, 0xa4, + 0x11, 0x09, 0x92, 0x84, 0x09, 0x15, 0xc2, 0xcd, 0xd7, 0xf7, 0x26, 0xb0, 0x76, 0x62, 0xd6, 0xdd, + 0x68, 0xf7, 0x69, 0xd0, 0xa3, 0x59, 0x7b, 0x40, 0xb3, 0xe8, 0x61, 0xd4, 0xb5, 0x6b, 0x2e, 0x4f, + 0x88, 0x57, 0x9f, 0xda, 0x5c, 0x04, 0x82, 0x9a, 0x88, 0x22, 0x77, 0xd2, 0x8d, 0x90, 0xa4, 0x19, + 0x63, 0x0f, 0xb9, 0xb9, 0x98, 0xb5, 0x73, 0x46, 0xfd, 0x26, 0x0f, 0xc9, 0xa0, 0x21, 0x2f, 0xfa, + 0x83, 0xdb, 0x03, 0xe7, 0xbe, 0x34, 0x63, 0x25, 0x8e, 0x57, 0x25, 0xe1, 0x5d, 0x05, 0xe8, 0xd3, + 0x47, 0x5b, 0x94, 0x0b, 0xfc, 0x31, 0x40, 0x6e, 0xce, 0x3c, 0x5a, 0x44, 0x4b, 0xd5, 0xe6, 0x9b, + 0x9e, 0xce, 0xe5, 0x49, 0x27, 0x3d, 0xbd, 0x05, 0xc6, 0x49, 0x6f, 0x3d, 0x08, 0xa9, 0x89, 0xf5, + 0xad, 0x48, 0xf7, 0x67, 0x04, 0xaf, 0x15, 0x96, 0xe1, 0x29, 0x4b, 0x38, 0xc5, 0x9f, 0xc1, 0x79, + 0xdb, 0x1f, 0x3e, 0x8f, 0x16, 0x67, 0x96, 0xaa, 0xcd, 0xba, 0x57, 0xb0, 0xa9, 0xe9, 0x86, 0x62, + 0x97, 0xda, 0xac, 0x54, 0xab, 0xa7, 0x9e, 0xff, 0xbd, 0x30, 0xe5, 0xcf, 0x76, 0xf2, 0x57, 0x1c, + 0xaf, 0xed, 0xc1, 0x9f, 0x56, 0xf8, 0x37, 0x26, 0xe2, 0x6b, 0xa6, 0x3d, 0xfc, 0x77, 0x8c, 0x4b, + 0x6b, 0x54, 0x14, 0xb8, 0x74, 0x05, 0xc0, 0xd0, 0x07, 0xbc, 0xaf, 0x5c, 0x9a, 0xf5, 0xcf, 0x69, + 0x90, 0x80, 0xf7, 0xdd, 0xd8, 0x68, 0xdf, 0x1f, 0x6c, 0xb4, 0x7f, 0x0a, 0xb3, 0xb6, 0x76, 0xe3, + 0xf2, 0x11, 0xa4, 0xfb, 0x55, 0x4b, 0xb4, 0xdb, 0x85, 0xcb, 0x23, 0xa7, 0x3f, 0x94, 0xd1, 0x0f, + 0x64, 0xc7, 0x9c, 0xf4, 0x7e, 0xfe, 0x88, 0xf2, 0xb6, 0xb1, 0xab, 0x18, 0x49, 0xf7, 0xa1, 0x6a, + 0xb5, 0xeb, 0x61, 0x9b, 0x69, 0x75, 0xb8, 0x97, 0x27, 0x32, 0x9b, 0x09, 0xdd, 0xdd, 0x37, 0x27, + 0xb7, 0x95, 0x6f, 0x1b, 0x7f, 0xd6, 0xa8, 0x38, 0xe8, 0xcf, 0x65, 0x38, 0xab, 0xc1, 0xa3, 0x9e, + 0x72, 0x67, 0xc6, 0x3f, 0xa3, 0x9e, 0xef, 0xf5, 0xdc, 0x28, 0x6f, 0x81, 0x02, 0xc5, 0x9f, 0xec, + 0x57, 0x8c, 0x8e, 0xa6, 0xd8, 0xd6, 0x2a, 0x7f, 0x2d, 0xaf, 0xaa, 0x5a, 0xeb, 0x19, 0x1b, 0x94, + 0x60, 0xc3, 0x73, 0x70, 0x46, 0x0c, 0x75, 0xf7, 0x49, 0x67, 0xce, 0xf9, 0xa7, 0xc5, 0x50, 0xb6, + 0x1e, 0x6e, 0x41, 0x45, 0xf5, 0xcb, 0xfc, 0x8c, 0x02, 0x7a, 0x63, 0x42, 0x53, 0xad, 0xcb, 0x8b, + 0xaf, 0x43, 0xf6, 0x75, 0xf5, 0x29, 0x95, 0x37, 0xef, 0x6a, 0x89, 0x23, 0x86, 0xed, 0x28, 0xe9, + 0xd1, 0xe1, 0x7c, 0x45, 0xe3, 0x88, 0xe1, 0x3d, 0xf9, 0xe8, 0xd6, 0x01, 0xdb, 0xf8, 0xc6, 0xa2, + 0x8b, 0x50, 0x19, 0x04, 0xb1, 0x81, 0x3f, 0xeb, 0xeb, 0x07, 0xf7, 0x1a, 0x5c, 0x55, 0x6b, 0x75, + 0xf7, 0x3e, 0xd8, 0x4a, 0x53, 0x96, 0x09, 0xda, 0x53, 0xce, 0x70, 0x23, 0xdd, 0x7d, 0x86, 0xc0, + 0x3d, 0x6c, 0x95, 0xa9, 0x90, 0xc1, 0x9c, 0x99, 0xaf, 0x7c, 0xb4, 0xa2, 0xad, 0xc4, 0x8e, 0xe6, + 0xc9, 0xad, 0x49, 0x1b, 0x52, 0x94, 0xdf, 0x34, 0xe3, 0xa5, 0x7e, 0x51, 0x6d, 0xf7, 0x2a, 0x2c, + 0x58, 0x64, 0x1f, 0x25, 0x41, 0x27, 0xde, 0x4f, 0xff, 0x2d, 0x82, 0xc5, 0xf1, 0x6b, 0x0c, 0x7b, + 0x02, 0xa6, 0x40, 0x9b, 0xea, 0xef, 0x27, 0x47, 0x7e, 0xa1, 0x7f, 0xb0, 0x6e, 0xf3, 0x2f, 0x80, + 0x8a, 0x82, 0xc2, 0x3f, 0x21, 0x78, 0xc5, 0x9a, 0x26, 0x2b, 0x71, 0x8c, 0x5b, 0x93, 0xaa, 0x8d, + 0x3f, 0x32, 0x9c, 0x3b, 0xc7, 0x8a, 0xd5, 0x2e, 0xb8, 0xcb, 0x5f, 0xff, 0xfe, 0xef, 0x77, 0xd3, + 0x37, 0xf0, 0x75, 0x75, 0xa2, 0xdd, 0xd4, 0x87, 0xdb, 0xb8, 0x53, 0x94, 0x3f, 0x9d, 0x46, 0xf8, + 0x57, 0x04, 0x55, 0x2b, 0x53, 0x49, 0xf4, 0xc2, 0x39, 0x5e, 0x12, 0xbd, 0x78, 0x8c, 0xbb, 0xef, + 0x2a, 0xf4, 0x5b, 0xb8, 0x59, 0x0a, 0x9d, 0x3c, 0xc9, 0x7f, 0x5b, 0x5f, 0x4a, 0x1d, 0x3f, 0x20, + 0x38, 0x9f, 0x8f, 0x03, 0xb9, 0x09, 0xb7, 0xcb, 0x1a, 0x79, 0x60, 0x8c, 0x39, 0xad, 0xe3, 0x84, + 0x1a, 0x1d, 0x9e, 0xd2, 0x71, 0x1d, 0x5f, 0x1b, 0xa7, 0xc3, 0x9a, 0x73, 0x12, 0xfc, 0x17, 0x04, + 0x90, 0xa7, 0x29, 0x49, 0x5d, 0x34, 0x7c, 0x9d, 0xd6, 0x71, 0x42, 0x0d, 0xf5, 0x6d, 0x45, 0xbd, + 0x8c, 0xbd, 0x12, 0xd4, 0xe4, 0xc9, 0x68, 0x8e, 0x2a, 0xe7, 0x9f, 0x21, 0xa8, 0xa8, 0x49, 0x85, + 0x1b, 0xa5, 0x00, 0xec, 0xa1, 0xec, 0x34, 0x8f, 0x12, 0x62, 0x58, 0x97, 0x14, 0xeb, 0x02, 0xbe, + 0x32, 0x8e, 0x35, 0x95, 0xcb, 0x25, 0xda, 0x9f, 0x08, 0x2e, 0x15, 0x8e, 0x3c, 0xbc, 0x52, 0xaa, + 0xee, 0x61, 0x43, 0xd5, 0x59, 0x7d, 0x99, 0x14, 0x46, 0x4a, 0x4b, 0x49, 0x69, 0x60, 0x32, 0x4e, + 0xca, 0x98, 0x79, 0x2c, 0xc5, 0xfd, 0x86, 0xe0, 0x42, 0xc1, 0x44, 0xc4, 0xef, 0x1f, 0x81, 0xab, + 0x68, 0xde, 0x3a, 0x1f, 0x1c, 0x3f, 0x81, 0x91, 0xf5, 0x8e, 0x92, 0x45, 0xf0, 0xcd, 0x09, 0xb2, + 0xf6, 0x8e, 0xea, 0xa7, 0xd3, 0xc8, 0xa9, 0x7c, 0xf5, 0xdf, 0xf7, 0x75, 0xb4, 0x7a, 0xf7, 0xf9, + 0x76, 0x0d, 0xbd, 0xd8, 0xae, 0xa1, 0x7f, 0xb6, 0x6b, 0xe8, 0x9b, 0x9d, 0xda, 0xd4, 0x8b, 0x9d, + 0xda, 0xd4, 0x1f, 0x3b, 0xb5, 0xa9, 0xcf, 0xbd, 0x30, 0x12, 0xfd, 0xad, 0x8e, 0xd7, 0x65, 0x9b, + 0x76, 0xf2, 0x84, 0xf5, 0x28, 0x19, 0xee, 0xa9, 0x21, 0x1e, 0xa7, 0x94, 0x77, 0x4e, 0xab, 0xbf, + 0xe9, 0x6f, 0xfd, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x83, 0xf3, 0xe5, 0xa0, 0x0b, 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -794,6 +794,7 @@ func NewQueryClient(cc grpc1.ClientConn) QueryClient { return &queryClient{cc} } +// Deprecated: Do not use. func (c *queryClient) BlockHeaderAll(ctx context.Context, in *QueryAllBlockHeaderRequest, opts ...grpc.CallOption) (*QueryAllBlockHeaderResponse, error) { out := new(QueryAllBlockHeaderResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/BlockHeaderAll", in, out, opts...) @@ -803,6 +804,7 @@ func (c *queryClient) BlockHeaderAll(ctx context.Context, in *QueryAllBlockHeade return out, nil } +// Deprecated: Do not use. func (c *queryClient) BlockHeader(ctx context.Context, in *QueryGetBlockHeaderRequest, opts ...grpc.CallOption) (*QueryGetBlockHeaderResponse, error) { out := new(QueryGetBlockHeaderResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/BlockHeader", in, out, opts...) @@ -812,6 +814,7 @@ func (c *queryClient) BlockHeader(ctx context.Context, in *QueryGetBlockHeaderRe return out, nil } +// Deprecated: Do not use. func (c *queryClient) ChainStateAll(ctx context.Context, in *QueryAllChainStateRequest, opts ...grpc.CallOption) (*QueryAllChainStateResponse, error) { out := new(QueryAllChainStateResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/ChainStateAll", in, out, opts...) @@ -821,6 +824,7 @@ func (c *queryClient) ChainStateAll(ctx context.Context, in *QueryAllChainStateR return out, nil } +// Deprecated: Do not use. func (c *queryClient) ChainState(ctx context.Context, in *QueryGetChainStateRequest, opts ...grpc.CallOption) (*QueryGetChainStateResponse, error) { out := new(QueryGetChainStateResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/ChainState", in, out, opts...) @@ -830,6 +834,7 @@ func (c *queryClient) ChainState(ctx context.Context, in *QueryGetChainStateRequ return out, nil } +// Deprecated: Do not use. func (c *queryClient) Prove(ctx context.Context, in *QueryProveRequest, opts ...grpc.CallOption) (*QueryProveResponse, error) { out := new(QueryProveResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/Prove", in, out, opts...) @@ -839,6 +844,7 @@ func (c *queryClient) Prove(ctx context.Context, in *QueryProveRequest, opts ... return out, nil } +// Deprecated: Do not use. func (c *queryClient) HeaderSupportedChains(ctx context.Context, in *QueryHeaderSupportedChainsRequest, opts ...grpc.CallOption) (*QueryHeaderSupportedChainsResponse, error) { out := new(QueryHeaderSupportedChainsResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/HeaderSupportedChains", in, out, opts...) @@ -848,6 +854,7 @@ func (c *queryClient) HeaderSupportedChains(ctx context.Context, in *QueryHeader return out, nil } +// Deprecated: Do not use. func (c *queryClient) HeaderEnabledChains(ctx context.Context, in *QueryHeaderEnabledChainsRequest, opts ...grpc.CallOption) (*QueryHeaderEnabledChainsResponse, error) { out := new(QueryHeaderEnabledChainsResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.lightclient.Query/HeaderEnabledChains", in, out, opts...) diff --git a/zetaclient/chains/bitcoin/observer/event_test.go b/zetaclient/chains/bitcoin/observer/event_test.go index b6a0c3cba4..e73b0eef1a 100644 --- a/zetaclient/chains/bitcoin/observer/event_test.go +++ b/zetaclient/chains/bitcoin/observer/event_test.go @@ -47,7 +47,7 @@ func Test_Category(t *testing.T) { cfg := config.Config{ ComplianceConfig: sample.ComplianceConfig(), } - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // test cases tests := []struct { @@ -315,7 +315,7 @@ func Test_IsEventProcessable(t *testing.T) { cfg := config.Config{ ComplianceConfig: sample.ComplianceConfig(), } - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // test cases tests := []struct { diff --git a/zetaclient/chains/bitcoin/signer/outbound_data.go b/zetaclient/chains/bitcoin/signer/outbound_data.go index ed62195707..669221c2a6 100644 --- a/zetaclient/chains/bitcoin/signer/outbound_data.go +++ b/zetaclient/chains/bitcoin/signer/outbound_data.go @@ -57,8 +57,8 @@ func NewOutboundData( } params := cctx.GetCurrentOutboundParam() - // support gas token only for Bitcoin outbound - if cctx.InboundParams.CoinType != coin.CoinType_Gas { + // support coin type GAS and CMD only + if cctx.InboundParams.CoinType != coin.CoinType_Gas && cctx.InboundParams.CoinType != coin.CoinType_Cmd { return nil, fmt.Errorf("invalid coin type %s", cctx.InboundParams.CoinType.String()) } diff --git a/zetaclient/chains/bitcoin/signer/outbound_data_test.go b/zetaclient/chains/bitcoin/signer/outbound_data_test.go index faf94920f1..31d7788e05 100644 --- a/zetaclient/chains/bitcoin/signer/outbound_data_test.go +++ b/zetaclient/chains/bitcoin/signer/outbound_data_test.go @@ -25,7 +25,7 @@ func Test_NewOutboundData(t *testing.T) { cfg := config.Config{ ComplianceConfig: sample.ComplianceConfig(), } - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // test cases tests := []struct { diff --git a/zetaclient/chains/evm/observer/inbound.go b/zetaclient/chains/evm/observer/inbound.go index 6b62216b5e..db4e3c475b 100644 --- a/zetaclient/chains/evm/observer/inbound.go +++ b/zetaclient/chains/evm/observer/inbound.go @@ -12,7 +12,7 @@ import ( "strings" sdkmath "cosmossdk.io/math" - "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum" ethcommon "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/pkg/errors" @@ -43,7 +43,7 @@ func (ob *Observer) ProcessInboundTrackers(ctx context.Context) error { for _, tracker := range trackers { // query tx and receipt - tx, _, err := ob.TransactionByHash(ctx, tracker.TxHash) + tx, _, err := ob.transactionByHash(ctx, tracker.TxHash) if err != nil { return errors.Wrapf( err, @@ -77,11 +77,11 @@ func (ob *Observer) ProcessInboundTrackers(ctx context.Context) error { // try processing the tracker for v1 inbound switch tracker.CoinType { case coin.CoinType_Zeta: - _, err = ob.CheckAndVoteInboundTokenZeta(ctx, tx, receipt, true) + _, err = ob.checkAndVoteInboundTokenZeta(ctx, tx, receipt, true) case coin.CoinType_ERC20: - _, err = ob.CheckAndVoteInboundTokenERC20(ctx, tx, receipt, true) + _, err = ob.checkAndVoteInboundTokenERC20(ctx, tx, receipt, true) case coin.CoinType_Gas: - _, err = ob.CheckAndVoteInboundTokenGas(ctx, tx, receipt, true) + _, err = ob.checkAndVoteInboundTokenGas(ctx, tx, receipt, true) default: return fmt.Errorf( "unknown coin type %s for inbound %s chain %d", @@ -140,42 +140,52 @@ func (ob *Observer) observeInboundInBlockRange(ctx context.Context, startBlock, Str(logs.FieldMethod, "observeInboundInBlockRange"). Uint64("start_block", startBlock).Uint64("to_block", toBlock).Logger() - // task 1: query evm chain for zeta sent logs (read at most 100 blocks in one go) - lastScannedZetaSent, err := ob.ObserveZetaSent(ctx, startBlock, toBlock) - if err != nil { - logger.Error().Err(err).Msg("error observing zeta sent events from ZetaConnector contract") - } - - // task 2: query evm chain for deposited logs (read at most 100 blocks in one go) - lastScannedDeposited, err := ob.ObserveERC20Deposited(ctx, startBlock, toBlock) - if err != nil { - logger.Error().Err(err).Msg("error observing deposited events from ERC20Custody contract") - } - // task 3: query the incoming tx to TSS address (read at most 100 blocks in one go) - lastScannedTssRecvd, err := ob.ObserveTSSReceive(ctx, startBlock, toBlock) + lastScannedTssRecvd, err := ob.observeTSSReceive(ctx, startBlock, toBlock) if err != nil { logger.Error().Err(err).Msg("error observing TSS received gas asset") } // task 4: filter the outbounds from TSS address to supplement outbound trackers // TODO: make this a separate go routine in outbound.go after switching to smart contract V2 - ob.FilterTSSOutbound(ctx, startBlock, toBlock) + ob.filterTSSOutbound(ctx, startBlock, toBlock) + + var ( + lastScannedZetaSent = startBlock - 1 + lastScannedDeposited = startBlock - 1 + lastScannedGatewayDeposit = startBlock - 1 + lastScannedGatewayCall = startBlock - 1 + lastScannedGatewayDepositAndCall = startBlock - 1 + ) - // 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) + logs, err := ob.fetchLogs(ctx, startBlock, toBlock) if err != nil { - ob.Logger().Inbound.Error().Err(err).Msg("error observing deposit events from Gateway contract") - } - lastScannedGatewayCall, err := ob.ObserveGatewayCall(ctx, startBlock, toBlock) - if err != nil { - ob.Logger().Inbound.Error().Err(err).Msg("error observing call events from Gateway contract") - } - lastScannedGatewayDepositAndCall, err := ob.ObserveGatewayDepositAndCall(ctx, startBlock, toBlock) - if err != nil { - ob.Logger().Inbound.Error().Err(err).Msg("error observing depositAndCall events from Gateway contract") + ob.Logger().Inbound.Error().Err(err).Msg("get gateway logs") + } else { + // handle connector contract deposit + lastScannedZetaSent, err = ob.observeZetaSent(ctx, startBlock, toBlock, logs) + if err != nil { + logger.Error().Err(err).Msg("error observing zeta sent events from ZetaConnector contract") + } + + // handle legacy erc20 direct deposit logs + lastScannedDeposited, err = ob.observeERC20Deposited(ctx, startBlock, toBlock, logs) + if err != nil { + logger.Error().Err(err).Msg("error observing deposited events from ERC20Custody contract") + } + + lastScannedGatewayDeposit, err = ob.observeGatewayDeposit(ctx, startBlock, toBlock, logs) + if err != nil { + ob.Logger().Inbound.Error().Err(err).Msg("error observing deposit events from Gateway contract") + } + lastScannedGatewayCall, err = ob.observeGatewayCall(ctx, startBlock, toBlock, logs) + if err != nil { + ob.Logger().Inbound.Error().Err(err).Msg("error observing call events from Gateway contract") + } + lastScannedGatewayDepositAndCall, err = ob.observeGatewayDepositAndCall(ctx, startBlock, toBlock, logs) + if err != nil { + ob.Logger().Inbound.Error().Err(err).Msg("error observing depositAndCall events from Gateway contract") + } } // note: using the lowest height for all events is not perfect, @@ -192,43 +202,75 @@ func (ob *Observer) observeInboundInBlockRange(ctx context.Context, startBlock, return lowestLastScannedBlock } -// ObserveZetaSent queries the ZetaSent event from the connector contract and posts to zetacore +func (ob *Observer) fetchLogs(ctx context.Context, startBlock, toBlock uint64) ([]ethtypes.Log, error) { + gatewayAddr, _, err := ob.getGatewayContract() + if err != nil { + return nil, errors.Wrap(err, "can't get gateway contract") + } + + erc20Addr, _, err := ob.getERC20CustodyContract() + if err != nil { + return nil, errors.Wrap(err, "can't get erc20 custody contract") + } + + connectorAddr, _, err := ob.getConnectorContract() + if err != nil { + return nil, errors.Wrap(err, "can't get connector contract") + } + + addresses := []ethcommon.Address{gatewayAddr, erc20Addr, connectorAddr} + + logs, err := ob.evmClient.FilterLogs(ctx, ethereum.FilterQuery{ + FromBlock: new(big.Int).SetUint64(startBlock), + ToBlock: new(big.Int).SetUint64(toBlock), + Addresses: addresses, + }) + if err != nil { + return nil, errors.Wrap(err, "filter logs") + } + + // increment prom counter + metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() + + return logs, nil +} + +// observeZetaSent queries the ZetaSent event from the connector contract and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserveZetaSent(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { +func (ob *Observer) observeZetaSent( + ctx context.Context, + startBlock, toBlock uint64, + logs []ethtypes.Log, +) (uint64, error) { app, err := zctx.FromContext(ctx) if err != nil { return 0, err } // filter ZetaSent logs - addrConnector, connector, err := ob.GetConnectorContract() + addrConnector, connector, err := ob.getConnectorContract() if err != nil { // we have to re-scan from this block next time return startBlock - 1, errors.Wrap(err, "error getting connector contract") } - iter, err := connector.FilterZetaSent(&bind.FilterOpts{ - Start: startBlock, - End: &toBlock, - Context: ctx, - }, []ethcommon.Address{}, []*big.Int{}) - if err != nil { - // we have to re-scan from this block next time - return startBlock - 1, errors.Wrap(err, "error filtering ZetaSent events") - } // collect and sort events by block number, then tx index, then log index (ascending) events := make([]*zetaconnector.ZetaConnectorNonEthZetaSent, 0) - for iter.Next() { + for _, log := range logs { // sanity check tx event - err := common.ValidateEvmTxLog(&iter.Event.Raw, addrConnector, "", common.TopicsZetaSent) + err := common.ValidateEvmTxLog(&log, addrConnector, "", common.TopicsZetaSent) + if err != nil { + continue + } + event, err := connector.ParseZetaSent(log) if err == nil { - events = append(events, iter.Event) + events = append(events, event) continue } ob.Logger().Inbound.Warn(). Err(err). Msgf("ObserveZetaSent: invalid ZetaSent event in tx %s on chain %d at height %d", - iter.Event.Raw.TxHash.Hex(), ob.Chain().ChainId, iter.Event.Raw.BlockNumber) + log.TxHash.Hex(), ob.Chain().ChainId, log.BlockNumber) } sort.SliceStable(events, func(i, j int) bool { if events[i].Raw.BlockNumber == events[j].Raw.BlockNumber { @@ -259,7 +301,7 @@ func (ob *Observer) ObserveZetaSent(ctx context.Context, startBlock, toBlock uin } guard[event.Raw.TxHash.Hex()] = true - msg := ob.BuildInboundVoteMsgForZetaSentEvent(app, event) + msg := ob.buildInboundVoteMsgForZetaSentEvent(app, event) if msg == nil { continue } @@ -275,39 +317,37 @@ func (ob *Observer) ObserveZetaSent(ctx context.Context, startBlock, toBlock uin return toBlock, nil } -// ObserveERC20Deposited queries the ERC20CustodyDeposited event from the ERC20Custody contract and posts to zetacore +// observeERC20Deposited queries the ERC20CustodyDeposited event from the ERC20Custody contract and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserveERC20Deposited(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { +func (ob *Observer) observeERC20Deposited( + ctx context.Context, + startBlock, toBlock uint64, + logs []ethtypes.Log, +) (uint64, error) { // filter ERC20CustodyDeposited logs - addrCustody, erc20custodyContract, err := ob.GetERC20CustodyContract() + addrCustody, erc20custodyContract, err := ob.getERC20CustodyContract() if err != nil { // we have to re-scan from this block next time return startBlock - 1, errors.Wrap(err, "error getting ERC20Custody contract") } - iter, err := erc20custodyContract.FilterDeposited(&bind.FilterOpts{ - Start: startBlock, - End: &toBlock, - Context: ctx, - }, []ethcommon.Address{}) - if err != nil { - // we have to re-scan from this block next time - return startBlock - 1, errors.Wrap(err, "error filtering ERC20 Deposited events") - } - // collect and sort events by block number, then tx index, then log index (ascending) events := make([]*erc20custody.ERC20CustodyDeposited, 0) - for iter.Next() { + for _, log := range logs { // sanity check tx event - err := common.ValidateEvmTxLog(&iter.Event.Raw, addrCustody, "", common.TopicsDeposited) + err := common.ValidateEvmTxLog(&log, addrCustody, "", common.TopicsDeposited) + if err != nil { + continue + } + event, err := erc20custodyContract.ParseDeposited(log) if err == nil { - events = append(events, iter.Event) + events = append(events, event) continue } ob.Logger().Inbound.Warn(). Err(err). Msgf("ObserveERC20Deposited: invalid Deposited event in tx %s on chain %d at height %d", - iter.Event.Raw.TxHash.Hex(), ob.Chain().ChainId, iter.Event.Raw.BlockNumber) + log.TxHash.Hex(), ob.Chain().ChainId, log.BlockNumber) } sort.SliceStable(events, func(i, j int) bool { if events[i].Raw.BlockNumber == events[j].Raw.BlockNumber { @@ -330,7 +370,7 @@ func (ob *Observer) ObserveERC20Deposited(ctx context.Context, startBlock, toBlo if event.Raw.BlockNumber > beingScanned { beingScanned = event.Raw.BlockNumber } - tx, _, err := ob.TransactionByHash(ctx, event.Raw.TxHash.Hex()) + tx, _, err := ob.transactionByHash(ctx, event.Raw.TxHash.Hex()) if err != nil { // we have to re-scan from this block next time return beingScanned - 1, errors.Wrapf(err, "error getting transaction %s", event.Raw.TxHash.Hex()) @@ -345,7 +385,7 @@ func (ob *Observer) ObserveERC20Deposited(ctx context.Context, startBlock, toBlo } guard[event.Raw.TxHash.Hex()] = true - msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) + msg := ob.buildInboundVoteMsgForDepositedEvent(event, sender) if msg != nil { _, err = ob.PostVoteInbound(ctx, msg, zetacore.PostVoteInboundExecutionGasLimit) if err != nil { @@ -358,13 +398,13 @@ func (ob *Observer) ObserveERC20Deposited(ctx context.Context, startBlock, toBlo return toBlock, nil } -// ObserveTSSReceive queries the incoming gas asset to TSS address and posts to zetacore +// observeTSSReceive queries the incoming gas asset to TSS address and posts to zetacore // returns the last block successfully scanned -func (ob *Observer) ObserveTSSReceive(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { +func (ob *Observer) observeTSSReceive(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { // query incoming gas asset for bn := startBlock; bn <= toBlock; bn++ { // observe TSS received gas token in block 'bn' - err := ob.ObserveTSSReceiveInBlock(ctx, bn) + err := ob.observeTSSReceiveInBlock(ctx, bn) if err != nil { // we have to re-scan from this block next time return bn - 1, errors.Wrapf(err, "error observing TSS received gas asset in block %d", bn) @@ -375,8 +415,8 @@ func (ob *Observer) ObserveTSSReceive(ctx context.Context, startBlock, toBlock u return toBlock, nil } -// CheckAndVoteInboundTokenZeta checks and votes on the given inbound Zeta token -func (ob *Observer) CheckAndVoteInboundTokenZeta( +// checkAndVoteInboundTokenZeta checks and votes on the given inbound Zeta token +func (ob *Observer) checkAndVoteInboundTokenZeta( ctx context.Context, tx *client.Transaction, receipt *ethtypes.Receipt, @@ -397,7 +437,7 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( } // get zeta connector contract - addrConnector, connector, err := ob.GetConnectorContract() + addrConnector, connector, err := ob.getConnectorContract() if err != nil { return "", err } @@ -410,7 +450,7 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( // sanity check tx event err = common.ValidateEvmTxLog(&event.Raw, addrConnector, tx.Hash, common.TopicsZetaSent) if err == nil { - msg = ob.BuildInboundVoteMsgForZetaSentEvent(app, event) + msg = ob.buildInboundVoteMsgForZetaSentEvent(app, event) } else { ob.Logger().Inbound.Error().Err(err).Msgf("CheckEvmTxLog error on inbound %s chain %d", tx.Hash, ob.Chain().ChainId) return "", err @@ -430,8 +470,8 @@ func (ob *Observer) CheckAndVoteInboundTokenZeta( return msg.Digest(), nil } -// CheckAndVoteInboundTokenERC20 checks and votes on the given inbound ERC20 token -func (ob *Observer) CheckAndVoteInboundTokenERC20( +// checkAndVoteInboundTokenERC20 checks and votes on the given inbound ERC20 token +func (ob *Observer) checkAndVoteInboundTokenERC20( ctx context.Context, tx *client.Transaction, receipt *ethtypes.Receipt, @@ -447,7 +487,7 @@ func (ob *Observer) CheckAndVoteInboundTokenERC20( } // get erc20 custody contract - addrCustody, custody, err := ob.GetERC20CustodyContract() + addrCustody, custody, err := ob.getERC20CustodyContract() if err != nil { return "", err } @@ -461,7 +501,7 @@ func (ob *Observer) CheckAndVoteInboundTokenERC20( // sanity check tx event err = common.ValidateEvmTxLog(&zetaDeposited.Raw, addrCustody, tx.Hash, common.TopicsDeposited) if err == nil { - msg = ob.BuildInboundVoteMsgForDepositedEvent(zetaDeposited, sender) + msg = ob.buildInboundVoteMsgForDepositedEvent(zetaDeposited, sender) } else { ob.Logger().Inbound.Error().Err(err).Msgf("CheckEvmTxLog error on inbound %s chain %d", tx.Hash, ob.Chain().ChainId) return "", err @@ -481,8 +521,8 @@ func (ob *Observer) CheckAndVoteInboundTokenERC20( return msg.Digest(), nil } -// CheckAndVoteInboundTokenGas checks and votes on the given inbound gas token -func (ob *Observer) CheckAndVoteInboundTokenGas( +// checkAndVoteInboundTokenGas checks and votes on the given inbound gas token +func (ob *Observer) checkAndVoteInboundTokenGas( ctx context.Context, tx *client.Transaction, receipt *ethtypes.Receipt, @@ -507,7 +547,7 @@ func (ob *Observer) CheckAndVoteInboundTokenGas( sender := ethcommon.HexToAddress(tx.From) // build inbound vote message and post vote - msg := ob.BuildInboundVoteMsgForTokenSentToTSS(tx, sender, receipt.BlockNumber.Uint64()) + msg := ob.buildInboundVoteMsgForTokenSentToTSS(tx, sender, receipt.BlockNumber.Uint64()) if msg == nil { // donation, restricted tx, etc. ob.Logger().Inbound.Info().Msgf("no vote message built for inbound %s chain %d", tx.Hash, ob.Chain().ChainId) @@ -520,8 +560,8 @@ func (ob *Observer) CheckAndVoteInboundTokenGas( return msg.Digest(), nil } -// BuildInboundVoteMsgForDepositedEvent builds a inbound vote message for a Deposited event -func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( +// buildInboundVoteMsgForDepositedEvent builds a inbound vote message for a Deposited event +func (ob *Observer) buildInboundVoteMsgForDepositedEvent( event *erc20custody.ERC20CustodyDeposited, sender ethcommon.Address, ) *types.MsgVoteInbound { @@ -576,8 +616,8 @@ func (ob *Observer) BuildInboundVoteMsgForDepositedEvent( ) } -// BuildInboundVoteMsgForZetaSentEvent builds a inbound vote message for a ZetaSent event -func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( +// buildInboundVoteMsgForZetaSentEvent builds a inbound vote message for a ZetaSent event +func (ob *Observer) buildInboundVoteMsgForZetaSentEvent( appContext *zctx.AppContext, event *zetaconnector.ZetaConnectorNonEthZetaSent, ) *types.MsgVoteInbound { @@ -629,8 +669,8 @@ func (ob *Observer) BuildInboundVoteMsgForZetaSentEvent( ) } -// BuildInboundVoteMsgForTokenSentToTSS builds a inbound vote message for a token sent to TSS -func (ob *Observer) BuildInboundVoteMsgForTokenSentToTSS( +// buildInboundVoteMsgForTokenSentToTSS builds a inbound vote message for a token sent to TSS +func (ob *Observer) buildInboundVoteMsgForTokenSentToTSS( tx *client.Transaction, sender ethcommon.Address, blockNumber uint64, @@ -679,8 +719,8 @@ func (ob *Observer) BuildInboundVoteMsgForTokenSentToTSS( ) } -// ObserveTSSReceiveInBlock queries the incoming gas asset to TSS address in a single block and posts votes -func (ob *Observer) ObserveTSSReceiveInBlock(ctx context.Context, blockNumber uint64) error { +// observeTSSReceiveInBlock queries the incoming gas asset to TSS address in a single block and posts votes +func (ob *Observer) observeTSSReceiveInBlock(ctx context.Context, blockNumber uint64) error { block, err := ob.GetBlockByNumberCached(ctx, blockNumber) if err != nil { return errors.Wrapf(err, "error getting block %d for chain %d", blockNumber, ob.Chain().ChainId) @@ -693,7 +733,7 @@ func (ob *Observer) ObserveTSSReceiveInBlock(ctx context.Context, blockNumber ui return errors.Wrapf(err, "error getting receipt for inbound %s chain %d", tx.Hash, ob.Chain().ChainId) } - _, err = ob.CheckAndVoteInboundTokenGas(ctx, &tx, receipt, true) + _, err = ob.checkAndVoteInboundTokenGas(ctx, &tx, receipt, true) if err != nil { return errors.Wrapf( err, diff --git a/zetaclient/chains/evm/observer/inbound_test.go b/zetaclient/chains/evm/observer/inbound_test.go index 71b4ecc94c..2eed26bfad 100644 --- a/zetaclient/chains/evm/observer/inbound_test.go +++ b/zetaclient/chains/evm/observer/inbound_test.go @@ -1,4 +1,4 @@ -package observer_test +package observer import ( "encoding/hex" @@ -41,7 +41,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) - ballot, err := ob.CheckAndVoteInboundTokenZeta(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenZeta(ob.ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -59,7 +59,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe() - 2) - _, err := ob.CheckAndVoteInboundTokenZeta(ob.ctx, tx, receipt, false) + _, err := ob.checkAndVoteInboundTokenZeta(ob.ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if no ZetaSent event", func(t *testing.T) { @@ -77,7 +77,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) - ballot, err := ob.CheckAndVoteInboundTokenZeta(ob.ctx, tx, receipt, true) + ballot, err := ob.checkAndVoteInboundTokenZeta(ob.ctx, tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -98,7 +98,7 @@ func Test_CheckAndVoteInboundTokenZeta(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) // ACT - _, err := ob.CheckAndVoteInboundTokenZeta(ob.ctx, tx, receipt, true) + _, err := ob.checkAndVoteInboundTokenZeta(ob.ctx, tx, receipt, true) // ASSERT require.ErrorContains(t, err, "emitter address mismatch") @@ -126,7 +126,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) - ballot, err := ob.CheckAndVoteInboundTokenERC20(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenERC20(ob.ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -144,7 +144,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe() - 2) - _, err := ob.CheckAndVoteInboundTokenERC20(ob.ctx, tx, receipt, false) + _, err := ob.checkAndVoteInboundTokenERC20(ob.ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if no Deposit event", func(t *testing.T) { @@ -162,7 +162,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) - ballot, err := ob.CheckAndVoteInboundTokenERC20(ob.ctx, tx, receipt, true) + ballot, err := ob.checkAndVoteInboundTokenERC20(ob.ctx, tx, receipt, true) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -184,7 +184,7 @@ func Test_CheckAndVoteInboundTokenERC20(t *testing.T) { ob.WithLastBlock(receipt.BlockNumber.Uint64() + ob.chainParams.InboundConfirmationSafe()) // ACT - _, err := ob.CheckAndVoteInboundTokenERC20(ob.ctx, tx, receipt, true) + _, err := ob.checkAndVoteInboundTokenERC20(ob.ctx, tx, receipt, true) // ASSERT require.ErrorContains(t, err, "emitter address mismatch") @@ -213,7 +213,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { ob := newTestSuite(t) ob.WithLastBlock(lastBlock) - ballot, err := ob.CheckAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, cctx.InboundParams.BallotIndex, ballot) }) @@ -225,7 +225,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { ob := newTestSuite(t) ob.WithLastBlock(lastBlock) - _, err := ob.CheckAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) + _, err := ob.checkAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) require.ErrorContains(t, err, "not been confirmed") }) t.Run("should not act if receiver is not TSS", func(t *testing.T) { @@ -237,7 +237,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { ob := newTestSuite(t) ob.WithLastBlock(lastBlock) - ballot, err := ob.CheckAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) require.ErrorContains(t, err, "not TSS address") require.Equal(t, "", ballot) }) @@ -250,7 +250,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { ob := newTestSuite(t) ob.WithLastBlock(lastBlock) - ballot, err := ob.CheckAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) require.ErrorContains(t, err, "not a successful tx") require.Equal(t, "", ballot) }) @@ -263,7 +263,7 @@ func Test_CheckAndVoteInboundTokenGas(t *testing.T) { ob := newTestSuite(t) ob.WithLastBlock(lastBlock) - ballot, err := ob.CheckAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) + ballot, err := ob.checkAndVoteInboundTokenGas(ob.ctx, tx, receipt, false) require.NoError(t, err) require.Equal(t, "", ballot) }) @@ -289,29 +289,29 @@ func Test_BuildInboundVoteMsgForZetaSentEvent(t *testing.T) { } t.Run("should return vote msg for archived ZetaSent event", func(t *testing.T) { - msg := ob.BuildInboundVoteMsgForZetaSentEvent(ob.appContext, event) + msg := ob.buildInboundVoteMsgForZetaSentEvent(ob.appContext, event) require.NotNil(t, msg) require.Equal(t, cctx.InboundParams.BallotIndex, msg.Digest()) }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { sender := event.ZetaTxSenderAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{sender} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(ob.appContext, event) + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForZetaSentEvent(ob.appContext, event) require.Nil(t, msg) }) t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { receiver := clienttypes.BytesToEthHex(event.DestinationAddress) cfg.ComplianceConfig.RestrictedAddresses = []string{receiver} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(ob.appContext, event) + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForZetaSentEvent(ob.appContext, event) require.Nil(t, msg) }) t.Run("should return nil msg if txOrigin is restricted", func(t *testing.T) { txOrigin := event.SourceTxOriginAddress.Hex() cfg.ComplianceConfig.RestrictedAddresses = []string{txOrigin} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForZetaSentEvent(ob.appContext, event) + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForZetaSentEvent(ob.appContext, event) require.Nil(t, msg) }) } @@ -337,26 +337,26 @@ func Test_BuildInboundVoteMsgForDepositedEvent(t *testing.T) { } t.Run("should return vote msg for archived Deposited event", func(t *testing.T) { - msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) + msg := ob.buildInboundVoteMsgForDepositedEvent(event, sender) require.NotNil(t, msg) require.Equal(t, cctx.InboundParams.BallotIndex, msg.Digest()) }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{sender.Hex()} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForDepositedEvent(event, sender) require.Nil(t, msg) }) t.Run("should return nil msg if receiver is restricted", func(t *testing.T) { receiver := clienttypes.BytesToEthHex(event.Recipient) cfg.ComplianceConfig.RestrictedAddresses = []string{receiver} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForDepositedEvent(event, sender) require.Nil(t, msg) }) t.Run("should return nil msg on donation transaction", func(t *testing.T) { event.Message = []byte(constant.DonationMessage) - msg := ob.BuildInboundVoteMsgForDepositedEvent(event, sender) + msg := ob.buildInboundVoteMsgForDepositedEvent(event, sender) require.Nil(t, msg) }) } @@ -390,7 +390,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { } t.Run("should return vote msg for archived gas token transfer to TSS", func(t *testing.T) { - msg := ob.BuildInboundVoteMsgForTokenSentToTSS( + msg := ob.buildInboundVoteMsgForTokenSentToTSS( tx, ethcommon.HexToAddress(tx.From), receipt.BlockNumber.Uint64(), @@ -400,8 +400,8 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { }) t.Run("should return nil msg if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{tx.From} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForTokenSentToTSS( + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForTokenSentToTSS( tx, ethcommon.HexToAddress(tx.From), receipt.BlockNumber.Uint64(), @@ -414,8 +414,8 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { message := hex.EncodeToString(ethcommon.HexToAddress(testutils.OtherAddress1).Bytes()) txCopy.Input = message // use other address as receiver cfg.ComplianceConfig.RestrictedAddresses = []string{testutils.OtherAddress1} - config.LoadComplianceConfig(cfg) - msg := ob.BuildInboundVoteMsgForTokenSentToTSS( + config.SetRestrictedAddressesFromConfig(cfg) + msg := ob.buildInboundVoteMsgForTokenSentToTSS( txCopy, ethcommon.HexToAddress(txCopy.From), receipt.BlockNumber.Uint64(), @@ -423,7 +423,7 @@ func Test_BuildInboundVoteMsgForTokenSentToTSS(t *testing.T) { require.Nil(t, msg) }) t.Run("should return nil msg on donation transaction", func(t *testing.T) { - msg := ob.BuildInboundVoteMsgForTokenSentToTSS(txDonation, + msg := ob.buildInboundVoteMsgForTokenSentToTSS(txDonation, ethcommon.HexToAddress(txDonation.From), receiptDonation.BlockNumber.Uint64()) require.Nil(t, msg) }) @@ -492,7 +492,7 @@ func Test_ObserveTSSReceiveInBlock(t *testing.T) { tt.mockEVMClient(ob.evmMock) } - err := ob.ObserveTSSReceiveInBlock(ob.ctx, blockNumber) + err := ob.observeTSSReceiveInBlock(ob.ctx, blockNumber) if tt.errMsg != "" { require.ErrorContains(t, err, tt.errMsg) return diff --git a/zetaclient/chains/evm/observer/observer.go b/zetaclient/chains/evm/observer/observer.go index 3c2430d3e3..051c3ec97a 100644 --- a/zetaclient/chains/evm/observer/observer.go +++ b/zetaclient/chains/evm/observer/observer.go @@ -13,8 +13,6 @@ import ( "github.com/pkg/errors" "github.com/zeta-chain/protocol-contracts/pkg/erc20custody.sol" "github.com/zeta-chain/protocol-contracts/pkg/gatewayevm.sol" - "github.com/zeta-chain/protocol-contracts/pkg/zeta.non-eth.sol" - zetaconnectoreth "github.com/zeta-chain/protocol-contracts/pkg/zetaconnector.eth.sol" "github.com/zeta-chain/protocol-contracts/pkg/zetaconnector.non-eth.sol" "github.com/zeta-chain/node/zetaclient/chains/base" @@ -63,79 +61,54 @@ func New(baseObserver *base.Observer, client interfaces.EVMRPCClient) (*Observer } // load last block scanned - if err := ob.LoadLastBlockScanned(context.Background()); err != nil { + if err := ob.loadLastBlockScanned(context.Background()); err != nil { return nil, errors.Wrap(err, "unable to load last block scanned") } return ob, nil } -// GetConnectorContract returns the non-Eth connector address and binder -func (ob *Observer) GetConnectorContract() (ethcommon.Address, *zetaconnector.ZetaConnectorNonEth, error) { +// getConnectorContract returns the non-Eth connector address and binder +func (ob *Observer) getConnectorContract() (ethcommon.Address, *zetaconnector.ZetaConnectorNonEth, error) { addr := ethcommon.HexToAddress(ob.ChainParams().ConnectorContractAddress) contract, err := zetaconnector.NewZetaConnectorNonEth(addr, ob.evmClient) return addr, contract, err } -// GetConnectorContractEth returns the Eth connector address and binder -func (ob *Observer) GetConnectorContractEth() (ethcommon.Address, *zetaconnectoreth.ZetaConnectorEth, error) { - addr := ethcommon.HexToAddress(ob.ChainParams().ConnectorContractAddress) - contract, err := FetchConnectorContractEth(addr, ob.evmClient) - return addr, contract, err -} - -// GetERC20CustodyContract returns ERC20Custody contract address and binder -func (ob *Observer) GetERC20CustodyContract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { +// getERC20CustodyContract returns ERC20Custody contract address and binder +func (ob *Observer) getERC20CustodyContract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { addr := ethcommon.HexToAddress(ob.ChainParams().Erc20CustodyContractAddress) contract, err := erc20custody.NewERC20Custody(addr, ob.evmClient) return addr, contract, err } -// GetERC20CustodyV2Contract returns ERC20Custody contract address and binder +// getERC20CustodyV2Contract returns ERC20Custody 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, *erc20custody.ERC20Custody, error) { +func (ob *Observer) getERC20CustodyV2Contract() (ethcommon.Address, *erc20custody.ERC20Custody, error) { addr := ethcommon.HexToAddress(ob.ChainParams().Erc20CustodyContractAddress) contract, err := erc20custody.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) { +// getGatewayContract returns the gateway contract address and binder +func (ob *Observer) getGatewayContract() (ethcommon.Address, *gatewayevm.GatewayEVM, error) { addr := ethcommon.HexToAddress(ob.ChainParams().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( - addr ethcommon.Address, - client interfaces.EVMRPCClient, -) (*zetaconnectoreth.ZetaConnectorEth, error) { - return zetaconnectoreth.NewZetaConnectorEth(addr, client) -} - -// FetchZetaTokenContract returns the non-Eth ZETA token binder -// TODO(revamp): move this to a contract package -func FetchZetaTokenContract( - addr ethcommon.Address, - client interfaces.EVMRPCClient, -) (*zeta.ZetaNonEth, error) { - return zeta.NewZetaNonEth(addr, client) -} - -// SetTxNReceipt sets the receipt and transaction in memory -func (ob *Observer) SetTxNReceipt(nonce uint64, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction) { +// setTxNReceipt sets the receipt and transaction in memory +func (ob *Observer) setTxNReceipt(nonce uint64, receipt *ethtypes.Receipt, transaction *ethtypes.Transaction) { ob.Mu().Lock() defer ob.Mu().Unlock() ob.outboundConfirmedReceipts[ob.OutboundID(nonce)] = receipt ob.outboundConfirmedTransactions[ob.OutboundID(nonce)] = transaction } -// GetTxNReceipt gets the receipt and transaction from memory -func (ob *Observer) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction) { +// getTxNReceipt gets the receipt and transaction from memory +func (ob *Observer) getTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes.Transaction) { ob.Mu().Lock() defer ob.Mu().Unlock() receipt := ob.outboundConfirmedReceipts[ob.OutboundID(nonce)] @@ -143,8 +116,8 @@ func (ob *Observer) GetTxNReceipt(nonce uint64) (*ethtypes.Receipt, *ethtypes.Tr return receipt, transaction } -// IsTxConfirmed returns true if there is a confirmed tx for 'nonce' -func (ob *Observer) IsTxConfirmed(nonce uint64) bool { +// isTxConfirmed returns true if there is a confirmed tx for 'nonce' +func (ob *Observer) isTxConfirmed(nonce uint64) bool { ob.Mu().Lock() defer ob.Mu().Unlock() id := ob.OutboundID(nonce) @@ -152,8 +125,8 @@ func (ob *Observer) IsTxConfirmed(nonce uint64) bool { return ob.outboundConfirmedReceipts[id] != nil && ob.outboundConfirmedTransactions[id] != nil } -// CheckTxInclusion returns nil only if tx is included at the position indicated by the receipt ([block, index]) -func (ob *Observer) CheckTxInclusion(ctx context.Context, tx *ethtypes.Transaction, receipt *ethtypes.Receipt) error { +// checkTxInclusion returns nil only if tx is included at the position indicated by the receipt ([block, index]) +func (ob *Observer) checkTxInclusion(ctx context.Context, tx *ethtypes.Transaction, receipt *ethtypes.Receipt) error { block, err := ob.GetBlockByNumberCached(ctx, receipt.BlockNumber.Uint64()) if err != nil { return errors.Wrapf(err, "GetBlockByNumberCached error for block %d txHash %s nonce %d", @@ -168,7 +141,7 @@ func (ob *Observer) CheckTxInclusion(ctx context.Context, tx *ethtypes.Transacti txAtIndex := block.Transactions[receipt.TransactionIndex] if !strings.EqualFold(txAtIndex.Hash, tx.Hash().Hex()) { - ob.RemoveCachedBlock(receipt.BlockNumber.Uint64()) // clean stale block from cache + ob.removeCachedBlock(receipt.BlockNumber.Uint64()) // clean stale block from cache return fmt.Errorf("transaction at index %d has different hash %s, txHash %s nonce %d block %d", receipt.TransactionIndex, txAtIndex.Hash, tx.Hash(), tx.Nonce(), receipt.BlockNumber.Uint64()) } @@ -176,8 +149,8 @@ func (ob *Observer) CheckTxInclusion(ctx context.Context, tx *ethtypes.Transacti return nil } -// TransactionByHash query transaction by hash via JSON-RPC -func (ob *Observer) TransactionByHash(ctx context.Context, txHash string) (*client.Transaction, bool, error) { +// transactionByHash query transaction by hash via JSON-RPC +func (ob *Observer) transactionByHash(ctx context.Context, txHash string) (*client.Transaction, bool, error) { tx, err := ob.evmClient.TransactionByHashCustom(ctx, txHash) if err != nil { return nil, false, err @@ -189,10 +162,6 @@ func (ob *Observer) TransactionByHash(ctx context.Context, txHash string) (*clie return tx, tx.BlockNumber == nil, nil } -func (ob *Observer) TransactionReceipt(ctx context.Context, hash ethcommon.Hash) (*ethtypes.Receipt, error) { - return ob.evmClient.TransactionReceipt(ctx, hash) -} - // GetBlockByNumberCached get block by number from cache // returns block, ethrpc.Block, isFallback, isSkip, error func (ob *Observer) GetBlockByNumberCached(ctx context.Context, blockNumber uint64) (*client.Block, error) { @@ -206,7 +175,7 @@ func (ob *Observer) GetBlockByNumberCached(ctx context.Context, blockNumber uint return nil, fmt.Errorf("block number %d is too large", blockNumber) } // #nosec G115 always in range, checked above - block, err := ob.BlockByNumber(ctx, int(blockNumber)) + block, err := ob.blockByNumber(ctx, int(blockNumber)) if err != nil { return nil, err } @@ -214,13 +183,13 @@ func (ob *Observer) GetBlockByNumberCached(ctx context.Context, blockNumber uint return block, nil } -// RemoveCachedBlock remove block from cache -func (ob *Observer) RemoveCachedBlock(blockNumber uint64) { +// removeCachedBlock remove block from cache +func (ob *Observer) removeCachedBlock(blockNumber uint64) { ob.BlockCache().Remove(blockNumber) } -// BlockByNumber query block by number via JSON-RPC -func (ob *Observer) BlockByNumber(ctx context.Context, blockNumber int) (*client.Block, error) { +// blockByNumber query block by number via JSON-RPC +func (ob *Observer) blockByNumber(ctx context.Context, blockNumber int) (*client.Block, error) { block, err := ob.evmClient.BlockByNumberCustom(ctx, big.NewInt(int64(blockNumber))) if err != nil { return nil, err @@ -237,9 +206,9 @@ func (ob *Observer) BlockByNumber(ctx context.Context, blockNumber int) (*client return block, nil } -// LoadLastBlockScanned loads the last scanned block from the database +// loadLastBlockScanned loads the last scanned block from the database // TODO(revamp): move to a db file -func (ob *Observer) LoadLastBlockScanned(ctx context.Context) error { +func (ob *Observer) loadLastBlockScanned(ctx context.Context) error { err := ob.Observer.LoadLastBlockScanned(ob.Logger().Chain) if err != nil { return errors.Wrapf(err, "error LoadLastBlockScanned for chain %d", ob.Chain().ChainId) diff --git a/zetaclient/chains/evm/observer/observer_gas_test.go b/zetaclient/chains/evm/observer/observer_gas_test.go index 4adb63533c..513a6772ad 100644 --- a/zetaclient/chains/evm/observer/observer_gas_test.go +++ b/zetaclient/chains/evm/observer/observer_gas_test.go @@ -1,4 +1,4 @@ -package observer_test +package observer import ( "context" diff --git a/zetaclient/chains/evm/observer/observer_test.go b/zetaclient/chains/evm/observer/observer_test.go index e3b3f1512c..dbf375d317 100644 --- a/zetaclient/chains/evm/observer/observer_test.go +++ b/zetaclient/chains/evm/observer/observer_test.go @@ -1,4 +1,4 @@ -package observer_test +package observer import ( "context" @@ -23,7 +23,6 @@ import ( crosschaintypes "github.com/zeta-chain/node/x/crosschain/types" observertypes "github.com/zeta-chain/node/x/observer/types" "github.com/zeta-chain/node/zetaclient/chains/base" - "github.com/zeta-chain/node/zetaclient/chains/evm/observer" "github.com/zeta-chain/node/zetaclient/chains/interfaces" "github.com/zeta-chain/node/zetaclient/config" "github.com/zeta-chain/node/zetaclient/metrics" @@ -195,7 +194,7 @@ func Test_NewObserver(t *testing.T) { tt.logger, ) require.NoError(t, err) - ob, err := observer.New(baseObserver, tt.evmClient) + ob, err := New(baseObserver, tt.evmClient) // check result if tt.fail { @@ -220,7 +219,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { ob.WriteLastBlockScannedToDB(123) // load last block scanned - err := ob.LoadLastBlockScanned(ctx) + err := ob.loadLastBlockScanned(ctx) require.NoError(t, err) require.EqualValues(t, 123, ob.LastBlockScanned()) }) @@ -231,7 +230,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { defer os.Unsetenv(envvar) // load last block scanned - err := ob.LoadLastBlockScanned(ctx) + err := ob.loadLastBlockScanned(ctx) require.ErrorContains(t, err, "error LoadLastBlockScanned") }) t.Run("should fail on RPC error", func(t *testing.T) { @@ -246,7 +245,7 @@ func Test_LoadLastBlockScanned(t *testing.T) { obOther.evmMock.On("BlockNumber", mock.Anything).Return(uint64(0), fmt.Errorf("error RPC")) // load last block scanned - err := obOther.LoadLastBlockScanned(ctx) + err := obOther.loadLastBlockScanned(ctx) require.ErrorContains(t, err, "error RPC") }) } @@ -289,7 +288,7 @@ func Test_BlockCache(t *testing.T) { // delete non-existing block should not panic blockNumber := uint64(123) - ts.RemoveCachedBlock(blockNumber) + ts.removeCachedBlock(blockNumber) // add a block block := &client.Block{Number: 123} @@ -301,7 +300,7 @@ func Test_BlockCache(t *testing.T) { require.EqualValues(t, block, result) // delete the block should not panic - ts.RemoveCachedBlock(blockNumber) + ts.removeCachedBlock(blockNumber) }) } @@ -325,7 +324,7 @@ func Test_CheckTxInclusion(t *testing.T) { ts.BlockCache().Add(blockNumber, block) t.Run("should pass for archived outbound", func(t *testing.T) { - err := ts.CheckTxInclusion(ts.ctx, tx, receipt) + err := ts.checkTxInclusion(ts.ctx, tx, receipt) require.NoError(t, err) }) t.Run("should fail on tx index out of range", func(t *testing.T) { @@ -333,7 +332,7 @@ func Test_CheckTxInclusion(t *testing.T) { copyReceipt := *receipt // #nosec G115 non negative value copyReceipt.TransactionIndex = uint(len(block.Transactions)) - err := ts.CheckTxInclusion(ts.ctx, tx, ©Receipt) + err := ts.checkTxInclusion(ts.ctx, tx, ©Receipt) require.ErrorContains(t, err, "out of range") }) t.Run("should fail on tx hash mismatch", func(t *testing.T) { @@ -343,7 +342,7 @@ func Test_CheckTxInclusion(t *testing.T) { ts.BlockCache().Add(blockNumber, block) // check inclusion should fail - err := ts.CheckTxInclusion(ts.ctx, tx, receipt) + err := ts.checkTxInclusion(ts.ctx, tx, receipt) require.ErrorContains(t, err, "has different hash") // wrong block should be removed from cache @@ -384,7 +383,7 @@ func Test_VoteOutboundBallot(t *testing.T) { } type testSuite struct { - *observer.Observer + *Observer ctx context.Context appContext *zctx.AppContext chainParams *observertypes.ChainParams @@ -433,7 +432,7 @@ func newTestSuite(t *testing.T, opts ...func(*testSuiteConfig)) *testSuite { baseObserver, err := base.NewObserver(chain, chainParams, zetacore, tss, 1000, nil, database, logger) require.NoError(t, err) - ob, err := observer.New(baseObserver, evmMock) + ob, err := New(baseObserver, evmMock) require.NoError(t, err) ob.WithLastBlock(1) diff --git a/zetaclient/chains/evm/observer/outbound.go b/zetaclient/chains/evm/observer/outbound.go index c72975a7f5..a6e6b0a500 100644 --- a/zetaclient/chains/evm/observer/outbound.go +++ b/zetaclient/chains/evm/observer/outbound.go @@ -50,7 +50,7 @@ func (ob *Observer) ProcessOutboundTrackers(ctx context.Context) error { for _, tracker := range trackers { // go to next tracker if this one already has a confirmed tx nonce := tracker.Nonce - if ob.IsTxConfirmed(nonce) { + if ob.isTxConfirmed(nonce) { continue } @@ -73,7 +73,7 @@ func (ob *Observer) ProcessOutboundTrackers(ctx context.Context) error { // should be only one txHash confirmed for each nonce. if txCount == 1 { - ob.SetTxNReceipt(nonce, outboundReceipt, outbound) + ob.setTxNReceipt(nonce, outboundReceipt, outbound) } else if txCount > 1 { // should not happen. We can't tell which txHash is true. It might happen (e.g. bug, glitchy/hacked endpoint) ob.Logger().Outbound.Error().Msgf("WatchOutbound: confirmed multiple (%d) outbound for chain %d nonce %d", txCount, chainID, nonce) @@ -87,8 +87,8 @@ func (ob *Observer) ProcessOutboundTrackers(ctx context.Context) error { return nil } -// PostVoteOutbound posts vote to zetacore for the confirmed outbound -func (ob *Observer) PostVoteOutbound( +// postVoteOutbound posts vote to zetacore for the confirmed outbound +func (ob *Observer) postVoteOutbound( ctx context.Context, cctxIndex string, receipt *ethtypes.Receipt, @@ -156,27 +156,27 @@ func (ob *Observer) VoteOutboundIfConfirmed( ) (bool, error) { // skip if outbound is not confirmed nonce := cctx.GetCurrentOutboundParam().TssNonce - if !ob.IsTxConfirmed(nonce) { + if !ob.isTxConfirmed(nonce) { return true, nil } - receipt, transaction := ob.GetTxNReceipt(nonce) + receipt, transaction := ob.getTxNReceipt(nonce) sendID := fmt.Sprintf("%d-%d", ob.Chain().ChainId, nonce) logger := ob.Logger().Outbound.With().Str("sendID", sendID).Logger() // get connector and erc20Custody contracts - connectorAddr, connector, err := ob.GetConnectorContract() + connectorAddr, connector, err := ob.getConnectorContract() if err != nil { return true, errors.Wrapf(err, "error getting zeta connector for chain %d", ob.Chain().ChainId) } - custodyAddr, custody, err := ob.GetERC20CustodyContract() + custodyAddr, custody, err := ob.getERC20CustodyContract() if err != nil { return true, errors.Wrapf(err, "error getting erc20 custody for chain %d", ob.Chain().ChainId) } - gatewayAddr, gateway, err := ob.GetGatewayContract() + gatewayAddr, gateway, err := ob.getGatewayContract() if err != nil { return true, errors.Wrap(err, "error getting gateway for chain") } - _, custodyV2, err := ob.GetERC20CustodyV2Contract() + _, custodyV2, err := ob.getERC20CustodyV2Contract() if err != nil { return true, errors.Wrapf(err, "error getting erc20 custody v2 for chain %d", ob.Chain().ChainId) } @@ -194,7 +194,7 @@ func (ob *Observer) VoteOutboundIfConfirmed( if receipt.Status == ethtypes.ReceiptStatusSuccessful { receiveStatus = chains.ReceiveStatus_success } - ob.PostVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + ob.postVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) return false, nil } @@ -220,7 +220,7 @@ func (ob *Observer) VoteOutboundIfConfirmed( } // post vote to zetacore - ob.PostVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) + ob.postVoteOutbound(ctx, cctx.Index, receipt, transaction, receiveValue, receiveStatus, nonce, cointype, logger) return false, nil } @@ -260,7 +260,7 @@ func parseOutboundReceivedValue( switch cointype { case coin.CoinType_Zeta: if receipt.Status == ethtypes.ReceiptStatusSuccessful { - receivedLog, revertedLog, err := ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) if err != nil { return nil, chains.ReceiveStatus_failed, err } @@ -273,7 +273,7 @@ func parseOutboundReceivedValue( } case coin.CoinType_ERC20: if receipt.Status == ethtypes.ReceiptStatusSuccessful { - withdrawn, err := ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) if err != nil { return nil, chains.ReceiveStatus_failed, err } @@ -288,9 +288,9 @@ func parseOutboundReceivedValue( return receiveValue, receiveStatus, nil } -// ParseAndCheckZetaEvent parses and checks ZetaReceived/ZetaReverted event from the outbound receipt +// 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( +func parseAndCheckZetaEvent( cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, connectorAddr ethcommon.Address, @@ -347,8 +347,8 @@ func ParseAndCheckZetaEvent( return nil, nil, errors.New("no ZetaReceived/ZetaReverted event found") } -// ParseAndCheckWithdrawnEvent parses and checks erc20 Withdrawn event from the outbound receipt -func ParseAndCheckWithdrawnEvent( +// parseAndCheckWithdrawnEvent parses and checks erc20 Withdrawn event from the outbound receipt +func parseAndCheckWithdrawnEvent( cctx *crosschaintypes.CrossChainTx, receipt *ethtypes.Receipt, custodyAddr ethcommon.Address, @@ -380,16 +380,16 @@ func ParseAndCheckWithdrawnEvent( return nil, errors.New("no ERC20 Withdrawn event found") } -// FilterTSSOutbound filters the outbounds from TSS address to supplement outbound trackers -func (ob *Observer) FilterTSSOutbound(ctx context.Context, startBlock, toBlock uint64) { +// filterTSSOutbound filters the outbounds from TSS address to supplement outbound trackers +func (ob *Observer) filterTSSOutbound(ctx context.Context, startBlock, toBlock uint64) { // filters the outbounds from TSS address block by block for bn := startBlock; bn <= toBlock; bn++ { - ob.FilterTSSOutboundInBlock(ctx, bn) + ob.filterTSSOutboundInBlock(ctx, bn) } } -// FilterTSSOutboundInBlock filters the outbounds in a single block to supplement outbound trackers -func (ob *Observer) FilterTSSOutboundInBlock(ctx context.Context, blockNumber uint64) { +// filterTSSOutboundInBlock filters the outbounds in a single block to supplement outbound trackers +func (ob *Observer) filterTSSOutboundInBlock(ctx context.Context, blockNumber uint64) { // query block and ignore error (we don't rescan as we are only supplementing outbound trackers) block, err := ob.GetBlockByNumberCached(ctx, blockNumber) if err != nil { @@ -405,9 +405,9 @@ func (ob *Observer) FilterTSSOutboundInBlock(ctx context.Context, blockNumber ui if ethcommon.HexToAddress(tx.From) == ob.TSS().PubKey().AddressEVM() { // #nosec G115 nonce always positive nonce := uint64(tx.Nonce) - if !ob.IsTxConfirmed(nonce) { + if !ob.isTxConfirmed(nonce) { if receipt, txx, ok := ob.checkConfirmedTx(ctx, tx.Hash, nonce); ok { - ob.SetTxNReceipt(nonce, receipt, txx) + ob.setTxNReceipt(nonce, receipt, txx) ob.Logger(). Outbound.Info(). Msgf("TSS outbound detected on chain %d nonce %d tx %s", ob.Chain().ChainId, nonce, tx.Hash) @@ -491,7 +491,7 @@ func (ob *Observer) checkConfirmedTx( // cross-check tx inclusion against the block // Note: a guard for false BlockNumber in receipt. The blob-carrying tx won't come here - err = ob.CheckTxInclusion(ctx, transaction, receipt) + err = ob.checkTxInclusion(ctx, transaction, receipt) if err != nil { logger.Error().Err(err).Msg("CheckTxInclusion error") return nil, nil, false diff --git a/zetaclient/chains/evm/observer/outbound_test.go b/zetaclient/chains/evm/observer/outbound_test.go index 750e7d09f9..0bc109964a 100644 --- a/zetaclient/chains/evm/observer/outbound_test.go +++ b/zetaclient/chains/evm/observer/outbound_test.go @@ -1,4 +1,4 @@ -package observer_test +package observer import ( "context" @@ -12,7 +12,6 @@ import ( "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" "github.com/zeta-chain/node/testutil/sample" - "github.com/zeta-chain/node/zetaclient/chains/evm/observer" "github.com/zeta-chain/node/zetaclient/config" "github.com/zeta-chain/node/zetaclient/testutils" "github.com/zeta-chain/node/zetaclient/testutils/mocks" @@ -48,7 +47,7 @@ func Test_IsOutboundProcessed(t *testing.T) { t.Run("should post vote and return true if outbound is processed", func(t *testing.T) { // create evm observer and set outbound and receipt ob := newTestSuite(t) - ob.SetTxNReceipt(nonce, receipt, outbound) + ob.setTxNReceipt(nonce, receipt, outbound) // post outbound vote continueKeysign, err := ob.VoteOutboundIfConfirmed(ctx, cctx) @@ -64,14 +63,14 @@ func Test_IsOutboundProcessed(t *testing.T) { // create evm observer and set outbound and receipt ob := newTestSuite(t) - ob.SetTxNReceipt(nonce, receipt, outbound) + ob.setTxNReceipt(nonce, receipt, outbound) // modify compliance config to restrict sender address cfg := config.Config{ ComplianceConfig: config.ComplianceConfig{}, } cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // post outbound vote continueKeysign, err := ob.VoteOutboundIfConfirmed(ctx, cctx) @@ -88,7 +87,7 @@ func Test_IsOutboundProcessed(t *testing.T) { t.Run("should fail if unable to parse ZetaReceived event", func(t *testing.T) { // create evm observer and set outbound and receipt ob := newTestSuite(t) - ob.SetTxNReceipt(nonce, receipt, outbound) + ob.setTxNReceipt(nonce, receipt, outbound) // set connector contract address to an arbitrary address to make event parsing fail chainParamsNew := ob.ChainParams() @@ -135,7 +134,7 @@ func Test_IsOutboundProcessed_ContractError(t *testing.T) { t.Run("should fail if unable to get connector/custody contract", func(t *testing.T) { // create evm observer and set outbound and receipt ob := newTestSuite(t) - ob.SetTxNReceipt(nonce, receipt, outbound) + ob.setTxNReceipt(nonce, receipt, outbound) abiConnector := zetaconnector.ZetaConnectorNonEthMetaData.ABI abiCustody := erc20custody.ERC20CustodyMetaData.ABI @@ -179,7 +178,7 @@ func Test_PostVoteOutbound(t *testing.T) { // create evm client using mock zetacore client and post outbound vote ob := newTestSuite(t) - ob.PostVoteOutbound( + ob.postVoteOutbound( ctx, cctx.Index, receipt, @@ -212,7 +211,7 @@ func Test_ParseZetaReceived(t *testing.T) { ) t.Run("should parse ZetaReceived event from archived outbound receipt", func(t *testing.T) { - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) require.NoError(t, err) require.NotNil(t, receivedLog) require.Nil(t, revertedLog) @@ -220,7 +219,7 @@ func Test_ParseZetaReceived(t *testing.T) { t.Run("should fail on connector address mismatch", func(t *testing.T) { // use an arbitrary address to make validation fail fakeConnectorAddress := sample.EthAddress() - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) require.ErrorContains(t, err, "error validating ZetaReceived event") require.Nil(t, revertedLog) require.Nil(t, receivedLog) @@ -229,7 +228,7 @@ func Test_ParseZetaReceived(t *testing.T) { // load cctx and set receiver address to an arbitrary address fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeCctx.GetCurrentOutboundParam().Receiver = sample.EthAddress().Hex() - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "receiver address mismatch") require.Nil(t, revertedLog) require.Nil(t, receivedLog) @@ -239,14 +238,14 @@ func Test_ParseZetaReceived(t *testing.T) { fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutboundParam().Amount.Uint64()-1) fakeCctx.GetCurrentOutboundParam().Amount = fakeAmount - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "amount mismatch") require.Nil(t, revertedLog) require.Nil(t, receivedLog) }) t.Run("should fail on cctx index mismatch", func(t *testing.T) { cctx.Index = sample.Hash().Hex() // use an arbitrary index - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "cctx index mismatch") require.Nil(t, revertedLog) require.Nil(t, receivedLog) @@ -262,7 +261,7 @@ func Test_ParseZetaReceived(t *testing.T) { testutils.EventZetaReceived, ) receipt.Logs = receipt.Logs[:1] // the 2nd log is ZetaReceived event - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "no ZetaReceived/ZetaReverted event") require.Nil(t, revertedLog) require.Nil(t, receivedLog) @@ -287,7 +286,7 @@ func Test_ParseZetaReverted(t *testing.T) { ) t.Run("should parse ZetaReverted event from archived outbound receipt", func(t *testing.T) { - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) require.NoError(t, err) require.Nil(t, receivedLog) require.NotNil(t, revertedLog) @@ -295,7 +294,7 @@ func Test_ParseZetaReverted(t *testing.T) { t.Run("should fail on connector address mismatch", func(t *testing.T) { // use an arbitrary address to make validation fail fakeConnectorAddress := sample.EthAddress() - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, fakeConnectorAddress, connector) require.ErrorContains(t, err, "error validating ZetaReverted event") require.Nil(t, receivedLog) require.Nil(t, revertedLog) @@ -304,7 +303,7 @@ func Test_ParseZetaReverted(t *testing.T) { // load cctx and set receiver address to an arbitrary address fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeCctx.InboundParams.Sender = sample.EthAddress().Hex() // the receiver is the sender for reverted ccxt - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "receiver address mismatch") require.Nil(t, revertedLog) require.Nil(t, receivedLog) @@ -314,14 +313,14 @@ func Test_ParseZetaReverted(t *testing.T) { fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutboundParam().Amount.Uint64()-1) fakeCctx.GetCurrentOutboundParam().Amount = fakeAmount - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(fakeCctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "amount mismatch") require.Nil(t, revertedLog) require.Nil(t, receivedLog) }) t.Run("should fail on cctx index mismatch", func(t *testing.T) { cctx.Index = sample.Hash().Hex() // use an arbitrary index to make validation fail - receivedLog, revertedLog, err := observer.ParseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) + receivedLog, revertedLog, err := parseAndCheckZetaEvent(cctx, receipt, connectorAddress, connector) require.ErrorContains(t, err, "cctx index mismatch") require.Nil(t, receivedLog) require.Nil(t, revertedLog) @@ -346,14 +345,14 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { ) t.Run("should parse ERC20 Withdrawn event from archived outbound receipt", func(t *testing.T) { - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) require.NoError(t, err) require.NotNil(t, withdrawn) }) t.Run("should fail on erc20 custody address mismatch", func(t *testing.T) { // use an arbitrary address to make validation fail fakeCustodyAddress := sample.EthAddress() - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(cctx, receipt, fakeCustodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(cctx, receipt, fakeCustodyAddress, custody) require.ErrorContains(t, err, "error validating Withdrawn event") require.Nil(t, withdrawn) }) @@ -361,7 +360,7 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { // load cctx and set receiver address to an arbitrary address fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeCctx.GetCurrentOutboundParam().Receiver = sample.EthAddress().Hex() - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) require.ErrorContains(t, err, "receiver address mismatch") require.Nil(t, withdrawn) }) @@ -369,7 +368,7 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { // load cctx and set asset to an arbitrary address fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeCctx.InboundParams.Asset = sample.EthAddress().Hex() - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) require.ErrorContains(t, err, "asset mismatch") require.Nil(t, withdrawn) }) @@ -378,7 +377,7 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { fakeCctx := testutils.LoadCctxByNonce(t, chainID, nonce) fakeAmount := sample.UintInRange(0, fakeCctx.GetCurrentOutboundParam().Amount.Uint64()-1) fakeCctx.GetCurrentOutboundParam().Amount = fakeAmount - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(fakeCctx, receipt, custodyAddress, custody) require.ErrorContains(t, err, "amount mismatch") require.Nil(t, withdrawn) }) @@ -393,7 +392,7 @@ func Test_ParseERC20WithdrawnEvent(t *testing.T) { testutils.EventERC20Withdraw, ) receipt.Logs = receipt.Logs[:1] // the 2nd log is Withdrawn event - withdrawn, err := observer.ParseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) + withdrawn, err := parseAndCheckWithdrawnEvent(cctx, receipt, custodyAddress, custody) require.ErrorContains(t, err, "no ERC20 Withdrawn event") require.Nil(t, withdrawn) }) @@ -431,14 +430,14 @@ func Test_FilterTSSOutbound(t *testing.T) { ob.WithLastBlock(blockNumber + confirmations - 1) // filter TSS outbound - ob.FilterTSSOutbound(ctx, blockNumber, blockNumber) + ob.filterTSSOutbound(ctx, blockNumber, blockNumber) // tx should be confirmed after filtering - found := ob.IsTxConfirmed(outboundNonce) + found := ob.isTxConfirmed(outboundNonce) require.True(t, found) // retrieve tx and receipt - receipt, tx = ob.GetTxNReceipt(outboundNonce) + receipt, tx = ob.getTxNReceipt(outboundNonce) require.NotNil(t, tx) require.NotNil(t, receipt) require.Equal(t, outboundHash, tx.Hash()) @@ -451,10 +450,10 @@ func Test_FilterTSSOutbound(t *testing.T) { ob.evmMock.On("BlockByNumberCustom", mock.Anything, mock.Anything).Return(nil, errors.New("rpc error")) // filter TSS outbound - ob.FilterTSSOutbound(ctx, blockNumber, blockNumber) + ob.filterTSSOutbound(ctx, blockNumber, blockNumber) // tx should be confirmed after filtering - found := ob.IsTxConfirmed(outboundNonce) + found := ob.isTxConfirmed(outboundNonce) require.False(t, found) }) } @@ -566,7 +565,7 @@ func Test_FilterTSSOutbound(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 +// coinType := coin.FungibleTokenCoinType(5) // unknown coin type // cctx, outbound, receipt := testutils.LoadEVMCctxNOutboundNReceipt( // t, // TestDataDir, diff --git a/zetaclient/chains/evm/observer/v2_inbound.go b/zetaclient/chains/evm/observer/v2_inbound.go index 04126364ae..69cdc8fb83 100644 --- a/zetaclient/chains/evm/observer/v2_inbound.go +++ b/zetaclient/chains/evm/observer/v2_inbound.go @@ -8,8 +8,8 @@ import ( "cosmossdk.io/errors" sdkmath "cosmossdk.io/math" - "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/zeta-chain/protocol-contracts/pkg/gatewayevm.sol" "github.com/zeta-chain/node/pkg/coin" @@ -20,7 +20,6 @@ import ( "github.com/zeta-chain/node/zetaclient/compliance" "github.com/zeta-chain/node/zetaclient/config" "github.com/zeta-chain/node/zetaclient/logs" - "github.com/zeta-chain/node/zetaclient/metrics" "github.com/zeta-chain/node/zetaclient/zetacore" ) @@ -59,37 +58,22 @@ func (ob *Observer) isEventProcessable( return true } -// ObserveGatewayDeposit queries the gateway contract for deposit events +// 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) { +func (ob *Observer) observeGatewayDeposit( + ctx context.Context, + startBlock, toBlock uint64, + rawLogs []ethtypes.Log, +) (uint64, error) { // filter ERC20CustodyDeposited logs - gatewayAddr, gatewayContract, err := ob.GetGatewayContract() + 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() + events := ob.parseAndValidateDepositEvents(rawLogs, gatewayAddr, gatewayContract) // post to zetacore lastScanned := uint64(0) @@ -124,22 +108,26 @@ func (ob *Observer) ObserveGatewayDeposit(ctx context.Context, startBlock, toBlo // parseAndValidateDepositEvents collects and sorts events by block number, tx index, and log index func (ob *Observer) parseAndValidateDepositEvents( - iterator *gatewayevm.GatewayEVMDepositedIterator, + rawLogs []ethtypes.Log, gatewayAddr ethcommon.Address, + gatewayContract *gatewayevm.GatewayEVM, ) []*gatewayevm.GatewayEVMDeposited { - // collect and sort validEvents by block number, then tx index, then log index (ascending) validEvents := make([]*gatewayevm.GatewayEVMDeposited, 0) - for iterator.Next() { - err := common.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", common.TopicsGatewayDeposit) - if err == nil { - validEvents = append(validEvents, iterator.Event) + for _, log := range rawLogs { + err := common.ValidateEvmTxLog(&log, gatewayAddr, "", common.TopicsGatewayDeposit) + if err != nil { + continue + } + depositedEvent, err := gatewayContract.ParseDeposited(log) + if err != nil { + ob.Logger(). + Inbound.Warn(). + Stringer(logs.FieldTx, log.TxHash). + Uint64(logs.FieldBlock, log.BlockNumber). + Msg("invalid Deposited event") continue } - ob.Logger(). - Inbound.Warn(). - Stringer(logs.FieldTx, iterator.Event.Raw.TxHash). - Uint64(logs.FieldBlock, iterator.Event.Raw.BlockNumber). - Msg("invalid Deposited event") + validEvents = append(validEvents, depositedEvent) } // order events by height, tx index and event index (ascending) @@ -211,50 +199,29 @@ func (ob *Observer) newDepositInboundVote(event *gatewayevm.GatewayEVMDeposited) ) } -// ObserveGatewayCall queries the gateway contract for call events +// 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() +func (ob *Observer) observeGatewayCall( + ctx context.Context, + startBlock, toBlock uint64, + rawLogs []ethtypes.Log, +) (uint64, error) { + 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 + events := ob.parseAndValidateCallEvents(rawLogs, gatewayAddr, gatewayContract) 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.isEventProcessable(event.Sender, event.Receiver, event.Raw.TxHash, event.Payload) { continue } @@ -268,33 +235,35 @@ func (ob *Observer) ObserveGatewayCall(ctx context.Context, startBlock, toBlock _, 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, + rawLogs []ethtypes.Log, gatewayAddr ethcommon.Address, + gatewayContract *gatewayevm.GatewayEVM, ) []*gatewayevm.GatewayEVMCalled { - // collect and sort validEvents by block number, then tx index, then log index (ascending) validEvents := make([]*gatewayevm.GatewayEVMCalled, 0) - for iterator.Next() { - err := common.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", common.TopicsGatewayCall) - if err == nil { - validEvents = append(validEvents, iterator.Event) + for _, log := range rawLogs { + err := common.ValidateEvmTxLog(&log, gatewayAddr, "", common.TopicsGatewayCall) + if err != nil { + continue + } + calledEvent, err := gatewayContract.ParseCalled(log) + if err != nil { + ob.Logger(). + Inbound.Warn(). + Stringer(logs.FieldTx, log.TxHash). + Uint64(logs.FieldBlock, log.BlockNumber). + Msg("invalid Called event") continue } - ob.Logger(). - Inbound.Warn(). - Stringer(logs.FieldTx, iterator.Event.Raw.TxHash). - Uint64(logs.FieldBlock, iterator.Event.Raw.BlockNumber). - Msg("invalid Called event") + validEvents = append(validEvents, calledEvent) } // order events by height, tx index and event index (ascending) @@ -350,38 +319,21 @@ func (ob *Observer) newCallInboundVote(event *gatewayevm.GatewayEVMCalled) types ) } -// ObserveGatewayDepositAndCall queries the gateway contract for deposit and call events +// observeGatewayDepositAndCall queries the gateway contract for deposit and call events // returns the last block successfully scanned -func (ob *Observer) ObserveGatewayDepositAndCall(ctx context.Context, startBlock, toBlock uint64) (uint64, error) { - gatewayAddr, gatewayContract, err := ob.GetGatewayContract() +func (ob *Observer) observeGatewayDepositAndCall( + ctx context.Context, + startBlock, toBlock uint64, + rawLogs []ethtypes.Log, +) (uint64, error) { + 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.FilterDepositedAndCalled(&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.parseAndValidateDepositAndCallEvents(eventIterator, gatewayAddr) - - // increment prom counter - metrics.GetFilterLogsPerChain.WithLabelValues(ob.Chain().Name).Inc() + events := ob.parseAndValidateDepositAndCallEvents(rawLogs, gatewayAddr, gatewayContract) - // 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) @@ -414,22 +366,27 @@ func (ob *Observer) ObserveGatewayDepositAndCall(ctx context.Context, startBlock // parseAndValidateDepositAndCallEvents collects and sorts events by block number, tx index, and log index func (ob *Observer) parseAndValidateDepositAndCallEvents( - iterator *gatewayevm.GatewayEVMDepositedAndCalledIterator, + rawLogs []ethtypes.Log, gatewayAddr ethcommon.Address, + gatewayContract *gatewayevm.GatewayEVM, ) []*gatewayevm.GatewayEVMDepositedAndCalled { // collect and sort validEvents by block number, then tx index, then log index (ascending) validEvents := make([]*gatewayevm.GatewayEVMDepositedAndCalled, 0) - for iterator.Next() { - err := common.ValidateEvmTxLog(&iterator.Event.Raw, gatewayAddr, "", common.TopicsGatewayDepositAndCall) - if err == nil { - validEvents = append(validEvents, iterator.Event) + for _, log := range rawLogs { + err := common.ValidateEvmTxLog(&log, gatewayAddr, "", common.TopicsGatewayDepositAndCall) + if err != nil { + continue + } + depositAndCallEvent, err := gatewayContract.ParseDepositedAndCalled(log) + if err != nil { + ob.Logger(). + Inbound.Warn(). + Stringer(logs.FieldTx, log.TxHash). + Uint64(logs.FieldBlock, log.BlockNumber). + Msg("invalid DepositedAndCalled event") continue } - ob.Logger(). - Inbound.Warn(). - Stringer(logs.FieldTx, iterator.Event.Raw.TxHash). - Uint64(logs.FieldBlock, iterator.Event.Raw.BlockNumber). - Msg("invalid DepositedAndCalled event") + validEvents = append(validEvents, depositAndCallEvent) } // order events by height, tx index and event index (ascending) diff --git a/zetaclient/chains/evm/observer/v2_inbound_tracker.go b/zetaclient/chains/evm/observer/v2_inbound_tracker.go index af7cb7c19f..f6df049f96 100644 --- a/zetaclient/chains/evm/observer/v2_inbound_tracker.go +++ b/zetaclient/chains/evm/observer/v2_inbound_tracker.go @@ -24,7 +24,7 @@ func (ob *Observer) ProcessInboundTrackerV2( tx *client.Transaction, receipt *ethtypes.Receipt, ) error { - gatewayAddr, gateway, err := ob.GetGatewayContract() + gatewayAddr, gateway, err := ob.getGatewayContract() if err != nil { ob.Logger().Inbound.Debug().Err(err).Msg("error getting gateway contract for processing inbound tracker") return ErrGatewayNotSet diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index 8c96410bd4..20827c6e3e 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -153,7 +153,7 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { // restrict sender cfg.ComplianceConfig.RestrictedAddresses = []string{sender} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) msg := ob.BuildInboundVoteMsgFromEvent(event) require.Nil(t, msg) @@ -173,7 +173,7 @@ func Test_IsEventProcessable(t *testing.T) { cfg := config.Config{ ComplianceConfig: sample.ComplianceConfig(), } - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // test cases tests := []struct { diff --git a/zetaclient/chains/solana/observer/outbound.go b/zetaclient/chains/solana/observer/outbound.go index c4153b1db6..21cd8bbf8d 100644 --- a/zetaclient/chains/solana/observer/outbound.go +++ b/zetaclient/chains/solana/observer/outbound.go @@ -109,6 +109,9 @@ func (ob *Observer) VoteOutboundIfConfirmed(ctx context.Context, cctx *crosschai // status was already verified as successful in CheckFinalizedTx outboundStatus := chains.ReceiveStatus_success + if inst.InstructionDiscriminator() == contracts.DiscriminatorIncrementNonce { + outboundStatus = chains.ReceiveStatus_failed + } // compliance check, special handling the cancelled cctx if compliance.IsCctxRestricted(cctx) { @@ -145,10 +148,14 @@ func (ob *Observer) PostVoteOutbound( // so we set retryGasLimit to 0 because the solana gateway withdrawal will always succeed // and the vote msg won't trigger ZEVM interaction const ( - gasLimit = zetacore.PostVoteOutboundGasLimit - retryGasLimit = 0 + gasLimit = zetacore.PostVoteOutboundGasLimit ) + var retryGasLimit uint64 + if msg.Status == chains.ReceiveStatus_failed { + retryGasLimit = zetacore.PostVoteOutboundRevertGasLimit + } + // post vote to zetacore zetaTxHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound(ctx, gasLimit, retryGasLimit, msg) if err != nil { @@ -301,6 +308,12 @@ func ParseGatewayInstruction( return nil, fmt.Errorf("programID %s is not matching gatewayID %s", programID, gatewayID) } + // first check if it was simple nonce increment instruction, which indicates that outbound failed + inst, err := contracts.ParseInstructionIncrementNonce(instruction) + if err == nil { + return inst, nil + } + // parse the outbound instruction switch coinType { case coin.CoinType_Gas: diff --git a/zetaclient/chains/solana/observer/outbound_test.go b/zetaclient/chains/solana/observer/outbound_test.go index 7c7a82e2c6..95a30472b7 100644 --- a/zetaclient/chains/solana/observer/outbound_test.go +++ b/zetaclient/chains/solana/observer/outbound_test.go @@ -43,6 +43,15 @@ const ( // withdrawSPLTxTest is local devnet tx result for testing withdrawSPLTxTest = "3NgoR4K9FJq7UunorPRGW9wpqMV8oNvZERejutd7bKmqh3CKEV5DMZndhZn7hQ1i4RhTyHXRWxtR5ZNVHmmjAUSF" + + // executeTxTest is local devnet tx result for testing + executeTxTest = "4ZuPTkYtBGDyDZNHKyHxEKL98VeaefAMUzmZVL2BrgwCvog7CqpjrRoegXDt6bD7w8dffGKGcDZqFYFi5vkAK8eo" + + // executeSPLTxTest is local devnet tx result for testing + executeSPLTxTest = "d3WvqtwFws9yftpxSrmwXqb48ZbBVjvxz34zY5Mo9TxaAPxsudPa68nDXZeShvK8UqtM84TgGfpdrgeX65q5WCW" + + // incrementNonceTxTest is local devnet tx result for testing + incrementNonceTxTest = "5dpFTsscUKCGVQzL9bAUSuEE6yLXaf7d1wMjZa7RLqvtSUtAdfcdxQHNsbfcS2Sfzu4zBVxMJC2KWzuaUUbg1ZGk" ) // createTestObserver creates a test observer for testing @@ -515,3 +524,219 @@ func Test_ParseInstructionWithdrawSPL(t *testing.T) { require.Nil(t, inst) }) } + +func Test_ParseInstructionExecute(t *testing.T) { + // the test chain and transaction hash + chain := chains.SolanaDevnet + txHash := executeTxTest + txAmount := uint64(1000000) + + t.Run("should parse instruction execute", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + // tss address used in local devnet + tssAddress := "0xF2eCA3Fd5a152eb5b9ceBcA7E492C668cA09Cdd3" + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + instruction := tx.Message.Instructions[0] + + // ACT + inst, err := contracts.ParseInstructionExecute(instruction) + require.NoError(t, err) + + // ASSERT + // check sender, nonce and amount + sender, err := inst.Signer() + require.NoError(t, err) + require.Equal(t, tssAddress, sender.String()) + require.EqualValues(t, inst.GatewayNonce(), 1) + require.EqualValues(t, inst.TokenAmount(), txAmount) + }) + + t.Run("should return error on invalid instruction data", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // set invalid instruction data + instruction := txFake.Message.Instructions[0] + instruction.Data = []byte("invalid instruction data") + + // ACT + inst, err := contracts.ParseInstructionExecute(instruction) + + // ASSERT + require.ErrorContains(t, err, "error deserializing instruction") + require.Nil(t, inst) + }) + + t.Run("should return error on discriminator mismatch", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // overwrite discriminator (first 8 bytes) + instruction := txFake.Message.Instructions[0] + fakeDiscriminator := "b712469c946da12100980d0000000000" + fakeDiscriminatorBytes, err := hex.DecodeString(fakeDiscriminator) + require.NoError(t, err) + copy(instruction.Data, fakeDiscriminatorBytes) + + // ACT + inst, err := contracts.ParseInstructionExecute(instruction) + + // ASSERT + require.ErrorContains(t, err, "not an execute instruction") + require.Nil(t, inst) + }) +} + +func Test_ParseInstructionExecuteSPL(t *testing.T) { + // the test chain and transaction hash + chain := chains.SolanaDevnet + txHash := executeSPLTxTest + txAmount := uint64(1000000) + + t.Run("should parse instruction execute SPL", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + // tss address used in local devnet + tssAddress := "0xF2eCA3Fd5a152eb5b9ceBcA7E492C668cA09Cdd3" + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + instruction := tx.Message.Instructions[0] + + // ACT + inst, err := contracts.ParseInstructionExecuteSPL(instruction) + require.NoError(t, err) + + // ASSERT + // check sender, nonce and amount + sender, err := inst.Signer() + require.NoError(t, err) + require.Equal(t, tssAddress, sender.String()) + require.EqualValues(t, inst.GatewayNonce(), 6) + require.EqualValues(t, inst.TokenAmount(), txAmount) + }) + + t.Run("should return error on invalid instruction data", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // set invalid instruction data + instruction := txFake.Message.Instructions[0] + instruction.Data = []byte("invalid instruction data") + + // ACT + inst, err := contracts.ParseInstructionExecuteSPL(instruction) + + // ASSERT + require.ErrorContains(t, err, "error deserializing instruction") + require.Nil(t, inst) + }) + + t.Run("should return error on discriminator mismatch", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // overwrite discriminator (first 8 bytes) + instruction := txFake.Message.Instructions[0] + fakeDiscriminator := "b712469c946da12100980d0000000000" + fakeDiscriminatorBytes, err := hex.DecodeString(fakeDiscriminator) + require.NoError(t, err) + copy(instruction.Data, fakeDiscriminatorBytes) + + // ACT + inst, err := contracts.ParseInstructionExecuteSPL(instruction) + + // ASSERT + require.ErrorContains(t, err, "not an execute_spl_token instruction") + require.Nil(t, inst) + }) +} + +func Test_ParseInstructionIncrementNonce(t *testing.T) { + // the test chain and transaction hash + chain := chains.SolanaDevnet + txHash := incrementNonceTxTest + txAmount := uint64(1000000) + + t.Run("should parse instruction increment nonce", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + // tss address used in local devnet + tssAddress := "0xF2eCA3Fd5a152eb5b9ceBcA7E492C668cA09Cdd3" + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + tx, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + instruction := tx.Message.Instructions[0] + + // ACT + inst, err := contracts.ParseInstructionIncrementNonce(instruction) + require.NoError(t, err) + + // ASSERT + // check sender, nonce and amount + sender, err := inst.Signer() + require.NoError(t, err) + require.Equal(t, tssAddress, sender.String()) + require.EqualValues(t, inst.GatewayNonce(), 2) + require.EqualValues(t, inst.TokenAmount(), txAmount) + }) + + t.Run("should return error on invalid instruction data", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // set invalid instruction data + instruction := txFake.Message.Instructions[0] + instruction.Data = []byte("invalid instruction data") + + // ACT + inst, err := contracts.ParseInstructionIncrementNonce(instruction) + + // ASSERT + require.ErrorContains(t, err, "error deserializing instruction") + require.Nil(t, inst) + }) + + t.Run("should return error on discriminator mismatch", func(t *testing.T) { + // ARRANGE + // load and unmarshal archived transaction + txResult := testutils.LoadSolanaOutboundTxResult(t, TestDataDir, chain.ChainId, txHash) + txFake, err := txResult.Transaction.GetTransaction() + require.NoError(t, err) + + // overwrite discriminator (first 8 bytes) + instruction := txFake.Message.Instructions[0] + fakeDiscriminator := "b712469c946da12100980d0000000000" + fakeDiscriminatorBytes, err := hex.DecodeString(fakeDiscriminator) + require.NoError(t, err) + copy(instruction.Data, fakeDiscriminatorBytes) + + // ACT + inst, err := contracts.ParseInstructionIncrementNonce(instruction) + + // ASSERT + require.ErrorContains(t, err, "not an increment_nonce instruction") + require.Nil(t, inst) + }) +} diff --git a/zetaclient/chains/solana/signer/execute.go b/zetaclient/chains/solana/signer/execute.go index 9ec45c2bb5..90d9cca513 100644 --- a/zetaclient/chains/solana/signer/execute.go +++ b/zetaclient/chains/solana/signer/execute.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/errors" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/zeta-chain/node/pkg/chains" @@ -55,8 +54,8 @@ func (signer *Signer) createAndSignMsgExecute( return msg.SetSignature(signature), nil } -// signExecuteTx wraps the execute 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) signExecuteTx(ctx context.Context, msg contracts.MsgExecute) (*solana.Transaction, error) { +// createExecuteInstruction wraps the execute 'msg' into a Solana instruction. +func (signer *Signer) createExecuteInstruction(msg contracts.MsgExecute) (*solana.GenericInstruction, error) { // create execute instruction with program call data dataBytes, err := borsh.Serialize(contracts.ExecuteInstructionParams{ Discriminator: contracts.DiscriminatorExecute, @@ -85,43 +84,11 @@ func (signer *Signer) signExecuteTx(ctx context.Context, msg contracts.MsgExecut } allAccounts := append(predefinedAccounts, msg.RemainingAccounts()...) - inst := solana.GenericInstruction{ + inst := &solana.GenericInstruction{ ProgID: signer.gatewayID, DataBytes: dataBytes, AccountValues: allAccounts, } - // get a recent blockhash - recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - return nil, errors.Wrap(err, "getLatestBlockhash error") - } - - // create a transaction that wraps the instruction - tx, err := solana.NewTransaction( - []solana.Instruction{ - // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget - // https://github.com/zeta-chain/node/issues/2599 - // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), - // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), - &inst}, - recent.Value.Blockhash, - solana.TransactionPayer(signer.relayerKey.PublicKey()), - ) - if err != nil { - return nil, errors.Wrap(err, "unable to create new tx") - } - - // relayer signs the transaction - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(signer.relayerKey.PublicKey()) { - return signer.relayerKey - } - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "signer unable to sign transaction") - } - - return tx, nil + return inst, nil } diff --git a/zetaclient/chains/solana/signer/execute_spl.go b/zetaclient/chains/solana/signer/execute_spl.go index 2336217660..0620f39517 100644 --- a/zetaclient/chains/solana/signer/execute_spl.go +++ b/zetaclient/chains/solana/signer/execute_spl.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/errors" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/zeta-chain/node/pkg/chains" @@ -85,11 +84,8 @@ func (signer *Signer) createAndSignMsgExecuteSPL( return msg.SetSignature(signature), nil } -// signExecuteSPLTx wraps the execute spl 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) signExecuteSPLTx( - ctx context.Context, - msg contracts.MsgExecuteSPL, -) (*solana.Transaction, error) { +// createExecuteSPLInstruction wraps the execute spl 'msg' into a Solana instruction. +func (signer *Signer) createExecuteSPLInstruction(msg contracts.MsgExecuteSPL) (*solana.GenericInstruction, error) { // create execute spl instruction with program call data dataBytes, err := borsh.Serialize(contracts.ExecuteSPLInstructionParams{ Discriminator: contracts.DiscriminatorExecuteSPL, @@ -130,42 +126,11 @@ func (signer *Signer) signExecuteSPLTx( } allAccounts := append(predefinedAccounts, msg.RemainingAccounts()...) - inst := solana.GenericInstruction{ + inst := &solana.GenericInstruction{ ProgID: signer.gatewayID, DataBytes: dataBytes, AccountValues: allAccounts, } - // get a recent blockhash - recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - return nil, errors.Wrap(err, "GetLatestBlockhash error") - } - - // create a transaction that wraps the instruction - tx, err := solana.NewTransaction( - []solana.Instruction{ - // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget - // https://github.com/zeta-chain/node/issues/2599 - // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), - // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), - &inst}, - recent.Value.Blockhash, - solana.TransactionPayer(signer.relayerKey.PublicKey()), - ) - if err != nil { - return nil, errors.Wrap(err, "unable to create new tx") - } - - // relayer signs the transaction - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(signer.relayerKey.PublicKey()) { - return signer.relayerKey - } - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "signer unable to sign transaction") - } - return tx, nil + return inst, nil } diff --git a/zetaclient/chains/solana/signer/increment_nonce.go b/zetaclient/chains/solana/signer/increment_nonce.go new file mode 100644 index 0000000000..9716814c01 --- /dev/null +++ b/zetaclient/chains/solana/signer/increment_nonce.go @@ -0,0 +1,74 @@ +package signer + +import ( + "context" + + "cosmossdk.io/errors" + "github.com/gagliardetto/solana-go" + "github.com/near/borsh-go" + + contracts "github.com/zeta-chain/node/pkg/contracts/solana" + "github.com/zeta-chain/node/x/crosschain/types" +) + +// createAndSignMsgIncrementNonce creates and signs a increment_nonce message for gateway increment_nonce instruction with TSS. +func (signer *Signer) createAndSignMsgIncrementNonce( + ctx context.Context, + params *types.OutboundParams, + height uint64, + cancelTx bool, +) (*contracts.MsgIncrementNonce, error) { + chain := signer.Chain() + // #nosec G115 always positive + chainID := uint64(signer.Chain().ChainId) + nonce := params.TssNonce + amount := params.Amount.Uint64() + + // zero out the amount if cancelTx is set. It's legal to withdraw 0 lamports through the gateway. + if cancelTx { + amount = 0 + } + + // prepare increment_nonce msg and compute hash + msg := contracts.NewMsgIncrementNonce(chainID, nonce, amount) + msgHash := msg.Hash() + + // sign the message with TSS to get an ECDSA signature. + // the produced signature is in the [R || S || V] format where V is 0 or 1. + signature, err := signer.TSS().Sign(ctx, msgHash[:], height, nonce, chain.ChainId) + if err != nil { + return nil, errors.Wrap(err, "key-sign failed") + } + + // attach the signature and return + return msg.SetSignature(signature), nil +} + +// createIncrementNonceInstruction wraps the increment_nonce 'msg' into a Solana instruction. +func (signer *Signer) createIncrementNonceInstruction( + msg contracts.MsgIncrementNonce, +) (*solana.GenericInstruction, error) { + // create increment_nonce instruction with program call data + dataBytes, err := borsh.Serialize(contracts.IncrementNonceInstructionParams{ + Discriminator: contracts.DiscriminatorIncrementNonce, + Amount: msg.Amount(), + Signature: msg.SigRS(), + RecoveryID: msg.SigV(), + MessageHash: msg.Hash(), + Nonce: msg.Nonce(), + }) + if err != nil { + return nil, errors.Wrap(err, "cannot serialize increment_nonce instruction") + } + + inst := &solana.GenericInstruction{ + ProgID: signer.gatewayID, + DataBytes: dataBytes, + AccountValues: []*solana.AccountMeta{ + solana.Meta(signer.relayerKey.PublicKey()).WRITE().SIGNER(), + solana.Meta(signer.pda).WRITE(), + }, + } + + return inst, nil +} diff --git a/zetaclient/chains/solana/signer/signer.go b/zetaclient/chains/solana/signer/signer.go index 18f24a989a..a90d75a313 100644 --- a/zetaclient/chains/solana/signer/signer.go +++ b/zetaclient/chains/solana/signer/signer.go @@ -132,6 +132,7 @@ func (signer *Signer) TryProcessOutbound( coinType := cctx.InboundParams.CoinType var tx *solana.Transaction + var fallbackTx *solana.Transaction switch coinType { case coin.CoinType_Cmd: @@ -150,8 +151,14 @@ func (signer *Signer) TryProcessOutbound( logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign execute outbound") return } + incrementNonceTx, err := signer.prepareIncrementNonceTx(ctx, cctx, height, logger) + if err != nil { + logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign increment_nonce outbound") + return + } tx = executeTx + fallbackTx = incrementNonceTx } else { withdrawTx, err := signer.prepareWithdrawTx(ctx, cctx, height, logger) if err != nil { @@ -170,7 +177,14 @@ func (signer *Signer) TryProcessOutbound( return } + incrementNonceTx, err := signer.prepareIncrementNonceTx(ctx, cctx, height, logger) + if err != nil { + logger.Error().Err(err).Msgf("TryProcessOutbound: Fail to sign increment_nonce outbound") + return + } + tx = executeSPLTx + fallbackTx = incrementNonceTx } else { withdrawSPLTx, err := signer.prepareWithdrawSPLTx(ctx, cctx, height, logger) if err != nil { @@ -196,13 +210,51 @@ func (signer *Signer) TryProcessOutbound( signer.SetRelayerBalanceMetrics(ctx) // broadcast the signed tx to the Solana network - signer.broadcastOutbound(ctx, tx, chainID, nonce, logger, zetacoreClient) + signer.broadcastOutbound(ctx, tx, fallbackTx, chainID, nonce, logger, zetacoreClient) +} + +// signTx creates and signs solana tx containing provided instruction with relayer key. +func (signer *Signer) signTx(ctx context.Context, inst *solana.GenericInstruction) (*solana.Transaction, error) { + // get a recent blockhash + recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) + if err != nil { + return nil, errors.Wrap(err, "getLatestBlockhash error") + } + + // create a transaction that wraps the instruction + tx, err := solana.NewTransaction( + []solana.Instruction{ + // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget + // https://github.com/zeta-chain/node/issues/2599 + // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), + // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), + inst}, + recent.Value.Blockhash, + solana.TransactionPayer(signer.relayerKey.PublicKey()), + ) + if err != nil { + return nil, errors.Wrap(err, "unable to create new tx") + } + + // relayer signs the transaction + _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { + if key.Equals(signer.relayerKey.PublicKey()) { + return signer.relayerKey + } + return nil + }) + if err != nil { + return nil, errors.Wrap(err, "signer unable to sign transaction") + } + + return tx, nil } // broadcastOutbound sends the signed transaction to the Solana network func (signer *Signer) broadcastOutbound( ctx context.Context, tx *solana.Transaction, + fallbackTx *solana.Transaction, chainID int64, nonce uint64, logger zerolog.Logger, @@ -247,6 +299,11 @@ func (signer *Signer) broadcastOutbound( rpc.TransactionOpts{PreflightCommitment: rpc.CommitmentProcessed}, ) if err != nil { + // in case it is not failure due to nonce mismatch, replace tx with fallback tx + // probably need a better way to do this, but currently this is the only error to tolerate like this + if !strings.Contains(err.Error(), "NonceMismatch") { + tx = fallbackTx + } logger.Warn().Err(err).Fields(lf).Msgf("SendTransactionWithOpts failed") backOff *= 2 continue @@ -259,6 +316,43 @@ func (signer *Signer) broadcastOutbound( } } +func (signer *Signer) prepareIncrementNonceTx( + ctx context.Context, + cctx *types.CrossChainTx, + height uint64, + logger zerolog.Logger, +) (*solana.Transaction, error) { + params := cctx.GetCurrentOutboundParam() + // compliance check + cancelTx := compliance.IsCctxRestricted(cctx) + if cancelTx { + compliance.PrintComplianceLog( + logger, + signer.Logger().Compliance, + true, + signer.Chain().ChainId, + cctx.Index, + cctx.InboundParams.Sender, + params.Receiver, + "SOL", + ) + } + + // sign gateway increment_nonce message by TSS + msg, err := signer.createAndSignMsgIncrementNonce(ctx, params, height, cancelTx) + if err != nil { + return nil, err + } + + // sign the increment_nonce transaction by relayer key + inst, err := signer.createIncrementNonceInstruction(*msg) + if err != nil { + return nil, errors.Wrap(err, "error creating increment nonce instruction") + } + + return signer.signTx(ctx, inst) +} + func (signer *Signer) prepareWithdrawTx( ctx context.Context, cctx *types.CrossChainTx, @@ -288,12 +382,12 @@ func (signer *Signer) prepareWithdrawTx( } // sign the withdraw transaction by relayer key - tx, err := signer.signWithdrawTx(ctx, *msg) + inst, err := signer.createWithdrawInstruction(*msg) if err != nil { - return nil, errors.Wrap(err, "signWithdrawTx error") + return nil, errors.Wrap(err, "error creating withdraw instruction") } - return tx, nil + return signer.signTx(ctx, inst) } func (signer *Signer) prepareExecuteTx( @@ -351,12 +445,12 @@ func (signer *Signer) prepareExecuteTx( } // sign the execute transaction by relayer key - tx, err := signer.signExecuteTx(ctx, *msgExecute) + inst, err := signer.createExecuteInstruction(*msgExecute) if err != nil { - return nil, errors.Wrap(err, "signExecuteTx error") + return nil, errors.Wrap(err, "error creating execute instruction") } - return tx, nil + return signer.signTx(ctx, inst) } func (signer *Signer) prepareWithdrawSPLTx( @@ -401,12 +495,12 @@ func (signer *Signer) prepareWithdrawSPLTx( } // sign the withdraw transaction by relayer key - tx, err := signer.signWithdrawSPLTx(ctx, *msg) + inst, err := signer.createWithdrawSPLInstruction(*msg) if err != nil { - return nil, errors.Wrap(err, "signWithdrawSPLTx error") + return nil, errors.Wrap(err, "error creating withdraw SPL instruction") } - return tx, nil + return signer.signTx(ctx, inst) } func (signer *Signer) prepareExecuteSPLTx( @@ -473,12 +567,12 @@ func (signer *Signer) prepareExecuteSPLTx( } // sign the execute spl transaction by relayer key - tx, err := signer.signExecuteSPLTx(ctx, *msgExecuteSpl) + inst, err := signer.createExecuteSPLInstruction(*msgExecuteSpl) if err != nil { - return nil, err + return nil, errors.Wrap(err, "error creating execute SPL instruction") } - return tx, nil + return signer.signTx(ctx, inst) } func (signer *Signer) prepareWhitelistTx( @@ -510,12 +604,12 @@ func (signer *Signer) prepareWhitelistTx( } // sign the whitelist transaction by relayer key - tx, err := signer.signWhitelistTx(ctx, msg) + inst, err := signer.createWhitelistInstruction(msg) if err != nil { - return nil, errors.Wrap(err, "signWhitelistTx error") + return nil, errors.Wrap(err, "error creating whitelist instruction") } - return tx, nil + return signer.signTx(ctx, inst) } func (signer *Signer) decodeMintAccountDetails(ctx context.Context, asset string) (token.Mint, error) { diff --git a/zetaclient/chains/solana/signer/whitelist.go b/zetaclient/chains/solana/signer/whitelist.go index e752d7abb9..7dd12c11e4 100644 --- a/zetaclient/chains/solana/signer/whitelist.go +++ b/zetaclient/chains/solana/signer/whitelist.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/errors" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" contracts "github.com/zeta-chain/node/pkg/contracts/solana" @@ -40,8 +39,8 @@ func (signer *Signer) createAndSignMsgWhitelist( return msg.SetSignature(signature), nil } -// signWhitelistTx wraps the whitelist 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhitelist) (*solana.Transaction, error) { +// createWhitelistInstruction wraps the whitelist 'msg' into a Solana instruction. +func (signer *Signer) createWhitelistInstruction(msg *contracts.MsgWhitelist) (*solana.GenericInstruction, error) { // create whitelist_spl_mint instruction with program call data dataBytes, err := borsh.Serialize(contracts.WhitelistInstructionParams{ Discriminator: contracts.DiscriminatorWhitelistSplMint, @@ -54,7 +53,7 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi return nil, errors.Wrap(err, "cannot serialize whitelist_spl_mint instruction") } - inst := solana.GenericInstruction{ + inst := &solana.GenericInstruction{ ProgID: signer.gatewayID, DataBytes: dataBytes, AccountValues: []*solana.AccountMeta{ @@ -66,37 +65,5 @@ func (signer *Signer) signWhitelistTx(ctx context.Context, msg *contracts.MsgWhi }, } - // get a recent blockhash - recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - return nil, errors.Wrap(err, "getLatestBlockhash error") - } - - // create a transaction that wraps the instruction - tx, err := solana.NewTransaction( - []solana.Instruction{ - // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget - // https://github.com/zeta-chain/node/issues/2599 - // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), - // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), - &inst}, - recent.Value.Blockhash, - solana.TransactionPayer(signer.relayerKey.PublicKey()), - ) - if err != nil { - return nil, errors.Wrap(err, "unable to create new tx") - } - - // relayer signs the transaction - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(signer.relayerKey.PublicKey()) { - return signer.relayerKey - } - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "signer unable to sign transaction") - } - - return tx, nil + return inst, nil } diff --git a/zetaclient/chains/solana/signer/withdraw.go b/zetaclient/chains/solana/signer/withdraw.go index 20b6582fc7..a95947f014 100644 --- a/zetaclient/chains/solana/signer/withdraw.go +++ b/zetaclient/chains/solana/signer/withdraw.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/errors" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/zeta-chain/node/pkg/chains" @@ -52,8 +51,8 @@ func (signer *Signer) createAndSignMsgWithdraw( return msg.SetSignature(signature), nil } -// signWithdrawTx wraps the withdraw 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithdraw) (*solana.Transaction, error) { +// createWithdrawInstruction wraps the withdraw 'msg' into a Solana instruction. +func (signer *Signer) createWithdrawInstruction(msg contracts.MsgWithdraw) (*solana.GenericInstruction, error) { // create withdraw instruction with program call data dataBytes, err := borsh.Serialize(contracts.WithdrawInstructionParams{ Discriminator: contracts.DiscriminatorWithdraw, @@ -67,7 +66,7 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd return nil, errors.Wrap(err, "cannot serialize withdraw instruction") } - inst := solana.GenericInstruction{ + inst := &solana.GenericInstruction{ ProgID: signer.gatewayID, DataBytes: dataBytes, AccountValues: []*solana.AccountMeta{ @@ -77,37 +76,5 @@ func (signer *Signer) signWithdrawTx(ctx context.Context, msg contracts.MsgWithd }, } - // get a recent blockhash - recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - return nil, errors.Wrap(err, "getLatestBlockhash error") - } - - // create a transaction that wraps the instruction - tx, err := solana.NewTransaction( - []solana.Instruction{ - // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget - // https://github.com/zeta-chain/node/issues/2599 - // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), - // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), - &inst}, - recent.Value.Blockhash, - solana.TransactionPayer(signer.relayerKey.PublicKey()), - ) - if err != nil { - return nil, errors.Wrap(err, "unable to create new tx") - } - - // relayer signs the transaction - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(signer.relayerKey.PublicKey()) { - return signer.relayerKey - } - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "signer unable to sign transaction") - } - - return tx, nil + return inst, nil } diff --git a/zetaclient/chains/solana/signer/withdraw_spl.go b/zetaclient/chains/solana/signer/withdraw_spl.go index 25365b8441..2671806ecb 100644 --- a/zetaclient/chains/solana/signer/withdraw_spl.go +++ b/zetaclient/chains/solana/signer/withdraw_spl.go @@ -5,7 +5,6 @@ import ( "cosmossdk.io/errors" "github.com/gagliardetto/solana-go" - "github.com/gagliardetto/solana-go/rpc" "github.com/near/borsh-go" "github.com/zeta-chain/node/pkg/chains" @@ -66,11 +65,8 @@ func (signer *Signer) createAndSignMsgWithdrawSPL( return msg.SetSignature(signature), nil } -// signWithdrawSPLTx wraps the withdraw spl 'msg' into a Solana transaction and signs it with the relayer key. -func (signer *Signer) signWithdrawSPLTx( - ctx context.Context, - msg contracts.MsgWithdrawSPL, -) (*solana.Transaction, error) { +// createWithdrawSPLInstruction wraps the withdraw spl 'msg' into a Solana instruction. +func (signer *Signer) createWithdrawSPLInstruction(msg contracts.MsgWithdrawSPL) (*solana.GenericInstruction, error) { // create withdraw spl instruction with program call data dataBytes, err := borsh.Serialize(contracts.WithdrawSPLInstructionParams{ Discriminator: contracts.DiscriminatorWithdrawSPL, @@ -95,7 +91,7 @@ func (signer *Signer) signWithdrawSPLTx( return nil, errors.Wrapf(err, "cannot find ATA for %s and mint account %s", msg.To(), msg.MintAccount()) } - inst := solana.GenericInstruction{ + inst := &solana.GenericInstruction{ ProgID: signer.gatewayID, DataBytes: dataBytes, AccountValues: []*solana.AccountMeta{ @@ -110,37 +106,6 @@ func (signer *Signer) signWithdrawSPLTx( solana.Meta(solana.SystemProgramID), }, } - // get a recent blockhash - recent, err := signer.client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - if err != nil { - return nil, errors.Wrap(err, "getLatestBlockhash error") - } - - // create a transaction that wraps the instruction - tx, err := solana.NewTransaction( - []solana.Instruction{ - // TODO: outbound now uses 5K lamports as the fixed fee, we could explore priority fee and compute budget - // https://github.com/zeta-chain/node/issues/2599 - // programs.ComputeBudgetSetComputeUnitLimit(computeUnitLimit), - // programs.ComputeBudgetSetComputeUnitPrice(computeUnitPrice), - &inst}, - recent.Value.Blockhash, - solana.TransactionPayer(signer.relayerKey.PublicKey()), - ) - if err != nil { - return nil, errors.Wrap(err, "unable to create new tx") - } - - // relayer signs the transaction - _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey { - if key.Equals(signer.relayerKey.PublicKey()) { - return signer.relayerKey - } - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "signer unable to sign transaction") - } - return tx, nil + return inst, nil } diff --git a/zetaclient/chains/sui/client/client.go b/zetaclient/chains/sui/client/client.go index 72772b6024..9714265b54 100644 --- a/zetaclient/chains/sui/client/client.go +++ b/zetaclient/chains/sui/client/client.go @@ -17,7 +17,7 @@ type Client struct { sui.ISuiAPI } -const DefaultEventsLimit = 100 +const DefaultEventsLimit = 50 const filterMoveEventModule = "MoveEventModule" @@ -130,9 +130,34 @@ func (p *EventQuery) asRequest() (models.SuiXQueryEventsRequest, error) { }, nil } +// GetOwnedObjectID returns the first owned object ID by owner address and struct type. +// If no objects found or multiple objects found, returns error. +func (c *Client) GetOwnedObjectID(ctx context.Context, ownerAddress, structType string) (string, error) { + res, err := c.SuiXGetOwnedObjects(ctx, models.SuiXGetOwnedObjectsRequest{ + Address: ownerAddress, + Query: models.SuiObjectResponseQuery{ + Filter: map[string]any{ + "StructType": structType, + }, + }, + Limit: 1, + }) + + switch { + case err != nil: + return "", errors.Wrap(err, "unable to get owned objects") + case len(res.Data) == 0: + return "", errors.New("no objects found") + case len(res.Data) > 1: + return "", errors.New("multiple objects found") + } + + return res.Data[0].Data.ObjectId, nil +} + // EncodeCursor encodes event ID into cursor. func EncodeCursor(id models.EventId) string { - return fmt.Sprintf("%s#%s", id.TxDigest, id.EventSeq) + return fmt.Sprintf("%s,%s", id.TxDigest, id.EventSeq) } // DecodeCursor decodes cursor into event ID. @@ -141,7 +166,7 @@ func DecodeCursor(cursor string) (*models.EventId, error) { return nil, nil } - parts := strings.Split(cursor, "#") + parts := strings.Split(cursor, ",") if len(parts) != 2 { return nil, errors.New("invalid cursor format") } diff --git a/zetaclient/chains/sui/client/client_live_test.go b/zetaclient/chains/sui/client/client_live_test.go index 8f74f86fda..f9494e1af2 100644 --- a/zetaclient/chains/sui/client/client_live_test.go +++ b/zetaclient/chains/sui/client/client_live_test.go @@ -93,6 +93,29 @@ func TestClientLive(t *testing.T) { eventsEqual(t, a, resCombined[i]) } }) + + t.Run("GetOwnedObjectID", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t, RpcMainnet) + + // Given admin wallet us Cetus DEX team + // (yeah, it took some time to find it) + const ownerAddress = "0xdbfd0b17fa804c98f51d552b050fb7f850b85db96fa2a0d79e50119525814a47" + + // Given AdminCap struct type of Cetus DEX + // (they use it for upgrades and stuff) + const structType = "0x1eabed72c53feb3805120a081dc15963c204dc8d091542592abaf7a35689b2fb::config::AdminCap" + + // ACT + // Get owned object id as we would fetch Gateway's WithdrawCap + // that should belong to TSS + objectID, err := ts.GetOwnedObjectID(ts.ctx, ownerAddress, structType) + + // ASSERT + // https://suiscan.xyz/mainnet/object/0x89c1a321291d15ddae5a086c9abc533dff697fde3d89e0ca836c41af73e36a75 + require.NoError(t, err) + require.Equal(t, "0x89c1a321291d15ddae5a086c9abc533dff697fde3d89e0ca836c41af73e36a75", objectID) + }) } type testSuite struct { diff --git a/zetaclient/chains/sui/observer/inbound.go b/zetaclient/chains/sui/observer/inbound.go index 46fd3ff41e..5ddec29ce4 100644 --- a/zetaclient/chains/sui/observer/inbound.go +++ b/zetaclient/chains/sui/observer/inbound.go @@ -19,9 +19,7 @@ var errTxNotFound = errors.New("no tx found") // ObserveInbound processes inbound deposit cross-chain transactions. func (ob *Observer) ObserveInbound(ctx context.Context) error { - if err := ob.ensureCursor(ctx); err != nil { - return errors.Wrap(err, "unable to ensure inbound cursor") - } + ob.ensureCursor() query := client.EventQuery{ PackageID: ob.gateway.PackageID(), @@ -36,6 +34,13 @@ func (ob *Observer) ObserveInbound(ctx context.Context) error { return errors.Wrap(err, "unable to query module events") } + if len(events) == 0 { + ob.Logger().Inbound.Debug().Msg("No inbound events found") + return nil + } + + ob.Logger().Inbound.Info().Int("events", len(events)).Msg("Processing inbound events") + for _, event := range events { // Note: we can make this concurrent if needed. // Let's revisit later @@ -46,7 +51,7 @@ func (ob *Observer) ObserveInbound(ctx context.Context) error { // try again later ob.Logger().Inbound.Warn().Err(err). Str(logs.FieldTx, event.Id.TxDigest). - Msg("TX not found or unfinalized. Pausing") + Msg("TX not found or not finalized. Pausing") return nil case err != nil: // failed processing also updates the cursor @@ -103,8 +108,6 @@ func (ob *Observer) processInboundEvent( return nil case err != nil: return errors.Wrap(err, "unable to parse event") - case !event.IsInbound(): - ob.Logger().Inbound.Info().Msg("Not an inbound event. Skipping") case event.EventIndex != 0: // Is it possible to have multiple events per tx? // e.g. contract "A" calls Gateway multiple times in a single tx (deposit to multiple accounts) @@ -161,14 +164,16 @@ func (ob *Observer) constructInboundVote( event sui.Event, tx models.SuiTransactionBlockResponse, ) (*cctypes.MsgVoteInbound, error) { - inbound, err := event.Inbound() + deposit, err := event.Deposit() if err != nil { return nil, errors.Wrap(err, "unable to extract inbound") } coinType := coin.CoinType_Gas - if !inbound.IsGasDeposit() { + asset := "" + if !deposit.IsGas() { coinType = coin.CoinType_ERC20 + asset = string(deposit.CoinType) } // Sui uses checkpoint seq num instead of block height @@ -177,21 +182,15 @@ func (ob *Observer) constructInboundVote( return nil, errors.Wrap(err, "unable to parse checkpoint") } - // Empty or full SUI coin name - var asset string - if !inbound.IsGasDeposit() { - asset = string(inbound.CoinType) - } - return cctypes.NewMsgVoteInbound( ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), - inbound.Sender, + deposit.Sender, ob.Chain().ChainId, - inbound.Sender, - inbound.Receiver.String(), + deposit.Sender, + deposit.Receiver.String(), ob.ZetacoreClient().Chain().ChainId, - inbound.Amount, - hex.EncodeToString(inbound.Payload), + deposit.Amount, + hex.EncodeToString(deposit.Payload), event.TxHash, checkpointSeqNum, zetacore.PostVoteInboundCallOptionsGasLimit, @@ -202,6 +201,6 @@ func (ob *Observer) constructInboundVote( false, cctypes.InboundStatus_SUCCESS, cctypes.ConfirmationMode_SAFE, - cctypes.WithCrossChainCall(inbound.IsCrossChainCall), + cctypes.WithCrossChainCall(deposit.IsCrossChainCall), ), nil } diff --git a/zetaclient/chains/sui/observer/observer.go b/zetaclient/chains/sui/observer/observer.go index 823326856b..b4600ceaba 100644 --- a/zetaclient/chains/sui/observer/observer.go +++ b/zetaclient/chains/sui/observer/observer.go @@ -4,6 +4,7 @@ import ( "context" "os" "strconv" + "sync" "time" "github.com/block-vision/sui-go-sdk/models" @@ -19,6 +20,13 @@ type Observer struct { *base.Observer client RPC gateway *sui.Gateway + + // nonce -> sui outbound tx + txMap map[uint64]models.SuiTransactionBlockResponse + txMu sync.RWMutex + + latestGasPrice uint64 + gasPriceMu sync.RWMutex } // RPC represents subset of Sui RPC methods. @@ -41,9 +49,13 @@ func New(baseObserver *base.Observer, client RPC, gateway *sui.Gateway) *Observe Observer: baseObserver, client: client, gateway: gateway, + txMap: make(map[uint64]models.SuiTransactionBlockResponse), } } +// Gateway returns Sui gateway. +func (ob *Observer) Gateway() *sui.Gateway { return ob.gateway } + // CheckRPCStatus checks the RPC status of the chain. func (ob *Observer) CheckRPCStatus(ctx context.Context) error { blockTime, err := ob.client.HealthCheck(ctx) @@ -90,6 +102,8 @@ func (ob *Observer) PostGasPrice(ctx context.Context) error { // no priority fee for Sui const priorityFee = 0 + ob.setLatestGasPrice(gasPrice) + _, err = ob.ZetacoreClient().PostVoteGasPrice(ctx, ob.Chain(), gasPrice, priorityFee, epochNum) if err != nil { return errors.Wrap(err, "unable to post vote for gas price") @@ -98,46 +112,30 @@ func (ob *Observer) PostGasPrice(ctx context.Context) error { return nil } +func (ob *Observer) getLatestGasPrice() uint64 { + ob.gasPriceMu.RLock() + defer ob.gasPriceMu.RUnlock() + + return ob.latestGasPrice +} + +func (ob *Observer) setLatestGasPrice(price uint64) { + ob.gasPriceMu.Lock() + defer ob.gasPriceMu.Unlock() + ob.latestGasPrice = price +} + // ensureCursor ensures tx scroll cursor for inbound observations -func (ob *Observer) ensureCursor(ctx context.Context) error { +func (ob *Observer) ensureCursor() { if ob.LastTxScanned() != "" { - return nil + return } // Note that this would only work for the empty chain database envValue := os.Getenv(base.EnvVarLatestTxByChain(ob.Chain())) if envValue != "" { ob.WithLastTxScanned(envValue) - return nil - } - - // let's take the first tx that was ever registered for the Gateway (deployment tx) - // Note that this might have for a non-archival node - req := models.SuiGetObjectRequest{ - ObjectId: ob.gateway.PackageID(), - Options: models.SuiObjectDataOptions{ - ShowPreviousTransaction: true, - }, } - - res, err := ob.client.SuiGetObject(ctx, req) - switch { - case err != nil: - return errors.Wrap(err, "unable to get object") - case res.Error != nil: - return errors.Errorf("get object error: %s (code %s)", res.Error.Error, res.Error.Code) - case res.Data == nil: - return errors.New("object data is empty") - case res.Data.PreviousTransaction == "": - return errors.New("previous transaction is empty") - } - - cursor := client.EncodeCursor(models.EventId{ - TxDigest: res.Data.PreviousTransaction, - EventSeq: "0", - }) - - return ob.setCursor(cursor) } func (ob *Observer) getCursor() string { return ob.LastTxScanned() } diff --git a/zetaclient/chains/sui/observer/observer_test.go b/zetaclient/chains/sui/observer/observer_test.go index 48823cf24d..1b22e83f5d 100644 --- a/zetaclient/chains/sui/observer/observer_test.go +++ b/zetaclient/chains/sui/observer/observer_test.go @@ -2,6 +2,7 @@ package observer import ( "context" + "encoding/base64" "fmt" "testing" @@ -24,6 +25,8 @@ import ( "github.com/zeta-chain/node/zetaclient/testutils/testlog" ) +var someArgStub = map[string]any{} + func TestObserver(t *testing.T) { t.Run("PostGasPrice", func(t *testing.T) { // ARRANGE @@ -63,36 +66,17 @@ func TestObserver(t *testing.T) { const usdc = "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN" - // Given gateway object from RPC (for "ensuring" the initial scroll cursor) - gatewayRequest := models.SuiGetObjectRequest{ - ObjectId: ts.gateway.PackageID(), - Options: models.SuiObjectDataOptions{ - ShowPreviousTransaction: true, - }, - } - - gatewayObject := models.SuiObjectResponse{ - Data: &models.SuiObjectData{ - ObjectId: ts.gateway.PackageID(), - PreviousTransaction: "ABC123_first_tx", - }, - } - - ts.suiMock. - On("SuiGetObject", mock.Anything, gatewayRequest). - Return(gatewayObject, nil) - // Given list of gateway events... expectedQuery := client.EventQuery{ PackageID: ts.gateway.PackageID(), Module: ts.gateway.Module(), - Cursor: "ABC123_first_tx#0", + Cursor: "", Limit: client.DefaultEventsLimit, } // ...two of which are valid (1 & 3) events := []models.SuiEventResponse{ - ts.SampleEvent("TX_1_ok", string(sui.Deposit), map[string]any{ + ts.SampleEvent("TX_1_ok", string(sui.DepositEvent), map[string]any{ "coin_type": string(sui.SUI), "amount": "200", "sender": "SUI_BOB", @@ -104,15 +88,15 @@ func TestObserver(t *testing.T) { "sender": "SUI_BOB", "receiver": evmBob.String(), }), - ts.SampleEvent("TX_3_ok", string(sui.DepositAndCall), map[string]any{ + ts.SampleEvent("TX_3_ok", string(sui.DepositAndCallEvent), map[string]any{ // USDC "coin_type": usdc, "amount": "300", "sender": "SUI_ALICE", "receiver": evmAlice.String(), - "payload": []any{float64(1), float64(2), float64(3)}, + "payload": preparePayload([]byte{1, 2, 3}), }), - ts.SampleEvent("TX_4_invalid_data", string(sui.Deposit), map[string]any{ + ts.SampleEvent("TX_4_invalid_data", string(sui.DepositEvent), map[string]any{ "coin_type": string(sui.SUI), "amount": "hello", "sender": "SUI_BOB", @@ -136,10 +120,10 @@ func TestObserver(t *testing.T) { require.NoError(t, err) // Check that final cursor is on INVALID event, that's expected - assert.Equal(t, "TX_4_invalid_data#0", ts.LastTxScanned()) + assert.Equal(t, "TX_4_invalid_data,0", ts.LastTxScanned()) // Check for transactions - assert.Equal(t, 2, len(ts.inboundVotesBag)) + require.Equal(t, 2, len(ts.inboundVotesBag)) vote1 := ts.inboundVotesBag[0] assert.Equal(t, "TX_1_ok", vote1.InboundHash) @@ -193,7 +177,7 @@ func TestObserver(t *testing.T) { evmAlice := sample.EthAddress() ts.OnGetTx("TX_TRACKER_1", "15000", true, []models.SuiEventResponse{ - ts.SampleEvent("TX_TRACKER_1", string(sui.Deposit), map[string]any{ + ts.SampleEvent("TX_TRACKER_1", string(sui.DepositEvent), map[string]any{ "coin_type": string(sui.SUI), "amount": "1000", "sender": "SUI_ALICE", @@ -221,6 +205,151 @@ func TestObserver(t *testing.T) { assert.Equal(t, math.NewUint(1000), vote.Amount) assert.Equal(t, evmAlice.String(), vote.Receiver) }) + + t.Run("ProcessOutboundTrackers", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given cctx + const nonce = 333 + cctx := sample.CrossChainTxV2(t, "0x123") + cctx.OutboundParams = []*cctypes.OutboundParams{{TssNonce: nonce}} + + ts.MockCCTXByNonce(cctx) + + // Given outbound tracker + const digest = "0xSuiTxHash" + tracker := cctypes.OutboundTracker{ + Index: "0xAAA", + ChainId: ts.Chain().ChainId, + Nonce: nonce, + HashList: []*cctypes.TxHash{{TxHash: digest}}, + } + + ts.MockOutboundTrackers([]cctypes.OutboundTracker{tracker}) + + // Given Sui tx signature + sigBase64, err := sui.SerializeSignatureECDSA([65]byte{1, 2, 3}, ts.TSS().PubKey().AsECDSA()) + require.NoError(t, err) + + // Given Sui tx + tx := models.SuiTransactionBlockResponse{ + Digest: digest, + Checkpoint: "123", + Effects: models.SuiEffects{ + Status: models.ExecutionStatus{Status: "success"}, + }, + Transaction: models.SuiTransactionBlock{ + Data: models.SuiTransactionBlockData{ + Transaction: models.SuiTransactionBlockKind{ + Inputs: []models.SuiCallArg{ + someArgStub, + someArgStub, + map[string]any{ + "type": "pure", + "valueType": "u64", + "value": fmt.Sprintf("%d", nonce), + }, + someArgStub, + someArgStub, + }, + }, + }, + TxSignatures: []string{sigBase64}, + }, + } + + ts.MockGetTxOnce(tx) + + // ACT + err = ts.ProcessOutboundTrackers(ts.ctx) + + // ASSERT + require.NoError(t, err) + assert.True(t, ts.OutboundCreated(nonce)) + assert.False(t, ts.OutboundCreated(nonce+1)) + }) + + t.Run("VoteOutbound", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + // Given Sui Gateway + gw := ts.Gateway() + + // Given cctx + const nonce = 333 + cctx := sample.CrossChainTxV2(t, "0x123") + cctx.OutboundParams = []*cctypes.OutboundParams{{TssNonce: nonce}} + + // Given Sui receiver + const receiver = "0xAliceOnSui" + + // Given a valid Sui outbound tx with Withdrawal event + const digest = "0xSuiTxDigest" + tx := models.SuiTransactionBlockResponse{ + Digest: digest, + Checkpoint: "999", + Effects: models.SuiEffects{ + Status: models.ExecutionStatus{Status: "success"}, + GasUsed: models.GasCostSummary{ + ComputationCost: "200", + StorageCost: "300", + StorageRebate: "50", + }, + }, + Events: []models.SuiEventResponse{{ + Id: models.EventId{TxDigest: digest, EventSeq: "1"}, + PackageId: gw.PackageID(), + Sender: ts.TSS().PubKey().AddressSui(), + Type: fmt.Sprintf("%s::%s::%s", gw.PackageID(), gw.Module(), "WithdrawEvent"), + ParsedJson: map[string]any{ + "coin_type": string(sui.SUI), + "amount": "200", + "sender": ts.TSS().PubKey().AddressSui(), + "receiver": receiver, + "nonce": fmt.Sprintf("%d", nonce), + }, + }}, + } + + // What was fetched during ProcessOutboundTracker(...) + ts.setTx(tx, nonce) + + // Given a gas price that was set during PostGasPrice(...) + ts.setLatestGasPrice(1000) + + // Given outbound votes catcher + ts.CatchOutboundVotes() + + // ACT + err := ts.VoteOutbound(ts.ctx, cctx) + + // ASSERT + require.NoError(t, err) + require.Len(t, ts.outboundVotesBag, 1) + + vote := ts.outboundVotesBag[0] + + // common + assert.Equal(t, chains.ReceiveStatus_success, vote.Status) + assert.Equal(t, cctx.Index, vote.CctxHash) + assert.Equal(t, uint64(nonce), vote.OutboundTssNonce) + assert.Equal(t, ts.Chain().ChainId, vote.OutboundChain) + + // digest + checkpoint + assert.Equal(t, digest, vote.ObservedOutboundHash) + assert.Equal(t, uint64(999), vote.ObservedOutboundBlockHeight) + + // amount + assert.Equal(t, coin.CoinType_Gas, vote.CoinType) + assert.Equal(t, uint64(200), vote.ValueReceived.Uint64()) + + // gas + assert.Equal(t, uint64(maxGasLimit), vote.ObservedOutboundEffectiveGasLimit) + assert.Equal(t, uint64(1000), vote.ObservedOutboundEffectiveGasPrice.Uint64()) + assert.Equal(t, uint64(200+300-50), vote.ObservedOutboundGasUsed) + }) } type testSuite struct { @@ -232,7 +361,8 @@ type testSuite struct { log *testlog.Log gateway *sui.Gateway - inboundVotesBag []*cctypes.MsgVoteInbound + inboundVotesBag []*cctypes.MsgVoteInbound + outboundVotesBag []*cctypes.MsgVoteOutbound *Observer } @@ -244,8 +374,6 @@ func newTestSuite(t *testing.T) *testSuite { chainParams := mocks.MockChainParams(chain.ChainId, 10) require.NotEmpty(t, chainParams.GatewayAddress) - // todo zctx with chain & params (in future PRs) - zetacore := mocks.NewZetacoreClient(t). WithZetaChain(). WithKeys(&keys.Keys{ @@ -268,7 +396,8 @@ func newTestSuite(t *testing.T) *testSuite { suiMock := mocks.NewSuiClient(t) - gw := sui.NewGateway(chainParams.GatewayAddress) + gw, err := sui.NewGatewayFromPairID(chainParams.GatewayAddress) + require.NoError(t, err) observer := New(baseObserver, suiMock, gw) @@ -315,6 +444,10 @@ func (ts *testSuite) OnGetTx(digest, checkpoint string, showEvents bool, events ts.suiMock.On("SuiGetTransactionBlock", mock.Anything, req).Return(res, nil).Once() } +func (ts *testSuite) MockGetTxOnce(tx models.SuiTransactionBlockResponse) { + ts.suiMock.On("SuiGetTransactionBlock", mock.Anything, mock.Anything).Return(tx, nil).Once() +} + func (ts *testSuite) CatchInboundVotes() { callback := func(_ context.Context, _, _ uint64, msg *cctypes.MsgVoteInbound) (string, string, error) { ts.inboundVotesBag = append(ts.inboundVotesBag, msg) @@ -326,3 +459,40 @@ func (ts *testSuite) CatchInboundVotes() { Return(callback). Maybe() } + +func (ts *testSuite) CatchOutboundVotes() { + callback := func(_ context.Context, _, _ uint64, msg *cctypes.MsgVoteOutbound) (string, string, error) { + ts.outboundVotesBag = append(ts.outboundVotesBag, msg) + return "", "", nil + } + + ts.zetaMock. + On("PostVoteOutbound", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(callback). + Maybe() +} + +func (ts *testSuite) MockCCTXByNonce(cctx *cctypes.CrossChainTx) *mock.Call { + nonce := cctx.GetCurrentOutboundParam().TssNonce + + return ts.zetaMock. + On("GetCctxByNonce", ts.ctx, ts.Chain().ChainId, nonce). + Return(cctx, nil) +} + +func (ts *testSuite) MockOutboundTrackers(trackers []cctypes.OutboundTracker) *mock.Call { + return ts.zetaMock. + On("GetAllOutboundTrackerByChain", mock.Anything, ts.Chain().ChainId, mock.Anything). + Return(trackers, nil) +} + +func preparePayload(payload []byte) []any { + payloadBytes := []byte(base64.StdEncoding.EncodeToString(payload)) + + var out []any + for _, p := range payloadBytes { + out = append(out, float64(p)) + } + + return out +} diff --git a/zetaclient/chains/sui/observer/outbound.go b/zetaclient/chains/sui/observer/outbound.go new file mode 100644 index 0000000000..f927552dea --- /dev/null +++ b/zetaclient/chains/sui/observer/outbound.go @@ -0,0 +1,326 @@ +package observer + +import ( + "context" + "strconv" + + "cosmossdk.io/math" + "github.com/block-vision/sui-go-sdk/models" + "github.com/pkg/errors" + + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/contracts/sui" + cctypes "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/interfaces" + "github.com/zeta-chain/node/zetaclient/logs" + "github.com/zeta-chain/node/zetaclient/zetacore" +) + +// https://github.com/zeta-chain/protocol-contracts-sui/blob/9d08a70817d8cc7cf799b9ae12c59b6e0b8aaab9/sources/gateway.move#L125 +// (excluding last arg of `ctx`) +const expectedWithdrawArgs = 5 + +// 50 SUI +// https://docs.sui.io/concepts/tokenomics/gas-in-sui#gas-budgets +const maxGasLimit = 50_000_000_000 + +// OutboundCreated checks if the outbound tx exists in the memory +// and has valid nonce & signature +func (ob *Observer) OutboundCreated(nonce uint64) bool { + _, ok := ob.getTx(nonce) + return ok +} + +// ProcessOutboundTrackers loads all freshly-included Sui transactions in-memory +// for further voting by Observer-Signer. +func (ob *Observer) ProcessOutboundTrackers(ctx context.Context) error { + chainID := ob.Chain().ChainId + + trackers, err := ob.ZetacoreClient().GetAllOutboundTrackerByChain(ctx, chainID, interfaces.Ascending) + if err != nil { + return errors.Wrap(err, "unable to get outbound trackers") + } + + for _, tracker := range trackers { + nonce := tracker.Nonce + + // already loaded + if _, ok := ob.getTx(nonce); ok { + continue + } + + // should not happen + if len(tracker.HashList) == 0 { + // we don't want to block other cctxs, so let's error and continue + ob.Logger().Outbound.Error(). + Str(logs.FieldMethod, "ProcessOutboundTrackers"). + Uint64(logs.FieldNonce, nonce). + Str(logs.FieldTracker, tracker.Index). + Msg("Tracker hash list is empty!") + continue + } + + digest := tracker.HashList[0].TxHash + + cctx, err := ob.ZetacoreClient().GetCctxByNonce(ctx, chainID, tracker.Nonce) + if err != nil { + return errors.Wrapf(err, "unable to get cctx by nonce %d (sui digest %q)", tracker.Nonce, digest) + } + + if err := ob.loadOutboundTx(ctx, cctx, digest); err != nil { + // we don't want to block other cctxs, so let's error and continue + ob.Logger().Outbound. + Error().Err(err). + Str(logs.FieldMethod, "ProcessOutboundTrackers"). + Uint64(logs.FieldNonce, nonce). + Str(logs.FieldTx, digest). + Msg("Unable to load outbound transaction") + } + } + + return nil +} + +// VoteOutbound calculates outbound result based on cctx and in-mem Sui tx +// and votes the ballot to zetacore. +func (ob *Observer) VoteOutbound(ctx context.Context, cctx *cctypes.CrossChainTx) error { + chainID := ob.Chain().ChainId + nonce := cctx.GetCurrentOutboundParam().TssNonce + + // should be fetched by ProcessOutboundTrackers routine + // if exists, we can safely assume it's authentic and nonce is valid + tx, ok := ob.getTx(nonce) + if !ok { + return errors.Errorf("missing tx for nonce %d", nonce) + } + + // used instead of block height + checkpoint, err := strconv.ParseUint(tx.Checkpoint, 10, 64) + if err != nil { + return errors.Wrap(err, "unable to parse checkpoint") + } + + // parse status, coinType, and amount + var ( + status = chains.ReceiveStatus_failed + coinType = coin.CoinType_Gas + amount = math.NewUint(0) + isSuccess = tx.Effects.Status.Status == "success" + ) + + if isSuccess { + status = chains.ReceiveStatus_success + + _, w, err := ob.gateway.ParseTxWithdrawal(tx) + if err != nil { + return errors.Wrap(err, "unable to parse tx withdrawal") + } + + if !w.IsGas() { + coinType = coin.CoinType_ERC20 + } + + amount = w.Amount + } + + // Gas parameters + // Gas price *might* change once per epoch (~24h), so using the latest value is fine. + // #nosec G115 - always in range + outboundGasPrice := math.NewInt(int64(ob.getLatestGasPrice())) + + // This might happen after zetacore restart when PostGasPrice has not been called yet. retry later. + if outboundGasPrice.IsZero() { + return errors.New("latest gas price is zero") + } + + outboundGasUsed, err := parseGasUsed(tx) + if err != nil { + return errors.Wrap(err, "unable to parse gas used") + } + + // Create message + msg := cctypes.NewMsgVoteOutbound( + ob.ZetacoreClient().GetKeys().GetOperatorAddress().String(), + cctx.Index, + tx.Digest, + checkpoint, + outboundGasUsed, + outboundGasPrice, + maxGasLimit, + amount, + status, + chainID, + nonce, + coinType, + cctypes.ConfirmationMode_SAFE, + ) + + // TODO compliance checks + // https://github.com/zeta-chain/node/issues/3584 + + if err := ob.postVoteOutbound(ctx, msg); err != nil { + return errors.Wrap(err, "unable to post vote outbound") + } + + ob.unsetTx(nonce) + + return nil +} + +// loadOutboundTx loads cross-chain outbound tx by digest and ensures its authenticity. +func (ob *Observer) loadOutboundTx(ctx context.Context, cctx *cctypes.CrossChainTx, digest string) error { + res, err := ob.client.SuiGetTransactionBlock(ctx, models.SuiGetTransactionBlockRequest{ + Digest: digest, + Options: models.SuiTransactionBlockOptions{ + ShowEvents: true, + ShowInput: true, + ShowEffects: true, + }, + }) + + if err != nil { + return errors.Wrap(err, "unable to get tx") + } + + if err := ob.validateOutbound(cctx, res); err != nil { + return errors.Wrap(err, "tx validation failed") + } + + ob.setTx(res, cctx.GetCurrentOutboundParam().TssNonce) + + return nil +} + +// validateOutbound validates the authenticity of the outbound transaction. +// Note that it doesn't care about successful execution (e.g. something failed). +func (ob *Observer) validateOutbound(cctx *cctypes.CrossChainTx, tx models.SuiTransactionBlockResponse) error { + nonce := cctx.GetCurrentOutboundParam().TssNonce + + inputs := tx.Transaction.Data.Transaction.Inputs + + // Check args length + if len(inputs) != expectedWithdrawArgs { + return errors.Errorf("invalid number of input arguments (got %d, want %d)", len(inputs), expectedWithdrawArgs) + } + + txNonce, err := parseNonceFromWithdrawInputs(inputs) + if err != nil { + return errors.Wrap(err, "unable to parse nonce from inputs") + } + + if txNonce != nonce { + return errors.Errorf("nonce mismatch (tx nonce %d, cctx nonce %d)", txNonce, nonce) + } + + if len(tx.Transaction.TxSignatures) == 0 { + return errors.New("missing tx signature") + } + + pubKey, _, err := sui.DeserializeSignatureECDSA(tx.Transaction.TxSignatures[0]) + if err != nil { + return errors.Wrap(err, "unable to deserialize tx signature") + } + + if !ob.TSS().PubKey().AsECDSA().Equal(pubKey) { + return errors.New("pubKey mismatch") + } + + return nil +} + +func (ob *Observer) postVoteOutbound(ctx context.Context, msg *cctypes.MsgVoteOutbound) error { + const gasLimit = zetacore.PostVoteOutboundGasLimit + + retryGasLimit := uint64(0) + if msg.Status == chains.ReceiveStatus_failed { + retryGasLimit = zetacore.PostVoteOutboundRevertGasLimit + } + + zetaTxHash, ballot, err := ob.ZetacoreClient().PostVoteOutbound(ctx, gasLimit, retryGasLimit, msg) + switch { + case err != nil: + return errors.Wrap(err, "unable to post vote outbound") + case zetaTxHash != "": + ob.Logger().Outbound.Info(). + Str(logs.FieldZetaTx, zetaTxHash). + Str(logs.FieldBallot, ballot). + Msg("PostVoteOutbound: posted outbound vote successfully") + } + + return nil +} + +func (ob *Observer) getTx(nonce uint64) (models.SuiTransactionBlockResponse, bool) { + ob.txMu.RLock() + defer ob.txMu.RUnlock() + + tx, ok := ob.txMap[nonce] + + return tx, ok +} + +func (ob *Observer) setTx(tx models.SuiTransactionBlockResponse, nonce uint64) { + ob.txMu.Lock() + defer ob.txMu.Unlock() + + ob.txMap[nonce] = tx +} + +func (ob *Observer) unsetTx(nonce uint64) { + ob.txMu.Lock() + defer ob.txMu.Unlock() + + delete(ob.txMap, nonce) +} + +func parseNonceFromWithdrawInputs(inputs []models.SuiCallArg) (uint64, error) { + if len(inputs) != expectedWithdrawArgs { + return 0, errors.New("invalid number of input arguments") + } + + const nonceIdx = 2 + + // { + // "type": "pure", + // "valueType": "u64", + // "value": "12345" + // } + raw := inputs[nonceIdx] + + if raw["type"] != "pure" || raw["valueType"] != "u64" { + return 0, errors.Errorf("invalid nonce object %+v", raw) + } + + return strconv.ParseUint(raw["value"].(string), 10, 64) +} + +func parseGasUsed(tx models.SuiTransactionBlockResponse) (uint64, error) { + gas := tx.Effects.GasUsed + + compCost, err := parseUint64(gas.ComputationCost) + if err != nil { + return 0, errors.Wrap(err, "comp cost") + } + + storageCost, err := parseUint64(gas.StorageCost) + if err != nil { + return 0, errors.Wrap(err, "storage cost") + } + + storageRebate, err := parseUint64(gas.StorageRebate) + if err != nil { + return 0, errors.Wrap(err, "storage rebate") + } + + // should not happen + if (compCost + storageCost) < storageRebate { + return 0, errors.New("storage rebate exceeds total costs") + } + + return compCost + storageCost - storageRebate, nil +} + +func parseUint64(v string) (uint64, error) { + return strconv.ParseUint(v, 10, 64) +} diff --git a/zetaclient/chains/sui/signer/signer.go b/zetaclient/chains/sui/signer/signer.go index 98c2088750..68b260bc5a 100644 --- a/zetaclient/chains/sui/signer/signer.go +++ b/zetaclient/chains/sui/signer/signer.go @@ -1,13 +1,124 @@ package signer -import "github.com/zeta-chain/node/zetaclient/chains/base" +import ( + "context" + "crypto/sha256" + + "github.com/block-vision/sui-go-sdk/models" + "github.com/pkg/errors" + + "github.com/zeta-chain/node/pkg/bg" + "github.com/zeta-chain/node/pkg/contracts/sui" + cctypes "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" + "github.com/zeta-chain/node/zetaclient/chains/interfaces" + "github.com/zeta-chain/node/zetaclient/logs" +) // Signer Sui outbound transaction signer. type Signer struct { *base.Signer + client RPC + gateway *sui.Gateway + withdrawCap *withdrawCap + + zetacore interfaces.ZetacoreClient +} + +// RPC represents Sui rpc. +type RPC interface { + GetOwnedObjectID(ctx context.Context, ownerAddress, structType string) (string, error) + + MoveCall(ctx context.Context, req models.MoveCallRequest) (models.TxnMetaData, error) + SuiExecuteTransactionBlock( + ctx context.Context, + req models.SuiExecuteTransactionBlockRequest, + ) (models.SuiTransactionBlockResponse, error) + SuiGetTransactionBlock( + ctx context.Context, + req models.SuiGetTransactionBlockRequest, + ) (models.SuiTransactionBlockResponse, error) } // New Signer constructor. -func New(baseSigner *base.Signer) *Signer { - return &Signer{Signer: baseSigner} +func New( + baseSigner *base.Signer, + client RPC, + gateway *sui.Gateway, + zetacore interfaces.ZetacoreClient, +) *Signer { + return &Signer{ + Signer: baseSigner, + client: client, + gateway: gateway, + zetacore: zetacore, + withdrawCap: &withdrawCap{}, + } +} + +// ProcessCCTX schedules outbound cross-chain transaction. +// Build --> Sign --> Broadcast --(async)--> Wait for execution --> PostOutboundTracker +func (s *Signer) ProcessCCTX(ctx context.Context, cctx *cctypes.CrossChainTx, zetaHeight uint64) error { + var ( + outboundID = base.OutboundIDFromCCTX(cctx) + nonce = cctx.GetCurrentOutboundParam().TssNonce + ) + + s.MarkOutbound(outboundID, true) + defer func() { s.MarkOutbound(outboundID, false) }() + + tx, err := s.buildWithdrawal(ctx, cctx) + if err != nil { + return errors.Wrap(err, "unable to build withdrawal tx") + } + + sig, err := s.signTx(ctx, tx, zetaHeight, nonce) + if err != nil { + return errors.Wrap(err, "unable to sign tx") + } + + txDigest, err := s.broadcast(ctx, tx, sig) + if err != nil { + // todo we might need additional error handling + // for the case when the tx is already broadcasted by another zetaclient + // (e.g. suppress error) + return errors.Wrap(err, "unable to broadcast tx") + } + + logger := s.Logger().Std.With(). + Str(logs.FieldMethod, "reportToOutboundTracker"). + Int64(logs.FieldChain, s.Chain().ChainId). + Uint64(logs.FieldNonce, nonce). + Str(logs.FieldTx, txDigest). + Logger() + + ctx = logger.WithContext(ctx) + + bg.Work(ctx, + func(ctx context.Context) error { return s.reportOutboundTracker(ctx, nonce, txDigest) }, + bg.WithLogger(logger), + bg.WithName("report_outbound_tracker"), + ) + + return nil +} + +func (s *Signer) signTx(ctx context.Context, tx models.TxnMetaData, zetaHeight, nonce uint64) ([65]byte, error) { + digest, err := sui.Digest(tx) + if err != nil { + return [65]byte{}, errors.Wrap(err, "unable to get digest") + } + + // Another hashing is required for ECDSA. + // https://docs.sui.io/concepts/cryptography/transaction-auth/signatures#signature-requirements + digestWrapped := sha256.Sum256(digest[:]) + + // Send TSS signature request. + return s.TSS().Sign( + ctx, + digestWrapped[:], + zetaHeight, + nonce, + s.Chain().ChainId, + ) } diff --git a/zetaclient/chains/sui/signer/signer_test.go b/zetaclient/chains/sui/signer/signer_test.go new file mode 100644 index 0000000000..e98a2d5e42 --- /dev/null +++ b/zetaclient/chains/sui/signer/signer_test.go @@ -0,0 +1,210 @@ +package signer + +import ( + "context" + "encoding/base64" + "fmt" + "testing" + "time" + + "cosmossdk.io/math" + "github.com/block-vision/sui-go-sdk/models" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/contracts/sui" + "github.com/zeta-chain/node/testutil/sample" + cc "github.com/zeta-chain/node/x/crosschain/types" + "github.com/zeta-chain/node/zetaclient/chains/base" + "github.com/zeta-chain/node/zetaclient/keys" + "github.com/zeta-chain/node/zetaclient/testutils/mocks" + "github.com/zeta-chain/node/zetaclient/testutils/testlog" +) + +func TestSigner(t *testing.T) { + t.Run("ProcessCCTX", func(t *testing.T) { + // ARRANGE + ts := newTestSuite(t) + + const zetaHeight = 1000 + + // Given cctx + nonce := uint64(123) + amount := math.NewUint(100_000) + receiver := "0xdecb47015beebed053c19ef48fe4d722fa3870f567133d235ebe3a70da7b0000" + + cctx := sample.CrossChainTxV2(t, "0xABC123") + cctx.InboundParams.CoinType = coin.CoinType_Gas + cctx.OutboundParams = []*cc.OutboundParams{{ + Receiver: receiver, + ReceiverChainId: ts.Chain.ChainId, + CoinType: coin.CoinType_Gas, + Amount: amount, + TssNonce: nonce, + }} + + // Given mocked WithdrawCapID + const withdrawCapID = "0xWithdrawCapID" + ts.MockWithdrawCapID(withdrawCapID) + + // Given expected MoveCall + txBytes := base64.StdEncoding.EncodeToString([]byte("raw_tx_bytes")) + + ts.MockMoveCall(func(req models.MoveCallRequest) { + require.Equal(t, ts.TSS.PubKey().AddressSui(), req.Signer) + require.Equal(t, ts.Gateway.PackageID(), req.PackageObjectId) + require.Equal(t, "withdraw", req.Function) + + expectedArgs := []any{ + ts.Gateway.ObjectID(), + amount.String(), + fmt.Sprintf("%d", nonce), + receiver, + withdrawCapID, + } + require.Equal(t, expectedArgs, req.Arguments) + }, txBytes) + + // Given expected SuiExecuteTransactionBlock + const digest = "0xTransactionBlockDigest" + ts.MockExec(func(req models.SuiExecuteTransactionBlockRequest) { + require.Equal(t, txBytes, req.TxBytes) + require.NotEmpty(t, req.Signature) + }, digest) + + // Given included tx from Sui RPC + ts.SuiMock. + On("SuiGetTransactionBlock", mock.Anything, mock.Anything). + Return(models.SuiTransactionBlockResponse{ + Digest: digest, + Checkpoint: "1000000", + }, nil) + + // ACT + err := ts.Signer.ProcessCCTX(ts.Ctx, cctx, zetaHeight) + + // ASSERT + require.NoError(t, err) + + // Wait for vote posting + wait := func() bool { + if len(ts.TrackerBag) == 0 { + return false + } + + vote := ts.TrackerBag[0] + return vote.hash == digest && vote.nonce == nonce + } + + require.Eventually(t, wait, 5*time.Second, 100*time.Millisecond) + }) +} + +type testSuite struct { + t *testing.T + Ctx context.Context + + Chain chains.Chain + + TSS *mocks.TSS + Zetacore *mocks.ZetacoreClient + SuiMock *mocks.SuiClient + Gateway *sui.Gateway + + *Signer + + TrackerBag []testTracker +} + +func newTestSuite(t *testing.T) *testSuite { + var ( + ctx = context.Background() + + chain = chains.SuiMainnet + chainParams = mocks.MockChainParams(chain.ChainId, 10) + + tss = mocks.NewTSS(t) + zetacore = mocks.NewZetacoreClient(t).WithKeys(&keys.Keys{}) + + testLogger = testlog.New(t) + logger = base.Logger{Std: testLogger.Logger, Compliance: testLogger.Logger} + ) + + suiMock := mocks.NewSuiClient(t) + + gw, err := sui.NewGatewayFromPairID(chainParams.GatewayAddress) + require.NoError(t, err) + + baseSigner := base.NewSigner(chain, tss, logger) + signer := New(baseSigner, suiMock, gw, zetacore) + + ts := &testSuite{ + t: t, + Ctx: ctx, + Chain: chain, + TSS: tss, + Zetacore: zetacore, + SuiMock: suiMock, + Gateway: gw, + Signer: signer, + } + + // Setup mocks + ts.Zetacore.On("Chain").Return(chain).Maybe() + + ts.setupTrackersBag() + + return ts +} + +func (ts *testSuite) MockWithdrawCapID(id string) { + tss, structType := ts.TSS.PubKey().AddressSui(), ts.Gateway.WithdrawCapType() + ts.SuiMock.On("GetOwnedObjectID", mock.Anything, tss, structType).Return(id, nil) +} + +func (ts *testSuite) MockMoveCall(assert func(req models.MoveCallRequest), txBytesBase64 string) { + call := func(ctx context.Context, req models.MoveCallRequest) (models.TxnMetaData, error) { + assert(req) + return models.TxnMetaData{TxBytes: txBytesBase64}, nil + } + + ts.SuiMock.On("MoveCall", mock.Anything, mock.Anything).Return(call) +} + +func (ts *testSuite) MockExec(assert func(req models.SuiExecuteTransactionBlockRequest), digest string) { + call := func( + ctx context.Context, + req models.SuiExecuteTransactionBlockRequest, + ) (models.SuiTransactionBlockResponse, error) { + assert(req) + return models.SuiTransactionBlockResponse{Digest: digest}, nil + } + + ts.SuiMock.On("SuiExecuteTransactionBlock", mock.Anything, mock.Anything).Return(call) +} + +type testTracker struct { + nonce uint64 + hash string +} + +func (ts *testSuite) setupTrackersBag() { + catcher := func(args mock.Arguments) { + require.Equal(ts.t, ts.Chain.ChainId, args.Get(1).(int64)) + nonce := args.Get(2).(uint64) + txHash := args.Get(3).(string) + + ts.t.Logf("Adding outbound tracker: nonce=%d, hash=%s", nonce, txHash) + + ts.TrackerBag = append(ts.TrackerBag, testTracker{nonce, txHash}) + } + + ts.Zetacore.On( + "PostOutboundTracker", + mock.Anything, + mock.Anything, + mock.Anything, + mock.Anything, + ).Maybe().Run(catcher).Return("", nil) +} diff --git a/zetaclient/chains/sui/signer/signer_tracker.go b/zetaclient/chains/sui/signer/signer_tracker.go new file mode 100644 index 0000000000..36af4f8154 --- /dev/null +++ b/zetaclient/chains/sui/signer/signer_tracker.go @@ -0,0 +1,69 @@ +package signer + +import ( + "context" + "time" + + "github.com/block-vision/sui-go-sdk/models" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +// reportOutboundTracker queries the tx and sends its digest to the outbound tracker +// for further processing by the Observer. +func (s *Signer) reportOutboundTracker(ctx context.Context, nonce uint64, digest string) error { + // approx Sui checkpoint interval + const interval = 3 * time.Second + + // some sanity timeout + const maxTimeout = time.Minute + + logger := zerolog.Ctx(ctx) + + alreadySet := s.SetBeingReportedFlag(digest) + if alreadySet { + logger.Info().Msg("Outbound is already being observed for the tracker") + return nil + } + + start := time.Now() + attempts := 0 + + req := models.SuiGetTransactionBlockRequest{Digest: digest} + + defer s.ClearBeingReportedFlag(digest) + + for { + switch { + case time.Since(start) > maxTimeout: + return errors.Errorf("timeout reached (%s)", maxTimeout.String()) + case attempts == 0: + // best case we'd be able to report the tx ~immediately + time.Sleep(interval / 2) + default: + time.Sleep(interval) + } + attempts++ + + res, err := s.client.SuiGetTransactionBlock(ctx, req) + switch { + case ctx.Err() != nil: + return errors.Wrap(ctx.Err(), "Failed to get transaction block") + case err != nil: + logger.Error().Err(err).Msg("Failed to get transaction block") + continue + case res.Checkpoint == "": + // should not happen + logger.Error().Msg("Checkpoint is empty") + continue + default: + return s.postTrackerVote(ctx, nonce, digest) + } + } +} + +// note that at this point we don't care whether tx was successful or not. +func (s *Signer) postTrackerVote(ctx context.Context, nonce uint64, digest string) error { + _, err := s.zetacore.PostOutboundTracker(ctx, s.Chain().ChainId, nonce, digest) + return err +} diff --git a/zetaclient/chains/sui/signer/signer_tx.go b/zetaclient/chains/sui/signer/signer_tx.go new file mode 100644 index 0000000000..b6e3ea5cf9 --- /dev/null +++ b/zetaclient/chains/sui/signer/signer_tx.go @@ -0,0 +1,84 @@ +package signer + +import ( + "context" + "strconv" + + "github.com/block-vision/sui-go-sdk/models" + "github.com/pkg/errors" + + "github.com/zeta-chain/node/pkg/coin" + "github.com/zeta-chain/node/pkg/contracts/sui" + cctypes "github.com/zeta-chain/node/x/crosschain/types" +) + +const funcWithdraw = "withdraw" + +// buildWithdrawal builds unsigned withdrawal transaction using CCTX and Sui RPC +// https://github.com/zeta-chain/protocol-contracts-sui/blob/0245ad3a2eb4001381625070fd76c87c165589b2/sources/gateway.move#L117 +func (s *Signer) buildWithdrawal(ctx context.Context, cctx *cctypes.CrossChainTx) (tx models.TxnMetaData, err error) { + params := cctx.GetCurrentOutboundParam() + coinType := "" + + // Basic common-sense validation & coin-type determination + switch { + case params.ReceiverChainId != s.Chain().ChainId: + return tx, errors.Errorf("invalid receiver chain id %d", params.ReceiverChainId) + case cctx.ProtocolContractVersion != cctypes.ProtocolContractVersion_V2: + return tx, errors.Errorf("invalid protocol version %q", cctx.ProtocolContractVersion) + case cctx.InboundParams == nil: + return tx, errors.New("inbound params are nil") + case cctx.InboundParams.IsCrossChainCall: + return tx, errors.New("withdrawAndCall is not supported yet") + case params.CoinType == coin.CoinType_Gas: + coinType = string(sui.SUI) + case params.CoinType == coin.CoinType_ERC20: + coinType = cctx.InboundParams.Asset + default: + return tx, errors.Errorf("unsupported coin type %q", params.CoinType.String()) + } + + var ( + nonce = strconv.FormatUint(params.TssNonce, 10) + recipient = params.Receiver + amount = params.Amount.String() + gasBudget = strconv.FormatUint(params.CallOptions.GasLimit, 10) + ) + + withdrawCapID, err := s.getWithdrawCapIDCached(ctx) + if err != nil { + return tx, errors.Wrap(err, "unable to get withdraw cap ID") + } + + req := models.MoveCallRequest{ + Signer: s.TSS().PubKey().AddressSui(), + PackageObjectId: s.gateway.PackageID(), + Module: s.gateway.Module(), + Function: funcWithdraw, + TypeArguments: []any{coinType}, + Arguments: []any{s.gateway.ObjectID(), amount, nonce, recipient, withdrawCapID}, + GasBudget: gasBudget, + } + + return s.client.MoveCall(ctx, req) +} + +// broadcast attaches signature to tx and broadcasts it to Sui network. Returns tx digest. +func (s *Signer) broadcast(ctx context.Context, tx models.TxnMetaData, sig [65]byte) (string, error) { + sigBase64, err := sui.SerializeSignatureECDSA(sig, s.TSS().PubKey().AsECDSA()) + if err != nil { + return "", errors.Wrap(err, "unable to serialize signature") + } + + req := models.SuiExecuteTransactionBlockRequest{ + TxBytes: tx.TxBytes, + Signature: []string{sigBase64}, + } + + res, err := s.client.SuiExecuteTransactionBlock(ctx, req) + if err != nil { + return "", errors.Wrap(err, "unable to execute tx block") + } + + return res.Digest, nil +} diff --git a/zetaclient/chains/sui/signer/signer_withdrawcap.go b/zetaclient/chains/sui/signer/signer_withdrawcap.go new file mode 100644 index 0000000000..2c38b495db --- /dev/null +++ b/zetaclient/chains/sui/signer/signer_withdrawcap.go @@ -0,0 +1,72 @@ +package signer + +import ( + "context" + "sync" + "time" + + "github.com/pkg/errors" +) + +const withdrawCapTTL = 5 * time.Minute + +// withdrawCap represents WithdrawCap (capability) object +// that is required as a "permission" to withdraw funds. +// Should belong to TSS address on Sui. +type withdrawCap struct { + objectID string + mu sync.RWMutex + fetchedAt time.Time +} + +func (wc *withdrawCap) valid() bool { + wc.mu.RLock() + defer wc.mu.RUnlock() + + if wc.objectID == "" { + return false + } + + return time.Since(wc.fetchedAt) < withdrawCapTTL +} + +func (wc *withdrawCap) set(objectID string) { + wc.mu.Lock() + defer wc.mu.Unlock() + + wc.objectID = objectID + wc.fetchedAt = time.Now() +} + +// getWithdrawCapIDCached getWithdrawCapID with withdrawCapTTL cache. +func (s *Signer) getWithdrawCapIDCached(ctx context.Context) (string, error) { + if s.withdrawCap.valid() { + return s.withdrawCap.objectID, nil + } + + s.Logger().Std.Info().Msg("WithdrawCap cache expired, fetching new objectID") + + objectID, err := s.getWithdrawCapID(ctx) + if err != nil { + return "", errors.Wrap(err, "unable to get withdraw cap ID") + } + + s.withdrawCap.set(objectID) + + s.Logger().Std.Info().Str("sui.object_id", objectID).Msg("WithdrawCap objectID fetched") + + return objectID, nil +} + +// getWithdrawCapID returns the objectID of the WithdrawCap. Should belong to TSS address on Sui. +func (s *Signer) getWithdrawCapID(ctx context.Context) (string, error) { + owner := s.TSS().PubKey().AddressSui() + structType := s.gateway.WithdrawCapType() + + objectID, err := s.client.GetOwnedObjectID(ctx, owner, structType) + if err != nil { + return "", errors.Wrap(err, "unable to get owned object ID") + } + + return objectID, nil +} diff --git a/zetaclient/chains/sui/sui.go b/zetaclient/chains/sui/sui.go index 61a3a43dcc..60a44912c5 100644 --- a/zetaclient/chains/sui/sui.go +++ b/zetaclient/chains/sui/sui.go @@ -6,10 +6,13 @@ import ( "time" "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/zeta-chain/node/pkg/bg" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/scheduler" "github.com/zeta-chain/node/pkg/ticker" + "github.com/zeta-chain/node/zetaclient/chains/base" "github.com/zeta-chain/node/zetaclient/chains/sui/observer" "github.com/zeta-chain/node/zetaclient/chains/sui/signer" zctx "github.com/zeta-chain/node/zetaclient/context" @@ -22,6 +25,14 @@ type Sui struct { signer *signer.Signer } +const ( + // outboundLookbackFactor is the factor to determine how many nonces to look back for pending cctxs + // For example, give OutboundScheduleLookahead of 120, pending NonceLow of 1000 and factor of 1.0, + // the scheduler need to be able to pick up and schedule any pending cctx with nonce < 880 (1000 - 120 * 1.0) + // NOTE: 1.0 means look back the same number of cctxs as we look ahead + outboundLookbackFactor = 1.0 +) + // New Sui observer-signer constructor. func New(scheduler *scheduler.Scheduler, observer *observer.Observer, signer *signer.Signer) *Sui { return &Sui{scheduler, observer, signer} @@ -71,6 +82,10 @@ func (s *Sui) Start(ctx context.Context) error { return ticker.DurationFromUint64Seconds(s.observer.ChainParams().InboundTicker) }) + optOutboundInterval := scheduler.IntervalUpdater(func() time.Duration { + return ticker.DurationFromUint64Seconds(s.observer.ChainParams().OutboundTicker) + }) + optGasInterval := scheduler.IntervalUpdater(func() time.Duration { return ticker.DurationFromUint64Seconds(s.observer.ChainParams().GasPriceTicker) }) @@ -87,6 +102,7 @@ func (s *Sui) Start(ctx context.Context) error { register(s.observer.ProcessInboundTrackers, "process_inbound_trackers", optInboundInterval, optInboundSkipper) register(s.observer.CheckRPCStatus, "check_rpc_status") register(s.observer.PostGasPrice, "post_gas_price", optGasInterval, optGenericSkipper) + register(s.observer.ProcessOutboundTrackers, "process_outbound_trackers", optOutboundInterval, optOutboundSkipper) // CCTX scheduler (every zetachain block) register(s.scheduleCCTX, "schedule_cctx", scheduler.BlockTicker(newBlockChan), optOutboundSkipper) @@ -105,7 +121,113 @@ func (s *Sui) group() scheduler.Group { } // scheduleCCTX schedules outbound cross-chain transactions. -func (s *Sui) scheduleCCTX(_ context.Context) error { - // todo +func (s *Sui) scheduleCCTX(ctx context.Context) error { + if err := s.updateChainParams(ctx); err != nil { + return errors.Wrap(err, "unable to update chain params") + } + + zetaBlock, delay, err := scheduler.BlockFromContextWithDelay(ctx) + if err != nil { + return errors.Wrap(err, "unable to get zeta block from context") + } + + time.Sleep(delay) + + cctxList, _, err := s.observer.ZetacoreClient().ListPendingCCTX(ctx, s.observer.Chain()) + if err != nil { + return errors.Wrap(err, "unable to list pending cctx") + } + + // noop + if len(cctxList) == 0 { + return nil + } + + var ( + // #nosec G115 always in range + zetaHeight = uint64(zetaBlock.Block.Height) + chainID = s.observer.Chain().ChainId + + lookahead = s.observer.ChainParams().OutboundScheduleLookahead + // #nosec G115 always in range + lookback = uint64(float64(lookahead) * outboundLookbackFactor) + + firstNonce = cctxList[0].GetCurrentOutboundParam().TssNonce + maxNonce = firstNonce + lookback + ) + + for i := range cctxList { + var ( + cctx = cctxList[i] + outboundID = base.OutboundIDFromCCTX(cctx) + outboundParams = cctx.GetCurrentOutboundParam() + nonce = outboundParams.TssNonce + ) + + switch { + case int64(i) == lookahead: + // take only first N cctxs + return nil + case outboundParams.ReceiverChainId != chainID: + // should not happen + s.outboundLogger(outboundID).Error().Msg("chain id mismatch") + continue + case nonce >= maxNonce: + return fmt.Errorf("nonce %d is too high (%s). Earliest nonce %d", nonce, outboundID, firstNonce) + case s.signer.IsOutboundActive(outboundID): + // cctx is already being processed & broadcasted by signer + continue + case s.observer.OutboundCreated(nonce): + // ProcessOutboundTrackers HAS fetched existing Sui outbound, + // Let's report this by voting to zetacore + if err := s.observer.VoteOutbound(ctx, cctx); err != nil { + s.outboundLogger(outboundID).Error().Err(err).Msg("VoteOutbound failed") + } + continue + } + + // Here we have a cctx that needs to be scheduled. Let's invoke async operation. + // - Signer will build, sign & broadcast the tx. + // - It will also monitor Sui to report outbound tracker + // so we'd have a pair of (tss_nonce -> sui tx hash) + // - Then this pair will be handled by ProcessOutboundTrackers -> OutboundCreated -> VoteOutbound + bg.Work(ctx, func(ctx context.Context) error { + if err := s.signer.ProcessCCTX(ctx, cctx, zetaHeight); err != nil { + s.outboundLogger(outboundID).Error().Err(err).Msg("ProcessCCTX failed") + } + + return nil + }) + } + return nil } + +func (s *Sui) updateChainParams(ctx context.Context) error { + app, err := zctx.FromContext(ctx) + if err != nil { + return err + } + + chain, err := app.GetChain(s.observer.Chain().ChainId) + if err != nil { + return err + } + + params := chain.Params() + + s.observer.SetChainParams(*params) + + // note that address should be in format of `$packageID,$gatewayObjectID` + if err := s.observer.Gateway().UpdateIDs(params.GatewayAddress); err != nil { + return errors.Wrap(err, "unable to update gateway ids") + } + + return nil +} + +func (s *Sui) outboundLogger(id string) *zerolog.Logger { + l := s.observer.Logger().Outbound.With().Str("outbound.id", id).Logger() + + return &l +} diff --git a/zetaclient/chains/ton/config/config.go b/zetaclient/chains/ton/config/config.go index c970a68baa..e0621fb813 100644 --- a/zetaclient/chains/ton/config/config.go +++ b/zetaclient/chains/ton/config/config.go @@ -59,11 +59,16 @@ func FromPath(path string) (*GlobalConfigurationFile, error) { // FromSource returns a parsed configuration file from a URL or a file path. func FromSource(ctx context.Context, urlOrPath string) (*GlobalConfigurationFile, error) { - if u, err := url.Parse(urlOrPath); err == nil { - return FromURL(ctx, u.String()) + if cfg, err := FromPath(urlOrPath); err == nil { + return cfg, nil } - return FromPath(urlOrPath) + u, err := url.Parse(urlOrPath) + if err != nil { + return nil, errors.Wrap(err, "failed to parse URL") + } + + return FromURL(ctx, u.String()) } // FetchGasConfig fetches gas price from the config. diff --git a/zetaclient/chains/ton/config/config_test.go b/zetaclient/chains/ton/config/config_test.go new file mode 100644 index 0000000000..c9202cf858 --- /dev/null +++ b/zetaclient/chains/ton/config/config_test.go @@ -0,0 +1,81 @@ +package config + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestConfig(t *testing.T) { + ctx := context.Background() + + t.Run("FromSource_path", func(t *testing.T) { + // ARRANGE + tmpDir := t.TempDir() + defer os.RemoveAll(tmpDir) + + configPath := filepath.Join(tmpDir, "config.json") + require.NoError(t, os.WriteFile(configPath, sampleConfigBytes(t), 0644)) + + // ACT + cfg, err := FromSource(ctx, configPath) + + // ASSERT + require.NoError(t, err) + require.NotNil(t, cfg) + }) + + t.Run("FromSource_url", func(t *testing.T) { + // ARRANGE + cfgBytes := sampleConfigBytes(t) + + handler := func(w http.ResponseWriter, r *http.Request) { + w.Write(cfgBytes) + } + + server := httptest.NewServer(http.HandlerFunc(handler)) + defer server.Close() + + url := fmt.Sprintf("%s/config.json", server.URL) + + // ACT + cfg, err := FromSource(ctx, url) + + // ASSERT + require.NoError(t, err) + require.NotNil(t, cfg) + }) +} + +func sampleConfigBytes(t *testing.T) []byte { + // https://ton.org/testnet-global.config.json + const sampleConfig = `{ + "liteservers": [ + { + "ip": 822907680, + "port": 27842, + "provided":"Beavis", + "id": { + "@type": "pub.ed25519", + "key": "sU7QavX2F964iI9oToP9gffQpCQIoOLppeqL/pdPvpM=" + } + }, + { + "ip": 1091956407, + "port": 16351, + "id": { + "@type": "pub.ed25519", + "key": "Mf/JGvcWAvcrN3oheze8RF/ps6p7oL6ifrIzFmGQFQ8=" + } + } + ] + }` + + return []byte(sampleConfig) +} diff --git a/zetaclient/compliance/compliance_test.go b/zetaclient/compliance/compliance_test.go index fd86cab2ea..9bb12a1a3e 100644 --- a/zetaclient/compliance/compliance_test.go +++ b/zetaclient/compliance/compliance_test.go @@ -23,29 +23,29 @@ func TestCctxRestricted(t *testing.T) { t.Run("should return true if sender is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.InboundParams.Sender} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return true if receiver is restricted", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{cctx.GetCurrentOutboundParam().Receiver} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) require.True(t, IsCctxRestricted(cctx)) }) t.Run("should return false if sender and receiver are not restricted", func(t *testing.T) { // restrict other address cfg.ComplianceConfig.RestrictedAddresses = []string{"0x27104b8dB4aEdDb054fCed87c346C0758Ff5dFB1"} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) require.False(t, IsCctxRestricted(cctx)) }) t.Run("should be able to restrict coinbase address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{ethcommon.Address{}.String()} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) cctx.InboundParams.Sender = ethcommon.Address{}.String() require.True(t, IsCctxRestricted(cctx)) }) t.Run("should ignore empty address", func(t *testing.T) { cfg.ComplianceConfig.RestrictedAddresses = []string{""} - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) cctx.InboundParams.Sender = "" require.False(t, IsCctxRestricted(cctx)) }) diff --git a/zetaclient/config/config.go b/zetaclient/config/config.go index 8bd3e9eff9..3714627d33 100644 --- a/zetaclient/config/config.go +++ b/zetaclient/config/config.go @@ -2,17 +2,24 @@ package config import ( + "context" "encoding/json" "fmt" "os" "path/filepath" "strings" + "sync" + "github.com/fsnotify/fsnotify" "github.com/pkg/errors" + "github.com/rs/zerolog" ) // restrictedAddressBook is a map of restricted addresses var restrictedAddressBook = map[string]bool{} +var restrictedAddressBookLock sync.RWMutex + +const restrictedAddressesPath string = "zetaclient_restricted_addresses.json" // filename is config file name for ZetaClient const filename string = "zetaclient_config.json" @@ -45,9 +52,9 @@ func Save(config *Config, path string) error { } // Load loads ZetaClient config from a filepath -func Load(path string) (Config, error) { +func Load(basePath string) (Config, error) { // retrieve file - file := filepath.Join(path, folder, filename) + file := filepath.Join(basePath, folder, filename) file, err := filepath.Abs(file) if err != nil { return Config{}, err @@ -76,19 +83,114 @@ func Load(path string) (Config, error) { // fields sanitization cfg.TssPath = GetPath(cfg.TssPath) cfg.PreParamsPath = GetPath(cfg.PreParamsPath) - cfg.ZetaCoreHome = path - - // load compliance config - LoadComplianceConfig(cfg) + cfg.ZetaCoreHome = basePath return cfg, nil } -// LoadComplianceConfig loads compliance data (restricted addresses) from config -func LoadComplianceConfig(cfg Config) { +// SetRestrictedAddressesFromConfig loads compliance data (restricted addresses) from config. +func SetRestrictedAddressesFromConfig(cfg Config) { restrictedAddressBook = cfg.GetRestrictedAddressBook() } +func getRestrictedAddressAbsPath(basePath string) (string, error) { + file := filepath.Join(basePath, folder, restrictedAddressesPath) + file, err := filepath.Abs(file) + if err != nil { + return "", errors.Wrapf(err, "absolute path conversion for %s", file) + } + return file, nil +} + +func loadRestrictedAddressesConfig(cfg Config, file string) error { + input, err := os.ReadFile(file) // #nosec G304 + if err != nil { + return errors.Wrapf(err, "reading file %s", file) + } + addresses := []string{} + err = json.Unmarshal(input, &addresses) + if err != nil { + return errors.Wrap(err, "invalid json") + } + + restrictedAddressBookLock.Lock() + defer restrictedAddressBookLock.Unlock() + + // Clear the existing map, load addresses from main config, then load addresses + // from dedicated config file + SetRestrictedAddressesFromConfig(cfg) + for _, addr := range cfg.ComplianceConfig.RestrictedAddresses { + restrictedAddressBook[strings.ToLower(addr)] = true + } + return nil +} + +// LoadRestrictedAddressesConfig loads the restricted addresses from the config file +func LoadRestrictedAddressesConfig(cfg Config, basePath string) error { + file, err := getRestrictedAddressAbsPath(basePath) + if err != nil { + return errors.Wrap(err, "getting restricted address path") + } + return loadRestrictedAddressesConfig(cfg, file) +} + +// WatchRestrictedAddressesConfig monitors the restricted addresses config file +// for changes and reloads it when necessary +func WatchRestrictedAddressesConfig(ctx context.Context, cfg Config, basePath string, logger zerolog.Logger) error { + file, err := getRestrictedAddressAbsPath(basePath) + if err != nil { + return errors.Wrap(err, "getting restricted address path") + } + watcher, err := fsnotify.NewWatcher() + if err != nil { + return errors.Wrap(err, "creating file watcher") + } + defer watcher.Close() + + // Watch the config directory + // If you only watch the file, the watch will be disconnected if/when + // the config is recreated. + dir := filepath.Dir(file) + err = watcher.Add(dir) + if err != nil { + return errors.Wrapf(err, "watching directory %s", dir) + } + + for { + select { + case <-ctx.Done(): + return nil + + case event, ok := <-watcher.Events: + if !ok { + return nil + } + + if event.Name != file { + continue + } + + // only reload on create or write + if event.Op&(fsnotify.Write|fsnotify.Create) == 0 { + continue + } + + logger.Info().Msg("restricted addresses config updated") + + err := loadRestrictedAddressesConfig(cfg, file) + if err != nil { + logger.Err(err).Msg("load restricted addresses config") + } + + case err, ok := <-watcher.Errors: + if !ok { + return nil + } + return errors.Wrap(err, "watcher error") + } + } +} + // GetPath returns the absolute path of the input path func GetPath(inputPath string) string { path := strings.Split(inputPath, "/") @@ -109,6 +211,8 @@ func GetPath(inputPath string) string { // ContainRestrictedAddress returns true if any one of the addresses is restricted // Note: the addrs can contains both ETH and BTC addresses func ContainRestrictedAddress(addrs ...string) bool { + restrictedAddressBookLock.RLock() + defer restrictedAddressBookLock.RUnlock() for _, addr := range addrs { if addr != "" && restrictedAddressBook[strings.ToLower(addr)] { return true diff --git a/zetaclient/config/types.go b/zetaclient/config/types.go index ed27bc9e33..375c83aa47 100644 --- a/zetaclient/config/types.go +++ b/zetaclient/config/types.go @@ -68,7 +68,8 @@ type TONConfig struct { // ComplianceConfig is the config for compliance type ComplianceConfig struct { - LogPath string `json:"LogPath"` + LogPath string `json:"LogPath"` + // Deprecated: use the separate restricted addresses config RestrictedAddresses []string `json:"RestrictedAddresses" mask:"zero"` } diff --git a/zetaclient/logs/fields.go b/zetaclient/logs/fields.go index 6f0b60b423..9c267030db 100644 --- a/zetaclient/logs/fields.go +++ b/zetaclient/logs/fields.go @@ -8,6 +8,7 @@ const ( FieldChain = "chain" FieldChainNetwork = "chain_network" FieldNonce = "nonce" + FieldTracker = "tracker_id" FieldTx = "tx" FieldOutboundID = "outbound_id" FieldBlock = "block" diff --git a/zetaclient/orchestrator/v2_bootstrap.go b/zetaclient/orchestrator/v2_bootstrap.go index 76fd5927d7..b1f573ad34 100644 --- a/zetaclient/orchestrator/v2_bootstrap.go +++ b/zetaclient/orchestrator/v2_bootstrap.go @@ -200,18 +200,22 @@ func (oc *V2) bootstrapSui(ctx context.Context, chain zctx.Chain) (*sui.Sui, err return nil, errors.Wrap(errSkipChain, "unable to find sui config") } - baseObserver, err := oc.newBaseObserver(chain, chain.Name()) + // note that gw address should be in format of `$packageID,$gatewayObjectID` + gateway, err := suigateway.NewGatewayFromPairID(chain.Params().GatewayAddress) if err != nil { - return nil, errors.Wrap(err, "unable to create base observer") + return nil, errors.Wrap(err, "unable to create gateway") } suiClient := suiclient.NewFromEndpoint(cfg.Endpoint) - gateway := suigateway.NewGateway(chain.Params().GatewayAddress) + baseObserver, err := oc.newBaseObserver(chain, chain.Name()) + if err != nil { + return nil, errors.Wrap(err, "unable to create base observer") + } observer := suiobserver.New(baseObserver, suiClient, gateway) - signer := suisigner.New(oc.newBaseSigner(chain)) + signer := suisigner.New(oc.newBaseSigner(chain), suiClient, gateway, oc.deps.Zetacore) return sui.New(oc.scheduler, observer, signer), nil } diff --git a/zetaclient/orchestrator/v2_bootstrap_test.go b/zetaclient/orchestrator/v2_bootstrap_test.go index dc40592459..2c2741006e 100644 --- a/zetaclient/orchestrator/v2_bootstrap_test.go +++ b/zetaclient/orchestrator/v2_bootstrap_test.go @@ -75,6 +75,12 @@ func TestBootstrap(t *testing.T) { cfg.EVMChainConfigs[chains.Polygon.ChainId] = config.EVMConfig{ Endpoint: maticServer.Endpoint, } + + // disable other chains + cfg.BTCChainConfigs = nil + cfg.SolanaConfig.Endpoint = "" + cfg.SuiConfig.Endpoint = "" + cfg.TONConfig.LiteClientConfigURL = "" }) // Mock zetacore calls diff --git a/zetaclient/orchestrator/v2_orchestrator_test.go b/zetaclient/orchestrator/v2_orchestrator_test.go index 76b2422d12..8565a9ca4a 100644 --- a/zetaclient/orchestrator/v2_orchestrator_test.go +++ b/zetaclient/orchestrator/v2_orchestrator_test.go @@ -123,7 +123,7 @@ func newTestSuite(t *testing.T) *testSuite { // Services var ( - schedulerService = scheduler.New(logger.Logger) + schedulerService = scheduler.New(logger.Logger, time.Second) zetacore = mocks.NewZetacoreClient(t) tss = mocks.NewTSS(t) ) diff --git a/zetaclient/testdata/solana/chain_901_outbound_tx_result_4ZuPTkYtBGDyDZNHKyHxEKL98VeaefAMUzmZVL2BrgwCvog7CqpjrRoegXDt6bD7w8dffGKGcDZqFYFi5vkAK8eo.json b/zetaclient/testdata/solana/chain_901_outbound_tx_result_4ZuPTkYtBGDyDZNHKyHxEKL98VeaefAMUzmZVL2BrgwCvog7CqpjrRoegXDt6bD7w8dffGKGcDZqFYFi5vkAK8eo.json new file mode 100644 index 0000000000..1c5b1190cd --- /dev/null +++ b/zetaclient/testdata/solana/chain_901_outbound_tx_result_4ZuPTkYtBGDyDZNHKyHxEKL98VeaefAMUzmZVL2BrgwCvog7CqpjrRoegXDt6bD7w8dffGKGcDZqFYFi5vkAK8eo.json @@ -0,0 +1,108 @@ +{ + "blockTime": 1740070795, + "meta": { + "computeUnitsConsumed": 79707, + "err": null, + "fee": 5000, + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "accounts": [ + 3, + 1, + 4, + 5 + ], + "data": "Qj4no588tu2RksabYK3y3pQYzHBqRCDjYKJAKTpJmARF93XWomZBGVyY1UVNe", + "programIdIndex": 2, + "stackHeight": 2 + } + ] + } + ], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: Execute", + "Program log: Computed message hash: [47, 152, 48, 18, 189, 52, 199, 141, 141, 107, 47, 194, 114, 167, 21, 198, 49, 255, 210, 144, 228, 234, 48, 48, 235, 226, 100, 53, 97, 94, 179, 49]", + "Program log: Recovered address [242, 236, 163, 253, 90, 21, 46, 181, 185, 206, 188, 167, 228, 146, 198, 104, 202, 9, 205, 211]", + "Program 4xEw862A2SEwMjofPkUyd4NEekmVJKJsdHkK3UkAtDrc invoke [2]", + "Program log: Instruction: OnCall", + "Program log: On call executed with amount 1000000, sender [16, 63, 217, 34, 79, 0, 206, 48, 19, 233, 86, 41, 229, 45, 252, 49, 216, 5, 214, 141] and message hello", + "Program 4xEw862A2SEwMjofPkUyd4NEekmVJKJsdHkK3UkAtDrc consumed 11322 of 149084 compute units", + "Program 4xEw862A2SEwMjofPkUyd4NEekmVJKJsdHkK3UkAtDrc success", + "Program log: Execute done: destination contract = 1000000, amount = 4xEw862A2SEwMjofPkUyd4NEekmVJKJsdHkK3UkAtDrc, sender = [16, 63, 217, 34, 79, 0, 206, 48, 19, 233, 86, 41, 229, 45, 252, 49, 216, 5, 214, 141]", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 79707 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "postBalances": [ + 99999990000, + 25447680, + 1141440, + 1947680, + 999966657520, + 1, + 1141440 + ], + "postTokenBalances": [], + "preBalances": [ + 99999995000, + 26447680, + 1141440, + 1447680, + 999966157520, + 1, + 1141440 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 510, + "transaction": { + "message": { + "accountKeys": [ + "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "4xEw862A2SEwMjofPkUyd4NEekmVJKJsdHkK3UkAtDrc", + "VqaCSRDVj4vDupnETzRshW69LsFXovkZwd65EeZA8ru", + "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ", + "11111111111111111111111111111111", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 0, + 1, + 2, + 3, + 3, + 1, + 4, + 5 + ], + "data": "H3UF2NoybtJj4qWbWhTYMkLNBaVRLTG7GTgkuAaPZ44uKXmaaSZG5Ae4iBXVYqX2zViQzA1Z4kd3Tcuoo73GJ28n7iMYMtgvjNWki6X32b8h21s83TsSDu7BBFPNA1YvC4yaSgUJ4uVjPSf9EfziZsFrtmVrirWh1jqenoKnQXMT799yvJC9rwPnbFk78ZSCekarLBNAEYtQP", + "programIdIndex": 6, + "stackHeight": null + } + ], + "recentBlockhash": "2PAQoYg7XZ4WLo1htU483HK3HG5QCicF4BudqPzAft9M" + }, + "signatures": [ + "4ZuPTkYtBGDyDZNHKyHxEKL98VeaefAMUzmZVL2BrgwCvog7CqpjrRoegXDt6bD7w8dffGKGcDZqFYFi5vkAK8eo" + ] + } +} \ No newline at end of file diff --git a/zetaclient/testdata/solana/chain_901_outbound_tx_result_5dpFTsscUKCGVQzL9bAUSuEE6yLXaf7d1wMjZa7RLqvtSUtAdfcdxQHNsbfcS2Sfzu4zBVxMJC2KWzuaUUbg1ZGk.json b/zetaclient/testdata/solana/chain_901_outbound_tx_result_5dpFTsscUKCGVQzL9bAUSuEE6yLXaf7d1wMjZa7RLqvtSUtAdfcdxQHNsbfcS2Sfzu4zBVxMJC2KWzuaUUbg1ZGk.json new file mode 100644 index 0000000000..10f28c7bdc --- /dev/null +++ b/zetaclient/testdata/solana/chain_901_outbound_tx_result_5dpFTsscUKCGVQzL9bAUSuEE6yLXaf7d1wMjZa7RLqvtSUtAdfcdxQHNsbfcS2Sfzu4zBVxMJC2KWzuaUUbg1ZGk.json @@ -0,0 +1,67 @@ +{ + "blockTime": 1740070842, + "meta": { + "computeUnitsConsumed": 47687, + "err": null, + "fee": 5000, + "innerInstructions": [], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: IncrementNonce", + "Program log: Computed message hash: [72, 247, 17, 19, 98, 106, 253, 89, 163, 125, 180, 224, 183, 214, 91, 148, 171, 154, 240, 91, 170, 155, 196, 20, 210, 253, 224, 238, 133, 178, 253, 90]", + "Program log: Recovered address [242, 236, 163, 253, 90, 21, 46, 181, 185, 206, 188, 167, 228, 146, 198, 104, 202, 9, 205, 211]", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 47687 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "postBalances": [ + 99999985000, + 25447680, + 1141440 + ], + "postTokenBalances": [], + "preBalances": [ + 99999990000, + 25447680, + 1141440 + ], + "preTokenBalances": [], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 607, + "transaction": { + "message": { + "accountKeys": [ + "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 0, + 1 + ], + "data": "tkRdUtxp2aFXkrWEGCDmNQxX6P6uUCt1RvHPWeX3w2cdz37SU815J2ZprZWESkTdL5uxMVAQjSW4mKTzC8coQG8AGwm3Mw3svG2AyGUuUHBuhpWynrMTd9sXD8FJDDtQFpPswxJk9MSZMqKUZnmXd59xVdCQEL4gxL1uq", + "programIdIndex": 2, + "stackHeight": null + } + ], + "recentBlockhash": "9dwcaNuhuKZ7QdSDQvoRt4tY2JZU5oZyRd8hLHSKrFCZ" + }, + "signatures": [ + "5dpFTsscUKCGVQzL9bAUSuEE6yLXaf7d1wMjZa7RLqvtSUtAdfcdxQHNsbfcS2Sfzu4zBVxMJC2KWzuaUUbg1ZGk" + ] + } +} \ No newline at end of file diff --git a/zetaclient/testdata/solana/chain_901_outbound_tx_result_d3WvqtwFws9yftpxSrmwXqb48ZbBVjvxz34zY5Mo9TxaAPxsudPa68nDXZeShvK8UqtM84TgGfpdrgeX65q5WCW.json b/zetaclient/testdata/solana/chain_901_outbound_tx_result_d3WvqtwFws9yftpxSrmwXqb48ZbBVjvxz34zY5Mo9TxaAPxsudPa68nDXZeShvK8UqtM84TgGfpdrgeX65q5WCW.json new file mode 100644 index 0000000000..decc0edd2c --- /dev/null +++ b/zetaclient/testdata/solana/chain_901_outbound_tx_result_d3WvqtwFws9yftpxSrmwXqb48ZbBVjvxz34zY5Mo9TxaAPxsudPa68nDXZeShvK8UqtM84TgGfpdrgeX65q5WCW.json @@ -0,0 +1,244 @@ +{ + "blockTime": 1740071109, + "meta": { + "computeUnitsConsumed": 139958, + "err": null, + "fee": 5000, + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "accounts": [ + 2, + 6, + 3, + 1 + ], + "data": "gvPShZQhKrzGM", + "programIdIndex": 8, + "stackHeight": 2 + }, + { + "accounts": [ + 4, + 3, + 6, + 1, + 11, + 5, + 8, + 10 + ], + "data": "Qj4no588tu2RksabYK3y3pQYzHBqRCDjYKJAKTpJmARF93XWomZBGVyY1UVNe", + "programIdIndex": 7, + "stackHeight": 2 + }, + { + "accounts": [ + 3, + 6, + 5, + 4 + ], + "data": "gX37MVsfGUBn5", + "programIdIndex": 8, + "stackHeight": 3 + } + ] + } + ], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d invoke [1]", + "Program log: Instruction: ExecuteSplToken", + "Program log: Computed message hash: [127, 87, 107, 159, 251, 178, 101, 24, 62, 24, 74, 254, 65, 174, 173, 71, 102, 52, 208, 68, 228, 95, 93, 44, 249, 190, 171, 234, 176, 226, 53, 68]", + "Program log: Recovered address [242, 236, 163, 253, 90, 21, 46, 181, 185, 206, 188, 167, 228, 146, 198, 104, 202, 9, 205, 211]", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: TransferChecked", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6201 of 128629 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program 8iUjRRhUCn8BjrvsWPfj8mguTe9L81ES4oAUApiF8JFC invoke [2]", + "Program log: Instruction: OnCall", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [3]", + "Program log: Instruction: TransferChecked", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6201 of 108415 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program log: On call executed with amount 1000000, sender [16, 63, 217, 34, 79, 0, 206, 48, 19, 233, 86, 41, 229, 45, 252, 49, 216, 5, 214, 141] and message hello", + "Program 8iUjRRhUCn8BjrvsWPfj8mguTe9L81ES4oAUApiF8JFC consumed 23863 of 119713 compute units", + "Program 8iUjRRhUCn8BjrvsWPfj8mguTe9L81ES4oAUApiF8JFC success", + "Program log: Execute SPL done: amount = 1000000, decimals = 6, recipient = 9d5kAQjewKUki56CfoAnX3CbPt8wskHXJgNqkm82BeNo, mint = B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz, pda = 9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d consumed 139958 of 200000 compute units", + "Program 94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d success" + ], + "postBalances": [ + 99999970000, + 40942680, + 2039280, + 2039280, + 1447680, + 2039280, + 1461600, + 1141440, + 929020800, + 731913600, + 1, + 999946996960, + 1141440 + ], + "postTokenBalances": [ + { + "accountIndex": 2, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "34900000", + "decimals": 6, + "uiAmount": 34.9, + "uiAmountString": "34.9" + } + }, + { + "accountIndex": 3, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "9d5kAQjewKUki56CfoAnX3CbPt8wskHXJgNqkm82BeNo", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "500000", + "decimals": 6, + "uiAmount": 0.5, + "uiAmountString": "0.5" + } + }, + { + "accountIndex": 5, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "99999964600000", + "decimals": 6, + "uiAmount": 99999964.6, + "uiAmountString": "99999964.6" + } + } + ], + "preBalances": [ + 99999975000, + 40942680, + 2039280, + 2039280, + 1447680, + 2039280, + 1461600, + 1141440, + 929020800, + 731913600, + 1, + 999946996960, + 1141440 + ], + "preTokenBalances": [ + { + "accountIndex": 2, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "35900000", + "decimals": 6, + "uiAmount": 35.9, + "uiAmountString": "35.9" + } + }, + { + "accountIndex": 3, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "9d5kAQjewKUki56CfoAnX3CbPt8wskHXJgNqkm82BeNo", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "0", + "decimals": 6, + "uiAmount": null, + "uiAmountString": "0" + } + }, + { + "accountIndex": 5, + "mint": "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "owner": "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "99999964100000", + "decimals": 6, + "uiAmount": 99999964.1, + "uiAmountString": "99999964.1" + } + } + ], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 1163, + "transaction": { + "message": { + "accountKeys": [ + "2qBVcNBZCubcnSR3NyCnFjCfkCVUB3G7ECPoaW5rxVjx", + "9dcAyYG4bawApZocwZSyJBi9Mynf5EuKAJfifXdfkqik", + "G3ov8DJFuqnzb4oSDejp3arUnJ5S4sYgPB5PVhHM3xgs", + "ERncAqevWezEB5HuTRF6mCrnJSRnAUwGVAQKBd3yq4NJ", + "9d5kAQjewKUki56CfoAnX3CbPt8wskHXJgNqkm82BeNo", + "HmHyB8mx19rRLRjpgwcJfNNU2QDx9JkvkSto2fqnxqz4", + "B4QuaxoateLvrWA48MvoYXf1tvBGTErbj6C6t4dyHKTz", + "8iUjRRhUCn8BjrvsWPfj8mguTe9L81ES4oAUApiF8JFC", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "11111111111111111111111111111111", + "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ", + "94U5AHQMKkV5txNJ17QPXWoh474PheGou6cNP2FEuL1d" + ], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 7, + "numRequiredSignatures": 1 + }, + "instructions": [ + { + "accounts": [ + 0, + 1, + 2, + 6, + 7, + 4, + 3, + 8, + 9, + 10, + 4, + 3, + 6, + 1, + 11, + 5, + 8, + 10 + ], + "data": "28gRk4vboxttdNAEPV6roTvKULX96fmierULcuyofq8NHFmyKatnfoeBCD2pNk9ZWWiVBqu1WRSKRAqgkNrW1fhopFkFjjCM4DdyA6ZtDysa33Sq8K6fknMHMQPDR5BFCK6PL99MTtjgkzJvsHex71ARRG4RowGggapkvR5t876DaS8dzS7Bc44Ai2Z6U7sM31BsQ2Sov7GBUMm", + "programIdIndex": 12, + "stackHeight": null + } + ], + "recentBlockhash": "8ZfeVR14evcAbnjV8upbHRg11Xkh2ZKwLFCXjinUsFLR" + }, + "signatures": [ + "d3WvqtwFws9yftpxSrmwXqb48ZbBVjvxz34zY5Mo9TxaAPxsudPa68nDXZeShvK8UqtM84TgGfpdrgeX65q5WCW" + ] + } +} \ No newline at end of file diff --git a/zetaclient/testutils/constant.go b/zetaclient/testutils/constant.go index d010a9dde6..46b578ccf6 100644 --- a/zetaclient/testutils/constant.go +++ b/zetaclient/testutils/constant.go @@ -44,7 +44,7 @@ var GatewayAddresses = map[int64]string{ chains.SolanaMainnet.ChainId: "ZETAjseVjuFsxdRxo6MmTCvqFwb3ZHUx56Co3vCmGis", // stub, will be replaced with real address later - chains.SuiMainnet.ChainId: "0x5d4b302506645c37ff133b98fff50a5ae14841659738d6d733d59d0d217a9fff", + chains.SuiMainnet.ChainId: "0x5d4b302506645c37ff133b98fff50a5ae14841659738d6d733d59d0d217a9fff,0xffff302506645c37ff133b98fff50a5ae14841659738d6d733d59d0d217a9aaa", } // ConnectorAddresses contains constants ERC20 connector addresses for testing diff --git a/zetaclient/testutils/mocks/sui_client.go b/zetaclient/testutils/mocks/sui_client.go index 94341298fb..a1b0e3b35f 100644 --- a/zetaclient/testutils/mocks/sui_client.go +++ b/zetaclient/testutils/mocks/sui_client.go @@ -47,6 +47,34 @@ func (_m *SuiClient) GetLatestCheckpoint(ctx context.Context) (models.Checkpoint return r0, r1 } +// GetOwnedObjectID provides a mock function with given fields: ctx, ownerAddress, structType +func (_m *SuiClient) GetOwnedObjectID(ctx context.Context, ownerAddress string, structType string) (string, error) { + ret := _m.Called(ctx, ownerAddress, structType) + + if len(ret) == 0 { + panic("no return value specified for GetOwnedObjectID") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { + return rf(ctx, ownerAddress, structType) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, ownerAddress, structType) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, ownerAddress, structType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // HealthCheck provides a mock function with given fields: ctx func (_m *SuiClient) HealthCheck(ctx context.Context) (time.Time, error) { ret := _m.Called(ctx) @@ -75,6 +103,34 @@ func (_m *SuiClient) HealthCheck(ctx context.Context) (time.Time, error) { return r0, r1 } +// MoveCall provides a mock function with given fields: ctx, req +func (_m *SuiClient) MoveCall(ctx context.Context, req models.MoveCallRequest) (models.TxnMetaData, error) { + ret := _m.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for MoveCall") + } + + var r0 models.TxnMetaData + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.MoveCallRequest) (models.TxnMetaData, error)); ok { + return rf(ctx, req) + } + if rf, ok := ret.Get(0).(func(context.Context, models.MoveCallRequest) models.TxnMetaData); ok { + r0 = rf(ctx, req) + } else { + r0 = ret.Get(0).(models.TxnMetaData) + } + + if rf, ok := ret.Get(1).(func(context.Context, models.MoveCallRequest) error); ok { + r1 = rf(ctx, req) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // QueryModuleEvents provides a mock function with given fields: ctx, q func (_m *SuiClient) QueryModuleEvents(ctx context.Context, q client.EventQuery) ([]models.SuiEventResponse, string, error) { ret := _m.Called(ctx, q) @@ -112,6 +168,34 @@ func (_m *SuiClient) QueryModuleEvents(ctx context.Context, q client.EventQuery) return r0, r1, r2 } +// SuiExecuteTransactionBlock provides a mock function with given fields: ctx, req +func (_m *SuiClient) SuiExecuteTransactionBlock(ctx context.Context, req models.SuiExecuteTransactionBlockRequest) (models.SuiTransactionBlockResponse, error) { + ret := _m.Called(ctx, req) + + if len(ret) == 0 { + panic("no return value specified for SuiExecuteTransactionBlock") + } + + var r0 models.SuiTransactionBlockResponse + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, models.SuiExecuteTransactionBlockRequest) (models.SuiTransactionBlockResponse, error)); ok { + return rf(ctx, req) + } + if rf, ok := ret.Get(0).(func(context.Context, models.SuiExecuteTransactionBlockRequest) models.SuiTransactionBlockResponse); ok { + r0 = rf(ctx, req) + } else { + r0 = ret.Get(0).(models.SuiTransactionBlockResponse) + } + + if rf, ok := ret.Get(1).(func(context.Context, models.SuiExecuteTransactionBlockRequest) error); ok { + r1 = rf(ctx, req) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SuiGetObject provides a mock function with given fields: ctx, req func (_m *SuiClient) SuiGetObject(ctx context.Context, req models.SuiGetObjectRequest) (models.SuiObjectResponse, error) { ret := _m.Called(ctx, req) diff --git a/zetaclient/testutils/mocks/sui_gen.go b/zetaclient/testutils/mocks/sui_gen.go index 1210ce7ad8..a2cfd4fb54 100644 --- a/zetaclient/testutils/mocks/sui_gen.go +++ b/zetaclient/testutils/mocks/sui_gen.go @@ -18,6 +18,7 @@ type suiClient interface { HealthCheck(ctx context.Context) (time.Time, error) GetLatestCheckpoint(ctx context.Context) (models.CheckpointResponse, error) QueryModuleEvents(ctx context.Context, q client.EventQuery) ([]models.SuiEventResponse, string, error) + GetOwnedObjectID(ctx context.Context, ownerAddress, structType string) (string, error) SuiXGetReferenceGasPrice(ctx context.Context) (uint64, error) SuiXQueryEvents(ctx context.Context, req models.SuiXQueryEventsRequest) (models.PaginatedEventsResponse, error) @@ -26,4 +27,9 @@ type suiClient interface { ctx context.Context, req models.SuiGetTransactionBlockRequest, ) (models.SuiTransactionBlockResponse, error) + MoveCall(ctx context.Context, req models.MoveCallRequest) (models.TxnMetaData, error) + SuiExecuteTransactionBlock( + ctx context.Context, + req models.SuiExecuteTransactionBlockRequest, + ) (models.SuiTransactionBlockResponse, error) } diff --git a/zetaclient/testutils/testlog/log.go b/zetaclient/testutils/testlog/log.go index b3d9555a90..aaed545025 100644 --- a/zetaclient/testutils/testlog/log.go +++ b/zetaclient/testutils/testlog/log.go @@ -2,7 +2,8 @@ package testlog import ( "bytes" - "io" + "fmt" + "strings" "sync" "testing" @@ -11,40 +12,63 @@ import ( type Log struct { zerolog.Logger - buf *concurrentBytesBuffer -} - -type concurrentBytesBuffer struct { + t *testing.T buf *bytes.Buffer - mu sync.RWMutex + mu sync.Mutex } // New creates a new Log instance with a buffer and a test writer. func New(t *testing.T) *Log { - buf := &concurrentBytesBuffer{ + log := &Log{ + t: t, buf: &bytes.Buffer{}, - mu: sync.RWMutex{}, } - log := zerolog.New(io.MultiWriter(zerolog.NewTestWriter(t), buf)) + log.Logger = zerolog.New(log) - return &Log{Logger: log, buf: buf} + return log } func (log *Log) String() string { - return log.buf.string() + log.mu.Lock() + defer log.mu.Unlock() + return log.buf.String() } -func (b *concurrentBytesBuffer) Write(p []byte) (n int, err error) { - b.mu.Lock() - defer b.mu.Unlock() +func (log *Log) Write(p []byte) (n int, err error) { + log.mu.Lock() + defer log.mu.Unlock() + + // silence panics in case this log line is written AFTER test termination. + const silencePanicSubstring = "Log in goroutine" + defer func() { silencePanic(recover(), silencePanicSubstring) }() + + // write to the buffer first + n, err = log.buf.Write(p) + if err != nil { + return n, fmt.Errorf("failed to write to buffer: %w", err) + } + + // Strip trailing newline because t.Log always adds one. + // (copied from zerolog NewTestWriter) + p = bytes.TrimRight(p, "\n") + + // Then write to test output + log.t.Log(string(p)) - return b.buf.Write(p) + return len(p), nil } -func (b *concurrentBytesBuffer) string() string { - b.mu.RLock() - defer b.mu.RUnlock() +func silencePanic(r any, substr string) { + // noop + if r == nil { + return + } + + panicStr := fmt.Sprintf("%v", r) + if strings.Contains(panicStr, substr) { + return + } - return b.buf.String() + panic(panicStr) } diff --git a/zetaclient/testutils/testlog/log_test.go b/zetaclient/testutils/testlog/log_test.go new file mode 100644 index 0000000000..ed0b8d9148 --- /dev/null +++ b/zetaclient/testutils/testlog/log_test.go @@ -0,0 +1,27 @@ +package testlog + +import ( + "testing" + "time" +) + +func Test(t *testing.T) { + t.Run("PanicHandling", func(t *testing.T) { + // ARRANGE + tl := New(t) + + // ACT + // Log indefinitely, even after parent test is done + go func() { + for { + tl.Info().Msg("hello from goroutine") + time.Sleep(10 * time.Millisecond) + } + }() + + time.Sleep(500 * time.Millisecond) + }) + + // Let parent test run a bit longer than PanicHandling + time.Sleep(time.Second) +} diff --git a/zetaclient/tss/crypto.go b/zetaclient/tss/crypto.go index 731b4344e3..1a8e343ee2 100644 --- a/zetaclient/tss/crypto.go +++ b/zetaclient/tss/crypto.go @@ -20,6 +20,7 @@ import ( "gitlab.com/thorchain/tss/go-tss/keysign" "github.com/zeta-chain/node/pkg/chains" + "github.com/zeta-chain/node/pkg/contracts/sui" "github.com/zeta-chain/node/pkg/cosmos" ) @@ -87,6 +88,10 @@ func NewPubKeyFromECDSAHexString(raw string) (PubKey, error) { return NewPubKeyFromECDSA(*pk) } +func (k PubKey) AsECDSA() *ecdsa.PublicKey { + return k.ecdsaPubKey +} + // Bytes marshals pubKey to bytes either as compressed or uncompressed slice. // // In ECDSA, a compressed pubKey includes only the X and a parity bit for the Y, @@ -95,7 +100,7 @@ func NewPubKeyFromECDSAHexString(raw string) (PubKey, error) { func (k PubKey) Bytes(compress bool) []byte { pk := k.ecdsaPubKey if compress { - return elliptic.MarshalCompressed(pk.Curve, pk.X, pk.Y) + return crypto.CompressPubkey(pk) } return crypto.FromECDSAPub(pk) @@ -131,6 +136,11 @@ func (k PubKey) AddressEVM() eth.Address { return crypto.PubkeyToAddress(*k.ecdsaPubKey) } +// AddressSui returns Sui address of the public key. +func (k PubKey) AddressSui() string { + return sui.AddressFromPubKeyECDSA(k.ecdsaPubKey) +} + // VerifySignature checks that keysign.Signature is valid and origins from expected TSS public key. // Also returns signature as [65]byte (R, S, V) func VerifySignature(sig keysign.Signature, pk PubKey, hash []byte) ([65]byte, error) { diff --git a/zetaclient/types/event_test.go b/zetaclient/types/event_test.go index 87e95943ec..7eb81407ef 100644 --- a/zetaclient/types/event_test.go +++ b/zetaclient/types/event_test.go @@ -63,7 +63,7 @@ func Test_Catetory(t *testing.T) { cfg := config.Config{ ComplianceConfig: sample.ComplianceConfig(), } - config.LoadComplianceConfig(cfg) + config.SetRestrictedAddressesFromConfig(cfg) // test cases tests := []struct {