diff --git a/changelog.md b/changelog.md index 4cbbc55a9d..f255139f81 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ### Features +* [2578](https://github.com/zeta-chain/node/pull/2578) - add Gateway address in protocol contract list +* [2630](https://github.com/zeta-chain/node/pull/2630) - implement `MsgMigrateERC20CustodyFunds` to migrate the funds from the ERC20Custody to a new contracts (to be used for the new ERC20Custody contract for smart contract V2) * [2578](https://github.com/zeta-chain/node/pull/2578) - Add Gateway address in protocol contract list * [2634](https://github.com/zeta-chain/node/pull/2634) - add support for EIP-1559 gas fees * [2597](https://github.com/zeta-chain/node/pull/2597) - Add generic rpc metrics to zetaclient diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 86adfac7c4..521c53569e 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -302,6 +302,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) { e2etests.TestUpdateBytecodeConnectorName, e2etests.TestDepositEtherLiquidityCapName, e2etests.TestCriticalAdminTransactionsName, + e2etests.TestMigrateERC20CustodyFundsName, // TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported // See issue below for details diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index 243e13c3af..137c3d1205 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -57231,6 +57231,11 @@ definitions: is_removed: type: boolean title: if the tx was removed from the tracker due to no pending cctx + crosschainMsgMigrateERC20CustodyFundsResponse: + type: object + properties: + cctx_index: + type: string crosschainMsgMigrateTssFundsResponse: type: object crosschainMsgRefundAbortedCCTXResponse: diff --git a/docs/spec/crosschain/messages.md b/docs/spec/crosschain/messages.md index feb02afc1f..0f032b2d34 100644 --- a/docs/spec/crosschain/messages.md +++ b/docs/spec/crosschain/messages.md @@ -272,3 +272,17 @@ message MsgUpdateRateLimiterFlags { } ``` +## MsgMigrateERC20CustodyFunds + +MigrateERC20CustodyFunds migrates the funds from the current ERC20Custody contract to the new ERC20Custody contract + +```proto +message MsgMigrateERC20CustodyFunds { + string creator = 1; + int64 chain_id = 2; + string new_custody_address = 3; + string erc20_address = 4; + string amount = 5; +} +``` + diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index 402eed367e..1115d7d6c2 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -108,6 +108,7 @@ const ( TestUpdateBytecodeConnectorName = "update_bytecode_connector" TestRateLimiterName = "rate_limiter" TestCriticalAdminTransactionsName = "critical_admin_transactions" + TestMigrateERC20CustodyFundsName = "migrate_erc20_custody_funds" TestMigrateTSSName = "migrate_TSS" @@ -572,6 +573,12 @@ var AllE2ETests = []runner.E2ETest{ []runner.ArgDefinition{}, TestCriticalAdminTransactions, ), + runner.NewE2ETest( + TestMigrateERC20CustodyFundsName, + "migrate ERC20 custody funds", + []runner.ArgDefinition{}, + TestMigrateERC20CustodyFunds, + ), /* Special tests */ diff --git a/e2e/e2etests/test_migrate_erc20_custody_funds.go b/e2e/e2etests/test_migrate_erc20_custody_funds.go new file mode 100644 index 0000000000..1c38909b3d --- /dev/null +++ b/e2e/e2etests/test_migrate_erc20_custody_funds.go @@ -0,0 +1,61 @@ +package e2etests + +import ( + sdkmath "cosmossdk.io/math" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/txserver" + "github.com/zeta-chain/zetacore/e2e/utils" + "github.com/zeta-chain/zetacore/testutil/sample" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// TestMigrateERC20CustodyFunds tests the migration of ERC20 custody funds +func TestMigrateERC20CustodyFunds(r *runner.E2ERunner, _ []string) { + // get erc20 balance on ERC20 custody contract + balance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, r.ERC20CustodyAddr) + require.NoError(r, err) + + // get EVM chain ID + chainID, err := r.EVMClient.ChainID(r.Ctx) + require.NoError(r, err) + + newAddr := sample.EthAddress() + + // send MigrateERC20CustodyFunds command + // NOTE: we currently use a random address for the destination as a sufficient way to check migration + // TODO: makes the test more complete and perform a withdraw to new custody once the contract V2 architecture is integrated + // https://github.com/zeta-chain/node/issues/2474 + msg := crosschaintypes.NewMsgMigrateERC20CustodyFunds( + r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName), + chainID.Int64(), + newAddr.Hex(), + r.ERC20Addr.Hex(), + sdkmath.NewUintFromBigInt(balance), + ) + res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg) + require.NoError(r, err) + + // fetch cctx index from tx response + cctxIndex, err := txserver.FetchAttributeFromTxResponse(res, "cctx_index") + require.NoError(r, err) + + cctxRes, err := r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: cctxIndex}) + require.NoError(r, err) + + cctx := cctxRes.CrossChainTx + r.Logger.CCTX(*cctx, "migration") + + // wait for the cctx to be mined + r.WaitForMinedCCTXFromIndex(cctxIndex) + + // check ERC20 balance on new address + newAddrBalance, err := r.ERC20.BalanceOf(&bind.CallOpts{}, newAddr) + require.NoError(r, err) + require.Equal(r, balance, newAddrBalance) + + // artificially set the ERC20 Custody address to the new address to prevent accounting check from failing + r.ERC20CustodyAddr = newAddr +} diff --git a/pkg/constant/constant.go b/pkg/constant/constant.go index b296be7654..57a922e5c0 100644 --- a/pkg/constant/constant.go +++ b/pkg/constant/constant.go @@ -14,6 +14,9 @@ const ( // CmdWhitelistERC20 is used for CCTX of type cmd to give the instruction to the TSS to whitelist an ERC20 on an exeternal chain CmdWhitelistERC20 = "cmd_whitelist_erc20" + // CmdMigrateERC20CustodyFunds is used for CCTX of type cmd to give the instruction to the TSS to transfer its funds on a new address + CmdMigrateERC20CustodyFunds = "cmd_migrate_erc20_custody_funds" + // CmdMigrateTssFunds is used for CCTX of type cmd to give the instruction to the TSS to transfer its funds on a new address CmdMigrateTssFunds = "cmd_migrate_tss_funds" diff --git a/proto/zetachain/zetacore/crosschain/events.proto b/proto/zetachain/zetacore/crosschain/events.proto index 3778b3bdce..5ec523d9e4 100644 --- a/proto/zetachain/zetacore/crosschain/events.proto +++ b/proto/zetachain/zetacore/crosschain/events.proto @@ -68,3 +68,10 @@ message EventERC20Whitelist { string whitelist_cctx_index = 1; string zrc20_address = 2; } + +message EventERC20CustodyFundsMigration { + string new_custody_address = 1; + string erc20_address = 2; + string amount = 3; + string cctx_index = 4; +} diff --git a/proto/zetachain/zetacore/crosschain/tx.proto b/proto/zetachain/zetacore/crosschain/tx.proto index 2bff84f733..0cd0c8e1f5 100644 --- a/proto/zetachain/zetacore/crosschain/tx.proto +++ b/proto/zetachain/zetacore/crosschain/tx.proto @@ -33,6 +33,9 @@ service Msg { rpc UpdateRateLimiterFlags(MsgUpdateRateLimiterFlags) returns (MsgUpdateRateLimiterFlagsResponse); + + rpc MigrateERC20CustodyFunds(MsgMigrateERC20CustodyFunds) + returns (MsgMigrateERC20CustodyFundsResponse); } message MsgMigrateTssFunds { @@ -188,3 +191,16 @@ message MsgUpdateRateLimiterFlags { } message MsgUpdateRateLimiterFlagsResponse {} + +message MsgMigrateERC20CustodyFunds { + string creator = 1; + int64 chain_id = 2; + string new_custody_address = 3; + string erc20_address = 4; + string amount = 5 [ + (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Uint", + (gogoproto.nullable) = false + ]; +} + +message MsgMigrateERC20CustodyFundsResponse { string cctx_index = 1; } diff --git a/testutil/sample/crosschain.go b/testutil/sample/crosschain.go index 4fd1e70018..2894431724 100644 --- a/testutil/sample/crosschain.go +++ b/testutil/sample/crosschain.go @@ -131,6 +131,19 @@ func GasPrice(t *testing.T, index string) *types.GasPrice { } } +func GasPriceWithChainID(t *testing.T, chainID int64) types.GasPrice { + r := newRandFromStringSeed(t, fmt.Sprintf("%d", chainID)) + + return types.GasPrice{ + Creator: AccAddress(), + ChainId: chainID, + Signers: []string{AccAddress(), AccAddress()}, + BlockNums: []uint64{r.Uint64(), r.Uint64()}, + Prices: []uint64{r.Uint64(), r.Uint64()}, + MedianIndex: 0, + } +} + func InboundParams(r *rand.Rand) *types.InboundParams { return &types.InboundParams{ Sender: EthAddress().String(), diff --git a/typescript/zetachain/zetacore/crosschain/events_pb.d.ts b/typescript/zetachain/zetacore/crosschain/events_pb.d.ts index 2a71867f7b..c46555a9e3 100644 --- a/typescript/zetachain/zetacore/crosschain/events_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/events_pb.d.ts @@ -354,3 +354,42 @@ export declare class EventERC20Whitelist extends Message { static equals(a: EventERC20Whitelist | PlainMessage | undefined, b: EventERC20Whitelist | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.EventERC20CustodyFundsMigration + */ +export declare class EventERC20CustodyFundsMigration extends Message { + /** + * @generated from field: string new_custody_address = 1; + */ + newCustodyAddress: string; + + /** + * @generated from field: string erc20_address = 2; + */ + erc20Address: string; + + /** + * @generated from field: string amount = 3; + */ + amount: string; + + /** + * @generated from field: string cctx_index = 4; + */ + cctxIndex: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.EventERC20CustodyFundsMigration"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): EventERC20CustodyFundsMigration; + + static fromJson(jsonValue: JsonValue, options?: Partial): EventERC20CustodyFundsMigration; + + static fromJsonString(jsonString: string, options?: Partial): EventERC20CustodyFundsMigration; + + static equals(a: EventERC20CustodyFundsMigration | PlainMessage | undefined, b: EventERC20CustodyFundsMigration | PlainMessage | undefined): boolean; +} + diff --git a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts index e15cf71780..52d4b8aaa9 100644 --- a/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts +++ b/typescript/zetachain/zetacore/crosschain/tx_pb.d.ts @@ -826,3 +826,71 @@ export declare class MsgUpdateRateLimiterFlagsResponse extends Message | undefined, b: MsgUpdateRateLimiterFlagsResponse | PlainMessage | undefined): boolean; } +/** + * @generated from message zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds + */ +export declare class MsgMigrateERC20CustodyFunds extends Message { + /** + * @generated from field: string creator = 1; + */ + creator: string; + + /** + * @generated from field: int64 chain_id = 2; + */ + chainId: bigint; + + /** + * @generated from field: string new_custody_address = 3; + */ + newCustodyAddress: string; + + /** + * @generated from field: string erc20_address = 4; + */ + erc20Address: string; + + /** + * @generated from field: string amount = 5; + */ + amount: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgMigrateERC20CustodyFunds; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgMigrateERC20CustodyFunds; + + static fromJsonString(jsonString: string, options?: Partial): MsgMigrateERC20CustodyFunds; + + static equals(a: MsgMigrateERC20CustodyFunds | PlainMessage | undefined, b: MsgMigrateERC20CustodyFunds | PlainMessage | undefined): boolean; +} + +/** + * @generated from message zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFundsResponse + */ +export declare class MsgMigrateERC20CustodyFundsResponse extends Message { + /** + * @generated from field: string cctx_index = 1; + */ + cctxIndex: string; + + constructor(data?: PartialMessage); + + static readonly runtime: typeof proto3; + static readonly typeName = "zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFundsResponse"; + static readonly fields: FieldList; + + static fromBinary(bytes: Uint8Array, options?: Partial): MsgMigrateERC20CustodyFundsResponse; + + static fromJson(jsonValue: JsonValue, options?: Partial): MsgMigrateERC20CustodyFundsResponse; + + static fromJsonString(jsonString: string, options?: Partial): MsgMigrateERC20CustodyFundsResponse; + + static equals(a: MsgMigrateERC20CustodyFundsResponse | PlainMessage | undefined, b: MsgMigrateERC20CustodyFundsResponse | PlainMessage | undefined): boolean; +} + diff --git a/x/authority/types/authorization_list.go b/x/authority/types/authorization_list.go index 58f3d80063..6d55a6770e 100644 --- a/x/authority/types/authorization_list.go +++ b/x/authority/types/authorization_list.go @@ -23,6 +23,7 @@ var ( } // AdminPolicyMessages keeps track of the message URLs that can, by default, only be executed by admin policy address AdminPolicyMessages = []string{ + "/zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds", "/zetachain.zetacore.crosschain.MsgMigrateTssFunds", "/zetachain.zetacore.crosschain.MsgUpdateTssAddress", "/zetachain.zetacore.crosschain.MsgWhitelistERC20", diff --git a/x/authority/types/authorization_list_test.go b/x/authority/types/authorization_list_test.go index 2322caa798..2f45b3b2c4 100644 --- a/x/authority/types/authorization_list_test.go +++ b/x/authority/types/authorization_list_test.go @@ -414,6 +414,7 @@ func TestDefaultAuthorizationsList(t *testing.T) { // AdminPolicyMessageList is a list of messages that can be authorized by the admin policy var AdminPolicyMessageList = []string{ + sdk.MsgTypeURL(&crosschaintypes.MsgMigrateERC20CustodyFunds{}), sdk.MsgTypeURL(&crosschaintypes.MsgMigrateTssFunds{}), sdk.MsgTypeURL(&crosschaintypes.MsgUpdateTssAddress{}), sdk.MsgTypeURL(&crosschaintypes.MsgWhitelistERC20{}), diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index 0d6f760fda..6be78bdfaa 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -12,7 +12,7 @@ import ( "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" - zetaObserverTypes "github.com/zeta-chain/zetacore/x/observer/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) // SetObserverOutboundInfo sets the CCTX outbound nonce to the next available nonce for the TSS address, and updates the nonce of blockchain state. @@ -20,7 +20,7 @@ import ( func (k Keeper) SetObserverOutboundInfo(ctx sdk.Context, receiveChainID int64, cctx *types.CrossChainTx) error { chain, found := k.GetObserverKeeper().GetSupportedChainFromChainID(ctx, receiveChainID) if !found { - return zetaObserverTypes.ErrSupportedChains + return observertypes.ErrSupportedChains } nonce, found := k.GetObserverKeeper().GetChainNonces(ctx, receiveChainID) diff --git a/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds.go b/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds.go new file mode 100644 index 0000000000..56bc0a5ea1 --- /dev/null +++ b/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds.go @@ -0,0 +1,103 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// MigrateERC20CustodyFunds migrates the funds from the current ERC20Custody contract to the new ERC20Custody contract +func (k msgServer) MigrateERC20CustodyFunds( + goCtx context.Context, + msg *types.MsgMigrateERC20CustodyFunds, +) (*types.MsgMigrateERC20CustodyFundsResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + // check if authorized + err := k.GetAuthorityKeeper().CheckAuthorization(ctx, msg) + if err != nil { + return nil, errorsmod.Wrap(authoritytypes.ErrUnauthorized, err.Error()) + } + + // get the current TSS nonce allow to set a unique index for the CCTX + chainNonce, found := k.GetObserverKeeper().GetChainNonces(ctx, msg.ChainId) + if !found { + return nil, errorsmod.Wrap(types.ErrInvalidChainID, "cannot find current chain nonce") + } + currentNonce := chainNonce.Nonce + + // get the current TSS + tss, found := k.GetObserverKeeper().GetTSS(ctx) + if !found { + return nil, errorsmod.Wrap(types.ErrCannotFindTSSKeys, "cannot find current TSS") + } + + // get necessary parameters to create the cctx + params, found := k.zetaObserverKeeper.GetChainParamsByChainID(ctx, msg.ChainId) + if !found { + return nil, errorsmod.Wrapf(types.ErrInvalidChainID, "chain params not found for chain id (%d)", msg.ChainId) + } + medianGasPrice, priorityFee, isFound := k.GetMedianGasValues(ctx, msg.ChainId) + if !isFound { + return nil, errorsmod.Wrapf( + types.ErrUnableToGetGasPrice, + "median gas price not found for chain id (%d)", + msg.ChainId, + ) + } + + // overpays gas price by 2x + medianGasPrice = medianGasPrice.MulUint64(types.ERC20CustodyMigrationGasMultiplierEVM) + priorityFee = priorityFee.MulUint64(types.ERC20CustodyMigrationGasMultiplierEVM) + + // should not happen + if priorityFee.GT(medianGasPrice) { + return nil, errorsmod.Wrapf( + types.ErrInvalidGasAmount, + "priorityFee %s is greater than median gasPrice %s", + priorityFee.String(), + medianGasPrice.String(), + ) + } + + // create the CCTX that allows to sign the fund migration + cctx := types.MigrateERC20CustodyFundsCmdCCTX( + msg.Creator, + msg.Erc20Address, + params.Erc20CustodyContractAddress, + msg.NewCustodyAddress, + msg.ChainId, + msg.Amount, + medianGasPrice.String(), + priorityFee.String(), + tss.TssPubkey, + currentNonce, + ) + + // save the cctx + err = k.SetObserverOutboundInfo(ctx, msg.ChainId, &cctx) + if err != nil { + return nil, err + } + k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctx) + + err = ctx.EventManager().EmitTypedEvent( + &types.EventERC20CustodyFundsMigration{ + NewCustodyAddress: msg.NewCustodyAddress, + Erc20Address: msg.Erc20Address, + Amount: msg.Amount.String(), + CctxIndex: cctx.Index, + }, + ) + if err != nil { + return nil, errorsmod.Wrapf(err, "failed to emit event") + } + + return &types.MsgMigrateERC20CustodyFundsResponse{ + CctxIndex: cctx.Index, + }, nil +} diff --git a/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds_test.go b/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds_test.go new file mode 100644 index 0000000000..c68df89467 --- /dev/null +++ b/x/crosschain/keeper/msg_server_migrate_erc20_custody_funds_test.go @@ -0,0 +1,336 @@ +package keeper_test + +import ( + "errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/coin" + testkeeper "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" + "github.com/zeta-chain/zetacore/x/crosschain/keeper" + "github.com/zeta-chain/zetacore/x/crosschain/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" + "testing" +) + +func TestKeeper_MigrateERC20CustodyFunds(t *testing.T) { + t.Run("can create CCTX to migrate ERC20 custody funds", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{sample.ChainParamsSupported(chainID)}, + }) + k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) + medianGasPrice, priorityFee, isFound := k.GetMedianGasValues(ctx, msg.ChainId) + require.True(t, isFound) + + // ACT + res, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.NoError(t, err) + + // check CCTX is created + cctx, found := k.GetCrossChainTx(ctx, res.CctxIndex) + require.True(t, found) + require.Equal(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType) + require.Len(t, cctx.OutboundParams, 1) + require.EqualValues( + t, + medianGasPrice.MulUint64(types.ERC20CustodyMigrationGasMultiplierEVM).String(), + cctx.OutboundParams[0].GasPrice, + ) + require.EqualValues( + t, + priorityFee.MulUint64(types.ERC20CustodyMigrationGasMultiplierEVM).String(), + cctx.OutboundParams[0].GasPriorityFee, + ) + }) + + t.Run("should fail if not authorized", func(t *testing.T) { + // ARRANGE + k, ctx, _, _ := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + msgServer := keeper.NewMsgServerImpl(*k) + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: getValidEthChain().ChainId, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, errors.New("not authorized")) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, authoritytypes.ErrUnauthorized) + }) + + t.Run("should fail if can't find chain nonces", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + //zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) // not set + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{sample.ChainParamsSupported(chainID)}, + }) + k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, types.ErrInvalidChainID) + }) + + t.Run("should fail if can't find current TSS", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + //zk.ObserverKeeper.SetTSS(ctx, tss) // not set + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{sample.ChainParamsSupported(chainID)}, + }) + k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, types.ErrCannotFindTSSKeys) + }) + + t.Run("should fail if can't find chain params", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{}) // not set + k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, types.ErrInvalidChainID) + }) + + t.Run("should fail if can't find gas price", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{sample.ChainParamsSupported(chainID)}, + }) + //k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) // not set + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, types.ErrUnableToGetGasPrice) + }) + + t.Run("should fail if priority fees higher than gas price", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{sample.ChainParamsSupported(chainID)}, + }) + k.SetGasPrice(ctx, types.GasPrice{ + Creator: sample.AccAddress(), + ChainId: chainID, + Signers: []string{sample.AccAddress()}, + BlockNums: []uint64{42}, + Prices: []uint64{42}, + PriorityFees: []uint64{43}, + MedianIndex: 0, + }) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, types.ErrInvalidGasAmount) + }) + + t.Run("should fail if can't set outbound info", func(t *testing.T) { + // ARRANGE + k, ctx, _, zk := testkeeper.CrosschainKeeperWithMocks(t, testkeeper.CrosschainMockOptions{ + UseAuthorityMock: true, + }) + + chainID := getValidEthChain().ChainId + msgServer := keeper.NewMsgServerImpl(*k) + tss := sample.Tss() + + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chainID, + NewCustodyAddress: sample.EthAddress().Hex(), + Erc20Address: sample.EthAddress().Hex(), + Amount: sample.UintInRange(42, 100), + } + + // mock authority calls + authorityMock := testkeeper.GetCrosschainAuthorityMock(t, k) + testkeeper.MockCheckAuthorization(&authorityMock.Mock, &msg, nil) + + // set necessary values in observer + zk.ObserverKeeper.SetChainNonces(ctx, observertypes.ChainNonces{ChainId: chainID}) + zk.ObserverKeeper.SetPendingNonces(ctx, observertypes.PendingNonces{ChainId: chainID, Tss: tss.TssPubkey}) + zk.ObserverKeeper.SetTSS(ctx, tss) + zk.ObserverKeeper.SetChainParamsList(ctx, observertypes.ChainParamsList{ + ChainParams: []*observertypes.ChainParams{ + sample.ChainParams(chainID), + }, // set non supported chain params to fail + }) + k.SetGasPrice(ctx, sample.GasPriceWithChainID(t, chainID)) + + // ACT + _, err := msgServer.MigrateERC20CustodyFunds(sdk.WrapSDKContext(ctx), &msg) + + // ASSERT + require.ErrorIs(t, err, observertypes.ErrSupportedChains) + }) +} diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds.go b/x/crosschain/keeper/msg_server_migrate_tss_funds.go index ed8c51b93d..7d48b48cdb 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds.go @@ -2,7 +2,6 @@ package keeper import ( "context" - "fmt" "sort" errorsmod "cosmossdk.io/errors" @@ -11,13 +10,7 @@ import ( tmtypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/ethereum/go-ethereum/crypto" - "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" - zetacrypto "github.com/zeta-chain/zetacore/pkg/crypto" - "github.com/zeta-chain/zetacore/pkg/gas" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" @@ -72,7 +65,7 @@ func (k msgServer) MigrateTssFunds( return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, "cannot migrate funds when there are pending nonces") } - err = k.MigrateTSSFundsForChain(ctx, msg.ChainId, msg.Amount, tss, tssHistory) + err = k.initiateMigrateTSSFundsCCTX(ctx, msg.Creator, msg.ChainId, msg.Amount, tss, tssHistory) if err != nil { return nil, errorsmod.Wrap(types.ErrCannotMigrateTssFunds, err.Error()) } @@ -80,8 +73,10 @@ func (k msgServer) MigrateTssFunds( return &types.MsgMigrateTssFundsResponse{}, nil } -func (k Keeper) MigrateTSSFundsForChain( +// initiateMigrateTSSFundsCCTX sets the CCTX for migrating the funds to initiate the migration outbound +func (k Keeper) initiateMigrateTSSFundsCCTX( ctx sdk.Context, + creator string, chainID int64, amount sdkmath.Uint, currentTss observertypes.TSS, @@ -93,126 +88,24 @@ func (k Keeper) MigrateTSSFundsForChain( if !isFound { return types.ErrUnableToGetGasPrice } - indexString := GetIndexStringForTssMigration( - currentTss.TssPubkey, - newTss.TssPubkey, + + // initialize the cmd CCTX + cctx, err := types.MigrateFundCmdCCTX( + ctx.BlockHeight(), + creator, + tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()).String(), chainID, amount, - ctx.BlockHeight(), + medianGasPrice, + priorityFee, + currentTss.TssPubkey, + newTss.TssPubkey, + k.GetAuthorityKeeper().GetAdditionalChainList(ctx), ) - - hash := crypto.Keccak256Hash([]byte(indexString)) - index := hash.Hex() - - // TODO : Use the `NewCCTX` method to create the cctx - // https://github.com/zeta-chain/node/issues/1909 - cctx := types.CrossChainTx{ - Creator: "", - Index: index, - ZetaFees: sdkmath.Uint{}, - RelayedMessage: fmt.Sprintf("%s:%s", constant.CmdMigrateTssFunds, "Funds Migrator Admin Cmd"), - CctxStatus: &types.Status{ - Status: types.CctxStatus_PendingOutbound, - StatusMessage: "", - LastUpdateTimestamp: 0, - }, - InboundParams: &types.InboundParams{ - Sender: "", - SenderChainId: chainID, - TxOrigin: "", - CoinType: coin.CoinType_Cmd, - Asset: "", - Amount: amount, - ObservedHash: tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()).String(), - ObservedExternalHeight: 0, - BallotIndex: "", - FinalizedZetaHeight: 0, - }, - OutboundParams: []*types.OutboundParams{{ - Receiver: "", - ReceiverChainId: chainID, - CoinType: coin.CoinType_Cmd, - Amount: amount, - TssNonce: 0, - GasLimit: 1_000_000, - GasPrice: medianGasPrice.MulUint64(2).String(), - GasPriorityFee: priorityFee.MulUint64(2).String(), - Hash: "", - BallotIndex: "", - ObservedExternalHeight: 0, - GasUsed: 0, - EffectiveGasPrice: sdkmath.Int{}, - EffectiveGasLimit: 0, - TssPubkey: currentTss.TssPubkey, - }}, - } - - // retrieve from authority keeper additional chains - additionalChains := k.GetAuthorityKeeper().GetAdditionalChainList(ctx) - - // Set the sender and receiver addresses for EVM chain - if chains.IsEVMChain(chainID, additionalChains) { - ethAddressOld, err := zetacrypto.GetTssAddrEVM(currentTss.TssPubkey) - if err != nil { - return err - } - ethAddressNew, err := zetacrypto.GetTssAddrEVM(newTss.TssPubkey) - if err != nil { - return err - } - cctx.InboundParams.Sender = ethAddressOld.String() - cctx.GetCurrentOutboundParam().Receiver = ethAddressNew.String() - // Tss migration is a send transaction, so the gas limit is set to 21000 - cctx.GetCurrentOutboundParam().GasLimit = gas.EVMSend - // Multiple current gas price with standard multiplier to add some buffer - multipliedGasPrice, err := gas.MultiplyGasPrice(medianGasPrice, types.TssMigrationGasMultiplierEVM) - if err != nil { - return err - } - evmFee := sdkmath.NewUint(cctx.GetCurrentOutboundParam().GasLimit).Mul(multipliedGasPrice) - if evmFee.GT(amount) { - return errorsmod.Wrap( - types.ErrInsufficientFundsTssMigration, - fmt.Sprintf( - "insufficient funds to pay for gas fee, amount: %s, gas fee: %s, chainid: %d", - amount.String(), - evmFee.String(), - chainID, - ), - ) - } - - cctx.GetCurrentOutboundParam().GasPrice = multipliedGasPrice.String() - cctx.GetCurrentOutboundParam().Amount = amount.Sub( - evmFee.Add(sdkmath.NewUintFromString(types.TSSMigrationBufferAmountEVM)), - ) - } - // Set the sender and receiver addresses for Bitcoin chain - if chains.IsBitcoinChain(chainID, additionalChains) { - bitcoinNetParams, err := chains.BitcoinNetParamsFromChainID(chainID) - if err != nil { - return err - } - btcAddressOld, err := zetacrypto.GetTssAddrBTC(currentTss.TssPubkey, bitcoinNetParams) - if err != nil { - return err - } - btcAddressNew, err := zetacrypto.GetTssAddrBTC(newTss.TssPubkey, bitcoinNetParams) - if err != nil { - return err - } - cctx.InboundParams.Sender = btcAddressOld - cctx.GetCurrentOutboundParam().Receiver = btcAddressNew - } - - if cctx.GetCurrentOutboundParam().Receiver == "" { - return errorsmod.Wrap(types.ErrReceiverIsEmpty, fmt.Sprintf("chain %d is not supported", chainID)) - } - - err := k.SetObserverOutboundInfo(ctx, chainID, &cctx) if err != nil { return err } + // The migrate funds can be run again to update the migration cctx index if the migration fails // This should be used after carefully calculating the amount again existingMigrationInfo, found := k.zetaObserverKeeper.GetFundMigrator(ctx, chainID) @@ -238,18 +131,9 @@ func (k Keeper) MigrateTSSFundsForChain( k.SetCctxAndNonceToCctxAndInboundHashToCctx(ctx, cctx) k.zetaObserverKeeper.SetFundMigrator(ctx, observertypes.TssFundMigratorInfo{ ChainId: chainID, - MigrationCctxIndex: index, + MigrationCctxIndex: cctx.Index, }) EmitEventInboundFinalized(ctx, &cctx) return nil } - -func GetIndexStringForTssMigration( - currentTssPubkey, newTssPubkey string, - chainID int64, - amount sdkmath.Uint, - height int64, -) string { - return fmt.Sprintf("%s-%s-%d-%s-%d", currentTssPubkey, newTssPubkey, chainID, amount.String(), height) -} diff --git a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go index 1896a35f59..a715fcf4e6 100644 --- a/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go +++ b/x/crosschain/keeper/msg_server_migrate_tss_funds_test.go @@ -74,7 +74,7 @@ func setupTssMigrationParams( ChainId: chain.ChainId, Nonce: 1, }) - indexString := keeper.GetIndexStringForTssMigration( + indexString := crosschaintypes.GetTssMigrationCCTXIndexString( currentTss.TssPubkey, newTss.TssPubkey, chain.ChainId, @@ -110,10 +110,12 @@ func TestKeeper_MigrateTSSFundsForChain(t *testing.T) { keepertest.MockGetChainListEmpty(&authorityMock.Mock) _, err := msgServer.MigrateTssFunds(ctx, &msg) require.NoError(t, err) + hash := crypto.Keccak256Hash([]byte(indexString)) index := hash.Hex() cctx, found := k.GetCrossChainTx(ctx, index) require.True(t, found) + multipliedValue, err := gas.MultiplyGasPrice(gp, crosschaintypes.TssMigrationGasMultiplierEVM) require.NoError(t, err) require.Equal(t, multipliedValue.String(), cctx.GetCurrentOutboundParam().GasPrice) diff --git a/x/crosschain/keeper/msg_server_whitelist_erc20.go b/x/crosschain/keeper/msg_server_whitelist_erc20.go index 8b6ad08b26..45cf836061 100644 --- a/x/crosschain/keeper/msg_server_whitelist_erc20.go +++ b/x/crosschain/keeper/msg_server_whitelist_erc20.go @@ -2,18 +2,14 @@ package keeper import ( "context" - "fmt" "math/big" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ethcommon "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" authoritytypes "github.com/zeta-chain/zetacore/x/authority/types" "github.com/zeta-chain/zetacore/x/crosschain/types" fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" @@ -101,7 +97,7 @@ func (k msgServer) WhitelistERC20( } // get necessary parameters to create the cctx - param, found := k.zetaObserverKeeper.GetChainParamsByChainID(ctx, msg.ChainId) + params, found := k.zetaObserverKeeper.GetChainParamsByChainID(ctx, msg.ChainId) if !found { return nil, errorsmod.Wrapf(types.ErrInvalidChainID, "chain params not found for chain id (%d)", msg.ChainId) } @@ -113,11 +109,10 @@ func (k msgServer) WhitelistERC20( msg.ChainId, ) } - // overpays gas price by 2x - const multiplier = 2 - medianGasPrice = medianGasPrice.MulUint64(multiplier) - priorityFee = priorityFee.MulUint64(multiplier) + // overpays gas price by 2x + medianGasPrice = medianGasPrice.MulUint64(types.ERC20CustodyWhitelistGasMultiplierEVM) + priorityFee = priorityFee.MulUint64(types.ERC20CustodyWhitelistGasMultiplierEVM) // should not happen if priorityFee.GT(medianGasPrice) { @@ -129,54 +124,18 @@ func (k msgServer) WhitelistERC20( ) } - // calculate the cctx index - // we use the deployed zrc20 contract address to generate a unique index - // since other parts of the system may use the zrc20 for the index, we add a message specific suffix - hash := crypto.Keccak256Hash(zrc20Addr.Bytes(), []byte("WhitelistERC20")) - index := hash.Hex() - - // create a cmd cctx to whitelist the erc20 on the external chain - // TODO : refactor this to use the `NewCCTX` function instead. - //https://github.com/zeta-chain/node/issues/1909 - cctx := types.CrossChainTx{ - Creator: msg.Creator, - Index: index, - ZetaFees: sdk.NewUint(0), - RelayedMessage: fmt.Sprintf("%s:%s", constant.CmdWhitelistERC20, msg.Erc20Address), - CctxStatus: &types.Status{ - Status: types.CctxStatus_PendingOutbound, - StatusMessage: "", - LastUpdateTimestamp: 0, - }, - InboundParams: &types.InboundParams{ - Sender: msg.Creator, - SenderChainId: 0, - TxOrigin: "", - CoinType: coin.CoinType_Cmd, - Asset: "", - Amount: math.Uint{}, - ObservedHash: hash.String(), // all Upper case Cosmos TX HEX, with no 0x prefix - ObservedExternalHeight: 0, - BallotIndex: "", - FinalizedZetaHeight: 0, - }, - OutboundParams: []*types.OutboundParams{ - { - Receiver: param.Erc20CustodyContractAddress, - ReceiverChainId: msg.ChainId, - CoinType: coin.CoinType_Cmd, - Amount: math.NewUint(0), - TssNonce: 0, - GasLimit: 100_000, - GasPrice: medianGasPrice.String(), - GasPriorityFee: priorityFee.String(), - Hash: "", - BallotIndex: "", - ObservedExternalHeight: 0, - TssPubkey: tss.TssPubkey, - }, - }, - } + // create the cctx + cctx := types.WhitelistERC20CmdCCTX( + msg.Creator, + zrc20Addr, + msg.Erc20Address, + params.Erc20CustodyContractAddress, + msg.ChainId, + medianGasPrice.String(), + priorityFee.String(), + tss.TssPubkey, + ) + err = k.SetObserverOutboundInfo(ctx, msg.ChainId, &cctx) if err != nil { return nil, err @@ -202,7 +161,7 @@ func (k msgServer) WhitelistERC20( err = ctx.EventManager().EmitTypedEvent( &types.EventERC20Whitelist{ Zrc20Address: zrc20Addr.Hex(), - WhitelistCctxIndex: index, + WhitelistCctxIndex: cctx.Index, }, ) if err != nil { @@ -211,6 +170,6 @@ func (k msgServer) WhitelistERC20( return &types.MsgWhitelistERC20Response{ Zrc20Address: zrc20Addr.Hex(), - CctxIndex: index, + CctxIndex: cctx.Index, }, nil } diff --git a/x/crosschain/types/cmd_cctxs.go b/x/crosschain/types/cmd_cctxs.go new file mode 100644 index 0000000000..51d4eaa9de --- /dev/null +++ b/x/crosschain/types/cmd_cctxs.go @@ -0,0 +1,260 @@ +package types + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + ethcommon "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + zetacrypto "github.com/zeta-chain/zetacore/pkg/crypto" + "github.com/zeta-chain/zetacore/pkg/gas" +) + +const ( + // TssMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the tss migration . + // This is done to avoid the tss migration tx getting stuck in the mempool + TssMigrationGasMultiplierEVM = "2.5" + + // TSSMigrationBufferAmountEVM is the buffer amount added to the gas price for the tss migration transaction + TSSMigrationBufferAmountEVM = "2100000000" + + // ERC20CustodyMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the erc20 custody migration + // NOTE: this is a integer type unlike type above because the message logic is slightly different and an integer is needed + ERC20CustodyMigrationGasMultiplierEVM = 2 + + // ERC20CustodyWhitelistGasMultiplierEVM is multiplied to the median gas price to get the gas price for the erc20 custody whitelist + ERC20CustodyWhitelistGasMultiplierEVM = 2 +) + +// MigrateERC20CustodyFundsCmdCCTX returns a CCTX allowing to migrate ERC20 custody funds +func MigrateERC20CustodyFundsCmdCCTX( + creator string, + erc20Address string, + custodyContractAddress string, + newCustodyContractAddress string, + chainID int64, + amount sdkmath.Uint, + gasPrice string, + priorityFee string, + tssPubKey string, + currentNonce uint64, +) CrossChainTx { + indexString := GetERC20CustodyMigrationCCTXIndexString(tssPubKey, currentNonce, erc20Address) + hash := crypto.Keccak256Hash([]byte(indexString)) + + return newCmdCCTX( + creator, + hash.Hex(), + fmt.Sprintf( + "%s:%s,%s,%s", + constant.CmdMigrateERC20CustodyFunds, + newCustodyContractAddress, + erc20Address, + amount.String(), + ), + creator, + hash.Hex(), + custodyContractAddress, + chainID, + sdkmath.NewUint(0), + 100_000, + gasPrice, + priorityFee, + tssPubKey, + ) +} + +// GetERC20CustodyMigrationCCTXIndexString returns the index string of the CCTX for migrating ERC20 custody funds +func GetERC20CustodyMigrationCCTXIndexString( + tssPubKey string, + nonce uint64, + erc20Address string, +) string { + return fmt.Sprintf("%s-%d-%s", tssPubKey, nonce, erc20Address) +} + +// WhitelistERC20CmdCCTX returns a CCTX allowing to whitelist an ERC20 token on an external chain +func WhitelistERC20CmdCCTX( + creator string, + zrc20Address ethcommon.Address, + erc20Address string, + custodyContractAddress string, + chainID int64, + gasPrice string, + priorityFee string, + tssPubKey string, +) CrossChainTx { + // calculate the cctx index + // we use the deployed zrc20 contract address to generate a unique index + // since other parts of the system may use the zrc20 for the index, we add a message specific suffix + hash := crypto.Keccak256Hash(zrc20Address.Bytes(), []byte("WhitelistERC20")) + + return newCmdCCTX( + creator, + hash.Hex(), + fmt.Sprintf("%s:%s", constant.CmdWhitelistERC20, erc20Address), + creator, + hash.Hex(), + custodyContractAddress, + chainID, + sdkmath.NewUint(0), + 100_000, + gasPrice, + priorityFee, + tssPubKey, + ) +} + +// MigrateFundCmdCCTX returns a CCTX allowing to migrate funds from the current TSS to the new TSS +func MigrateFundCmdCCTX( + blockHeight int64, + creator string, + inboundHash string, + chainID int64, + amount sdkmath.Uint, + medianGasPrice sdkmath.Uint, + priorityFee sdkmath.Uint, + currentTSSPubKey string, + newTSSPubKey string, + additionalStaticChainInfo []chains.Chain, +) (CrossChainTx, error) { + var ( + sender string + receiver string + gasLimit uint64 + gasPrice string + finalAmount sdkmath.Uint + ) + + // set sender, receiver, gas limit, gas price and final amount based on the chain + switch { + case chains.IsEVMChain(chainID, additionalStaticChainInfo): + ethAddressOld, err := zetacrypto.GetTssAddrEVM(currentTSSPubKey) + if err != nil { + return CrossChainTx{}, err + } + ethAddressNew, err := zetacrypto.GetTssAddrEVM(newTSSPubKey) + if err != nil { + return CrossChainTx{}, err + } + sender = ethAddressOld.String() + receiver = ethAddressNew.String() + gasLimit = gas.EVMSend + gasPriceUint, err := gas.MultiplyGasPrice(medianGasPrice, TssMigrationGasMultiplierEVM) + if err != nil { + return CrossChainTx{}, err + } + evmFee := sdkmath.NewUint(gasLimit). + Mul(gasPriceUint). + Add(sdkmath.NewUintFromString(TSSMigrationBufferAmountEVM)) + if evmFee.GT(amount) { + return CrossChainTx{}, errorsmod.Wrap( + ErrInsufficientFundsTssMigration, + fmt.Sprintf( + "insufficient funds to pay for gas fee, amount: %s, gas fee: %s, chainid: %d", + amount.String(), + evmFee.String(), + chainID, + ), + ) + } + gasPrice = gasPriceUint.String() + finalAmount = amount.Sub(evmFee) + case chains.IsBitcoinChain(chainID, additionalStaticChainInfo): + bitcoinNetParams, err := chains.BitcoinNetParamsFromChainID(chainID) + if err != nil { + return CrossChainTx{}, err + } + btcAddressOld, err := zetacrypto.GetTssAddrBTC(currentTSSPubKey, bitcoinNetParams) + if err != nil { + return CrossChainTx{}, err + } + btcAddressNew, err := zetacrypto.GetTssAddrBTC(newTSSPubKey, bitcoinNetParams) + if err != nil { + return CrossChainTx{}, err + } + sender = btcAddressOld + receiver = btcAddressNew + gasLimit = 1_000_000 + gasPrice = medianGasPrice.MulUint64(2).String() + finalAmount = amount + default: + return CrossChainTx{}, errorsmod.Wrap(ErrUnsupportedChain, fmt.Sprintf("chain %d is not supported", chainID)) + } + + indexString := GetTssMigrationCCTXIndexString(currentTSSPubKey, newTSSPubKey, chainID, amount, blockHeight) + hash := crypto.Keccak256Hash([]byte(indexString)) + + return newCmdCCTX( + creator, + hash.Hex(), + fmt.Sprintf("%s:%s", constant.CmdMigrateTssFunds, "Funds Migrator Admin Cmd"), + sender, + inboundHash, + receiver, + chainID, + finalAmount, + gasLimit, + gasPrice, + priorityFee.MulUint64(2).String(), + newTSSPubKey, + ), nil +} + +// GetTssMigrationCCTXIndexString returns the index string of the CCTX for migrating funds from the current TSS to the new TSS +func GetTssMigrationCCTXIndexString( + currentTssPubkey, + newTssPubkey string, + chainID int64, + amount sdkmath.Uint, + height int64, +) string { + return fmt.Sprintf("%s-%s-%d-%s-%d", currentTssPubkey, newTssPubkey, chainID, amount.String(), height) +} + +// newCmdCCTX returns a new CCTX for admin cmd with the given parameters +func newCmdCCTX( + creator string, + index string, + relayedMessage, + sender string, + inboundHash string, + receiver string, + chainID int64, + amount sdkmath.Uint, + gasLimit uint64, + medianGasPrice string, + priorityFee string, + tssPubKey string, +) CrossChainTx { + return CrossChainTx{ + Creator: creator, + Index: index, + RelayedMessage: relayedMessage, + CctxStatus: &Status{ + Status: CctxStatus_PendingOutbound, + }, + InboundParams: &InboundParams{ + Sender: sender, + CoinType: coin.CoinType_Cmd, + ObservedHash: inboundHash, + }, + OutboundParams: []*OutboundParams{ + { + Receiver: receiver, + ReceiverChainId: chainID, + CoinType: coin.CoinType_Cmd, + Amount: amount, + GasLimit: gasLimit, + GasPrice: medianGasPrice, + GasPriorityFee: priorityFee, + TssPubkey: tssPubKey, + }, + }, + } +} diff --git a/x/crosschain/types/cmd_cctxs_test.go b/x/crosschain/types/cmd_cctxs_test.go new file mode 100644 index 0000000000..d2a21300e2 --- /dev/null +++ b/x/crosschain/types/cmd_cctxs_test.go @@ -0,0 +1,540 @@ +package types_test + +import ( + sdkmath "cosmossdk.io/math" + "fmt" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/pkg/coin" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/pkg/gas" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" + "testing" +) + +func TestMigrateERC20CustodyFundsCmdCCTX(t *testing.T) { + t.Run("returns a new CCTX for migrating ERC20 custody funds with unique index", func(t *testing.T) { + // ARRANGE + creator := sample.AccAddress() + erc20Address := sample.EthAddress().String() + custodyContractAddress := sample.EthAddress().String() + newCustodyContractAddress := sample.EthAddress().String() + chainID := int64(42) + amount := sdkmath.NewUint(1000) + gasPrice := "100000" + priorityFee := "100000" + tssPubKey := sample.PubKeyString() + currentNonce := uint64(1) + + // ACT + cctx := types.MigrateERC20CustodyFundsCmdCCTX( + creator, + erc20Address, + custodyContractAddress, + newCustodyContractAddress, + chainID, + amount, + gasPrice, + priorityFee, + tssPubKey, + currentNonce, + ) + cctxDifferentERC20Address := types.MigrateERC20CustodyFundsCmdCCTX( + creator, + sample.EthAddress().String(), + custodyContractAddress, + newCustodyContractAddress, + chainID, + amount, + gasPrice, + priorityFee, + tssPubKey, + currentNonce, + ) + cctxDifferentNonce := types.MigrateERC20CustodyFundsCmdCCTX( + creator, + erc20Address, + custodyContractAddress, + newCustodyContractAddress, + chainID, + amount, + gasPrice, + priorityFee, + tssPubKey, + currentNonce+1, + ) + cctxDifferentTSSPubkey := types.MigrateERC20CustodyFundsCmdCCTX( + creator, + erc20Address, + custodyContractAddress, + newCustodyContractAddress, + chainID, + amount, + gasPrice, + priorityFee, + sample.PubKeyString(), + currentNonce, + ) + + // ASSERT + require.NotEmpty(t, cctx.Index) + require.EqualValues(t, creator, cctx.Creator) + require.EqualValues(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.EqualValues(t, fmt.Sprintf("%s:%s,%s,1000", + constant.CmdMigrateERC20CustodyFunds, + newCustodyContractAddress, + erc20Address, + ), cctx.RelayedMessage) + require.EqualValues(t, creator, cctx.InboundParams.Sender) + require.EqualValues(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType) + require.Len(t, cctx.OutboundParams, 1) + require.EqualValues(t, custodyContractAddress, cctx.OutboundParams[0].Receiver) + require.EqualValues(t, chainID, cctx.OutboundParams[0].ReceiverChainId) + require.EqualValues(t, coin.CoinType_Cmd, cctx.OutboundParams[0].CoinType) + require.EqualValues(t, sdkmath.NewUint(0), cctx.OutboundParams[0].Amount) + require.EqualValues(t, 100_000, cctx.OutboundParams[0].GasLimit) + require.EqualValues(t, gasPrice, cctx.OutboundParams[0].GasPrice) + require.EqualValues(t, priorityFee, cctx.OutboundParams[0].GasPriorityFee) + require.EqualValues(t, tssPubKey, cctx.OutboundParams[0].TssPubkey) + + // check erc20, TSS pubkey and nonce produce unique index + require.NotEqual(t, cctx.Index, cctxDifferentERC20Address.Index) + require.NotEqual(t, cctx.Index, cctxDifferentNonce.Index) + require.NotEqual(t, cctx.Index, cctxDifferentTSSPubkey.Index) + }) +} + +func TestGetERC20CustodyMigrationCCTXIndexString(t *testing.T) { + t.Run("returns the unique index string for the CCTX for migrating ERC20 custody funds", func(t *testing.T) { + // ARRANGE + tssPubKey := sample.PubKeyString() + nonce := uint64(1) + erc20Address := sample.EthAddress().String() + + // ACT + index := types.GetERC20CustodyMigrationCCTXIndexString( + tssPubKey, + nonce, + erc20Address, + ) + indexDifferentTSSPubkey := types.GetERC20CustodyMigrationCCTXIndexString( + sample.PubKeyString(), + nonce, + erc20Address, + ) + indexDifferentNonce := types.GetERC20CustodyMigrationCCTXIndexString( + tssPubKey, + nonce+1, + erc20Address, + ) + indexDifferentERC20Address := types.GetERC20CustodyMigrationCCTXIndexString( + tssPubKey, + nonce, + sample.EthAddress().String(), + ) + + // ASSERT + require.NotEmpty(t, index) + require.NotEqual(t, index, indexDifferentTSSPubkey) + require.NotEqual(t, index, indexDifferentNonce) + require.NotEqual(t, index, indexDifferentERC20Address) + }) +} + +func TestWhitelistERC20CmdCCTX(t *testing.T) { + t.Run("returns a new CCTX for whitelisting ERC20 tokens", func(t *testing.T) { + // ARRANGE + creator := sample.AccAddress() + zrc20Address := sample.EthAddress() + erc20Address := sample.EthAddress().Hex() + custodyAddress := sample.EthAddress().Hex() + chainID := int64(42) + gasPrice := "100000" + priorityFee := "100000" + tssPubKey := sample.PubKeyString() + + // ACT + cctx := types.WhitelistERC20CmdCCTX( + creator, + zrc20Address, + erc20Address, + custodyAddress, + chainID, + gasPrice, + priorityFee, + tssPubKey, + ) + cctxDifferentZRC20Address := types.WhitelistERC20CmdCCTX( + creator, + sample.EthAddress(), + erc20Address, + custodyAddress, + chainID, + gasPrice, + priorityFee, + tssPubKey, + ) + + // ASSERT + require.NotEmpty(t, cctx.Index) + require.EqualValues(t, creator, cctx.Creator) + require.EqualValues(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.EqualValues(t, fmt.Sprintf("%s:%s", constant.CmdWhitelistERC20, erc20Address), cctx.RelayedMessage) + require.EqualValues(t, creator, cctx.InboundParams.Sender) + require.EqualValues(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType) + require.Len(t, cctx.OutboundParams, 1) + require.EqualValues(t, custodyAddress, cctx.OutboundParams[0].Receiver) + require.EqualValues(t, chainID, cctx.OutboundParams[0].ReceiverChainId) + require.EqualValues(t, coin.CoinType_Cmd, cctx.OutboundParams[0].CoinType) + require.EqualValues(t, sdkmath.NewUint(0), cctx.OutboundParams[0].Amount) + require.EqualValues(t, 100_000, cctx.OutboundParams[0].GasLimit) + require.EqualValues(t, gasPrice, cctx.OutboundParams[0].GasPrice) + require.EqualValues(t, priorityFee, cctx.OutboundParams[0].GasPriorityFee) + require.EqualValues(t, tssPubKey, cctx.OutboundParams[0].TssPubkey) + + // check zrc20 address produces unique index + require.NotEqual(t, cctx.Index, cctxDifferentZRC20Address.Index) + }) +} + +func TestMigrateFundCmdCCTX(t *testing.T) { + t.Run("returns a new CCTX for migrating funds on EVM", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.Ethereum.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := sample.Tss() + + // ACT + cctx, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.NoError(t, err) + require.NotEmpty(t, cctx.Index) + require.EqualValues(t, creator, cctx.Creator) + require.EqualValues(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.EqualValues( + t, + fmt.Sprintf("%s:%s", constant.CmdMigrateTssFunds, "Funds Migrator Admin Cmd"), + cctx.RelayedMessage, + ) + require.NotEmpty(t, cctx.InboundParams.Sender) + require.EqualValues(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType) + require.Len(t, cctx.OutboundParams, 1) + require.NotEmpty(t, cctx.OutboundParams[0].Receiver) + require.EqualValues(t, chains.Ethereum.ChainId, cctx.OutboundParams[0].ReceiverChainId) + require.EqualValues(t, coin.CoinType_Cmd, cctx.OutboundParams[0].CoinType) + require.False(t, cctx.OutboundParams[0].Amount.IsZero()) + require.EqualValues(t, gas.EVMSend, cctx.OutboundParams[0].GasLimit) + require.NotEmpty(t, cctx.OutboundParams[0].GasPrice) + require.NotEmpty(t, cctx.OutboundParams[0].GasPriorityFee) + }) + + t.Run("returns a new CCTX for migrating funds on Bitcoin", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.BitcoinMainnet.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := sample.Tss() + + // ACT + cctx, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.NoError(t, err) + require.NotEmpty(t, cctx.Index) + require.EqualValues(t, creator, cctx.Creator) + require.EqualValues(t, types.CctxStatus_PendingOutbound, cctx.CctxStatus.Status) + require.EqualValues( + t, + fmt.Sprintf("%s:%s", constant.CmdMigrateTssFunds, "Funds Migrator Admin Cmd"), + cctx.RelayedMessage, + ) + require.NotEmpty(t, cctx.InboundParams.Sender) + require.EqualValues(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType) + require.Len(t, cctx.OutboundParams, 1) + require.NotEmpty(t, cctx.OutboundParams[0].Receiver) + require.EqualValues(t, chains.BitcoinMainnet.ChainId, cctx.OutboundParams[0].ReceiverChainId) + require.EqualValues(t, coin.CoinType_Cmd, cctx.OutboundParams[0].CoinType) + require.False(t, cctx.OutboundParams[0].Amount.IsZero()) + require.EqualValues(t, uint64(1_000_000), cctx.OutboundParams[0].GasLimit) + require.NotEmpty(t, cctx.OutboundParams[0].GasPrice) + require.NotEmpty(t, cctx.OutboundParams[0].GasPriorityFee) + }) + + t.Run("prevent migration with invalid ETH address for current TSS", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.Ethereum.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := "invalid" + newTSSPubkey := sample.Tss() + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) + + t.Run("prevent migration with invalid ETH address for new TSS", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.Ethereum.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := "invalid" + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) + + t.Run("prevent migration on EVM if fees higher than amount", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.Ethereum.ChainId + amount := sdkmath.NewUint(100_000_000) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := sample.Tss() + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) + + t.Run("prevent migration with invalid Bitcoin address for current TSS", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.BitcoinMainnet.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := "invalid" + newTSSPubkey := sample.Tss() + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) + + t.Run("prevent migration with invalid Bitcoin address for new TSS", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := chains.BitcoinMainnet.ChainId + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := "invalid" + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) + + t.Run("prevent migration if invalid chain ID", func(t *testing.T) { + // ARRANGE + blockHeight := int64(1000) + creator := sample.AccAddress() + inboundHash := sample.Hash().Hex() + chainID := int64(1000) + amount := sdkmath.NewUint(1e18) + medianGasPrice := sdkmath.NewUint(100000) + priorityFee := sdkmath.NewUint(100000) + currentTSSPubkey := sample.Tss() + newTSSPubkey := sample.Tss() + + // ACT + _, err := types.MigrateFundCmdCCTX( + blockHeight, + creator, + inboundHash, + chainID, + amount, + medianGasPrice, + priorityFee, + currentTSSPubkey.TssPubkey, + newTSSPubkey.TssPubkey, + []chains.Chain{}, + ) + + // ASSERT + require.Error(t, err) + }) +} + +func TestGetTssMigrationCCTXIndexString(t *testing.T) { + t.Run("returns unique index string for the CCTX for migrating funds", func(t *testing.T) { + // ARRANGE + currentTSSPubkey := sample.PubKeyString() + newTSSPubkey := sample.PubKeyString() + chainID := int64(42) + amount := sdkmath.NewUint(1000) + height := int64(1000) + + // ACT + index := types.GetTssMigrationCCTXIndexString( + currentTSSPubkey, + newTSSPubkey, + chainID, + amount, + height, + ) + indexDifferentCurrentTSSPubkey := types.GetTssMigrationCCTXIndexString( + sample.PubKeyString(), + newTSSPubkey, + chainID, + amount, + height, + ) + indexDifferentNewTSSPubkey := types.GetTssMigrationCCTXIndexString( + currentTSSPubkey, + sample.PubKeyString(), + chainID, + amount, + height, + ) + indexDifferentChainID := types.GetTssMigrationCCTXIndexString( + currentTSSPubkey, + newTSSPubkey, + chainID+1, + amount, + height, + ) + indexDifferentAmount := types.GetTssMigrationCCTXIndexString( + currentTSSPubkey, + newTSSPubkey, + chainID, + sdkmath.NewUint(1001), + height, + ) + indexDifferentHeight := types.GetTssMigrationCCTXIndexString( + currentTSSPubkey, + newTSSPubkey, + chainID, + amount, + height+1, + ) + + // ASSERT + require.NotEmpty(t, index) + require.NotEqual(t, index, indexDifferentCurrentTSSPubkey) + require.NotEqual(t, index, indexDifferentNewTSSPubkey) + require.NotEqual(t, index, indexDifferentChainID) + require.NotEqual(t, index, indexDifferentAmount) + require.NotEqual(t, index, indexDifferentHeight) + }) +} diff --git a/x/crosschain/types/events.pb.go b/x/crosschain/types/events.pb.go index ff1dd01b62..133b88f884 100644 --- a/x/crosschain/types/events.pb.go +++ b/x/crosschain/types/events.pb.go @@ -619,6 +619,74 @@ func (m *EventERC20Whitelist) GetZrc20Address() string { return "" } +type EventERC20CustodyFundsMigration struct { + NewCustodyAddress string `protobuf:"bytes,1,opt,name=new_custody_address,json=newCustodyAddress,proto3" json:"new_custody_address,omitempty"` + Erc20Address string `protobuf:"bytes,2,opt,name=erc20_address,json=erc20Address,proto3" json:"erc20_address,omitempty"` + Amount string `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + CctxIndex string `protobuf:"bytes,4,opt,name=cctx_index,json=cctxIndex,proto3" json:"cctx_index,omitempty"` +} + +func (m *EventERC20CustodyFundsMigration) Reset() { *m = EventERC20CustodyFundsMigration{} } +func (m *EventERC20CustodyFundsMigration) String() string { return proto.CompactTextString(m) } +func (*EventERC20CustodyFundsMigration) ProtoMessage() {} +func (*EventERC20CustodyFundsMigration) Descriptor() ([]byte, []int) { + return fileDescriptor_dd08b628129fa2e1, []int{7} +} +func (m *EventERC20CustodyFundsMigration) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventERC20CustodyFundsMigration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventERC20CustodyFundsMigration.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventERC20CustodyFundsMigration) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventERC20CustodyFundsMigration.Merge(m, src) +} +func (m *EventERC20CustodyFundsMigration) XXX_Size() int { + return m.Size() +} +func (m *EventERC20CustodyFundsMigration) XXX_DiscardUnknown() { + xxx_messageInfo_EventERC20CustodyFundsMigration.DiscardUnknown(m) +} + +var xxx_messageInfo_EventERC20CustodyFundsMigration proto.InternalMessageInfo + +func (m *EventERC20CustodyFundsMigration) GetNewCustodyAddress() string { + if m != nil { + return m.NewCustodyAddress + } + return "" +} + +func (m *EventERC20CustodyFundsMigration) GetErc20Address() string { + if m != nil { + return m.Erc20Address + } + return "" +} + +func (m *EventERC20CustodyFundsMigration) GetAmount() string { + if m != nil { + return m.Amount + } + return "" +} + +func (m *EventERC20CustodyFundsMigration) GetCctxIndex() string { + if m != nil { + return m.CctxIndex + } + return "" +} + func init() { proto.RegisterType((*EventInboundFinalized)(nil), "zetachain.zetacore.crosschain.EventInboundFinalized") proto.RegisterType((*EventZrcWithdrawCreated)(nil), "zetachain.zetacore.crosschain.EventZrcWithdrawCreated") @@ -627,6 +695,7 @@ func init() { proto.RegisterType((*EventOutboundSuccess)(nil), "zetachain.zetacore.crosschain.EventOutboundSuccess") proto.RegisterType((*EventCCTXGasPriceIncreased)(nil), "zetachain.zetacore.crosschain.EventCCTXGasPriceIncreased") proto.RegisterType((*EventERC20Whitelist)(nil), "zetachain.zetacore.crosschain.EventERC20Whitelist") + proto.RegisterType((*EventERC20CustodyFundsMigration)(nil), "zetachain.zetacore.crosschain.EventERC20CustodyFundsMigration") } func init() { @@ -634,50 +703,54 @@ func init() { } var fileDescriptor_dd08b628129fa2e1 = []byte{ - // 686 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0xd1, 0x4e, 0x13, 0x4f, - 0x14, 0xc6, 0x59, 0x68, 0x4b, 0x3b, 0xb4, 0xfd, 0xff, 0x33, 0x56, 0x5d, 0x49, 0x68, 0xa0, 0xc6, - 0x68, 0x8c, 0x16, 0xc4, 0x27, 0x90, 0x06, 0x84, 0x18, 0x83, 0x01, 0x0c, 0x86, 0x9b, 0xc9, 0x74, - 0xe7, 0xb8, 0x3b, 0x71, 0xbb, 0xd3, 0xcc, 0xcc, 0xd2, 0xc2, 0x53, 0x18, 0x1f, 0xc5, 0xc4, 0x3b, - 0x1f, 0xc0, 0x4b, 0x2e, 0xbd, 0x34, 0xf4, 0x45, 0xcc, 0xcc, 0xec, 0x16, 0xda, 0x1a, 0xbd, 0x30, - 0x9a, 0x78, 0xb7, 0xe7, 0x3b, 0x67, 0xa6, 0xbf, 0xf9, 0xbe, 0xed, 0x0e, 0x7a, 0x78, 0x0e, 0x9a, - 0x06, 0x11, 0xe5, 0xc9, 0xba, 0x7d, 0x12, 0x12, 0xd6, 0x03, 0x29, 0x94, 0x72, 0x1a, 0x9c, 0x42, - 0xa2, 0x55, 0xbb, 0x2f, 0x85, 0x16, 0x78, 0x65, 0x3c, 0xdb, 0xce, 0x67, 0xdb, 0x57, 0xb3, 0xcb, - 0x8d, 0x50, 0x84, 0xc2, 0x4e, 0xae, 0x9b, 0x27, 0xb7, 0xa8, 0x35, 0x5a, 0x40, 0x37, 0xb7, 0xcd, - 0x2e, 0x7b, 0x49, 0x57, 0xa4, 0x09, 0xdb, 0xe1, 0x09, 0x8d, 0xf9, 0x39, 0x30, 0xbc, 0x8a, 0xaa, - 0x3d, 0x15, 0x12, 0x7d, 0xd6, 0x07, 0x92, 0xca, 0xd8, 0xf7, 0x56, 0xbd, 0x07, 0x95, 0x03, 0xd4, - 0x53, 0xe1, 0xd1, 0x59, 0x1f, 0x5e, 0xcb, 0x18, 0xaf, 0x20, 0x14, 0x04, 0x7a, 0x48, 0x78, 0xc2, - 0x60, 0xe8, 0xcf, 0xdb, 0x7e, 0xc5, 0x28, 0x7b, 0x46, 0xc0, 0xb7, 0x50, 0x49, 0x41, 0xc2, 0x40, - 0xfa, 0x0b, 0xb6, 0x95, 0x55, 0xf8, 0x0e, 0x2a, 0xeb, 0x21, 0x11, 0x32, 0xe4, 0x89, 0x5f, 0xb0, - 0x9d, 0x45, 0x3d, 0xdc, 0x37, 0x25, 0x6e, 0xa0, 0x22, 0x55, 0x0a, 0xb4, 0x5f, 0xb4, 0xba, 0x2b, - 0xf0, 0x1a, 0xaa, 0x72, 0x47, 0x47, 0x22, 0xaa, 0x22, 0xbf, 0x64, 0x9b, 0x4b, 0x99, 0xb6, 0x4b, - 0x55, 0x84, 0x37, 0x50, 0x23, 0x1f, 0xe9, 0xc6, 0x22, 0x78, 0x47, 0x22, 0xe0, 0x61, 0xa4, 0xfd, - 0x45, 0x3b, 0x8a, 0xb3, 0xde, 0x96, 0x69, 0xed, 0xda, 0x0e, 0x5e, 0x46, 0x65, 0x09, 0x01, 0xf0, - 0x53, 0x90, 0x7e, 0xd9, 0x4e, 0x8d, 0x6b, 0x7c, 0x0f, 0xd5, 0xf3, 0x67, 0x62, 0xcd, 0xf3, 0x2b, - 0x76, 0xa2, 0x96, 0xab, 0x1d, 0x23, 0x9a, 0x03, 0xd2, 0x9e, 0x48, 0x13, 0xed, 0x23, 0x77, 0x40, - 0x57, 0xe1, 0xfb, 0xe8, 0x3f, 0x09, 0x31, 0x3d, 0x03, 0x46, 0x7a, 0xa0, 0x14, 0x0d, 0xc1, 0x5f, - 0xb2, 0x03, 0xf5, 0x4c, 0x7e, 0xe9, 0x54, 0x63, 0x60, 0x02, 0x03, 0xa2, 0x34, 0xd5, 0xa9, 0xf2, - 0xab, 0xce, 0xc0, 0x04, 0x06, 0x87, 0x56, 0x30, 0x18, 0xae, 0x35, 0xde, 0xa6, 0xe6, 0x30, 0x9c, - 0x9a, 0xef, 0xb2, 0x86, 0xaa, 0xce, 0xd9, 0x8c, 0xb5, 0xee, 0xec, 0x71, 0x9a, 0x25, 0x6d, 0x7d, - 0x9c, 0x47, 0xb7, 0x6d, 0xca, 0x27, 0x32, 0x38, 0xe6, 0x3a, 0x62, 0x92, 0x0e, 0x3a, 0x12, 0xa8, - 0xfe, 0x93, 0x39, 0x4f, 0x73, 0x15, 0x66, 0xb8, 0x66, 0x92, 0x2d, 0xce, 0x26, 0x7b, 0x3d, 0xa7, - 0xd2, 0x2f, 0x73, 0x5a, 0xfc, 0x79, 0x4e, 0xe5, 0x89, 0x9c, 0x26, 0xed, 0xaf, 0x4c, 0xd9, 0xdf, - 0xfa, 0xe4, 0x21, 0xdf, 0x99, 0x06, 0x9a, 0xfe, 0x4d, 0xd7, 0x26, 0x2c, 0x29, 0xcc, 0x5a, 0x32, - 0xc9, 0x5d, 0x9c, 0xe6, 0xfe, 0xec, 0xa1, 0x86, 0xe5, 0xde, 0x4f, 0xb5, 0xfb, 0x4f, 0x53, 0x1e, - 0xa7, 0x12, 0x7e, 0x9f, 0x79, 0x05, 0x21, 0x11, 0xb3, 0xfc, 0x87, 0x1d, 0x77, 0x45, 0xc4, 0x2c, - 0x7b, 0x5f, 0x27, 0xb9, 0x0a, 0x3f, 0x78, 0x9d, 0x4f, 0x69, 0x9c, 0x02, 0xc9, 0xd2, 0x61, 0x19, - 0x7a, 0xcd, 0xaa, 0x07, 0x99, 0x38, 0x8b, 0x7f, 0x98, 0x06, 0x01, 0x28, 0xf5, 0x8f, 0xe0, 0x7f, - 0xf0, 0xd0, 0xb2, 0xc5, 0xef, 0x74, 0x8e, 0xde, 0x3c, 0xa7, 0xea, 0x95, 0xe4, 0x01, 0xec, 0x25, - 0x81, 0x04, 0xaa, 0x80, 0x4d, 0x21, 0x7a, 0xd3, 0x88, 0x8f, 0x10, 0x0e, 0xa9, 0x22, 0x7d, 0xb3, - 0x88, 0xf0, 0x6c, 0x55, 0x76, 0x92, 0xff, 0xc3, 0xa9, 0xdd, 0xcc, 0x87, 0x86, 0x32, 0xc6, 0x35, - 0x17, 0x09, 0x8d, 0xc9, 0x5b, 0x80, 0xfc, 0x54, 0xf5, 0x2b, 0x79, 0x07, 0x40, 0xb5, 0x62, 0x74, - 0xc3, 0x32, 0x6d, 0x1f, 0x74, 0x36, 0x37, 0x8e, 0x23, 0xae, 0x21, 0xe6, 0x4a, 0x9b, 0xaf, 0xe6, - 0x20, 0x2f, 0xc8, 0x0c, 0x16, 0x1e, 0xf7, 0x3a, 0x63, 0xbe, 0xbb, 0xa8, 0x76, 0x2e, 0x83, 0xcd, - 0x0d, 0x42, 0x19, 0x93, 0xa0, 0x54, 0x86, 0x56, 0xb5, 0xe2, 0x33, 0xa7, 0x6d, 0xbd, 0xf8, 0x72, - 0xd9, 0xf4, 0x2e, 0x2e, 0x9b, 0xde, 0xb7, 0xcb, 0xa6, 0xf7, 0x7e, 0xd4, 0x9c, 0xbb, 0x18, 0x35, - 0xe7, 0xbe, 0x8e, 0x9a, 0x73, 0x27, 0x4f, 0x42, 0xae, 0xa3, 0xb4, 0xdb, 0x0e, 0x44, 0xcf, 0xde, - 0x67, 0x8f, 0xa7, 0xae, 0xb6, 0xe1, 0xf5, 0xcb, 0xcd, 0x04, 0xad, 0xba, 0x25, 0x7b, 0x4f, 0x3d, - 0xfd, 0x1e, 0x00, 0x00, 0xff, 0xff, 0xc4, 0x44, 0xd3, 0x7e, 0x0a, 0x07, 0x00, 0x00, + // 752 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x95, 0x41, 0x4f, 0xdb, 0x48, + 0x14, 0xc7, 0x31, 0x24, 0x21, 0x19, 0x92, 0xec, 0xae, 0xc9, 0xee, 0x7a, 0x91, 0xc8, 0x42, 0x56, + 0xab, 0x5d, 0xad, 0xb6, 0x81, 0xd2, 0x4f, 0x50, 0x22, 0x28, 0xa8, 0x42, 0x54, 0x40, 0x45, 0xc5, + 0xc5, 0x9a, 0x78, 0x5e, 0xed, 0x51, 0x9d, 0x99, 0x68, 0x66, 0x4c, 0x12, 0x3e, 0x45, 0xd5, 0xef, + 0xd1, 0x4b, 0xa5, 0xde, 0xfa, 0x01, 0x7a, 0xe4, 0xd8, 0x63, 0x45, 0xbe, 0x48, 0x35, 0x33, 0x76, + 0x48, 0x1c, 0xd4, 0x1e, 0xaa, 0x56, 0xea, 0xcd, 0xf3, 0x7f, 0x6f, 0xde, 0xfb, 0xcd, 0xff, 0xd9, + 0x1e, 0xf4, 0xdf, 0x15, 0x28, 0x1c, 0x44, 0x98, 0xb2, 0x2d, 0xf3, 0xc4, 0x05, 0x6c, 0x05, 0x82, + 0x4b, 0x69, 0x35, 0xb8, 0x04, 0xa6, 0x64, 0xbb, 0x2f, 0xb8, 0xe2, 0xee, 0xfa, 0x24, 0xb7, 0x9d, + 0xe5, 0xb6, 0x6f, 0x73, 0xd7, 0x1a, 0x21, 0x0f, 0xb9, 0xc9, 0xdc, 0xd2, 0x4f, 0x76, 0x53, 0x6b, + 0xbc, 0x84, 0x7e, 0xdd, 0xd3, 0x55, 0x0e, 0x59, 0x97, 0x27, 0x8c, 0xec, 0x53, 0x86, 0x63, 0x7a, + 0x05, 0xc4, 0xdd, 0x40, 0xd5, 0x9e, 0x0c, 0x7d, 0x35, 0xea, 0x83, 0x9f, 0x88, 0xd8, 0x73, 0x36, + 0x9c, 0x7f, 0x2b, 0x27, 0xa8, 0x27, 0xc3, 0xb3, 0x51, 0x1f, 0x9e, 0x8a, 0xd8, 0x5d, 0x47, 0x28, + 0x08, 0xd4, 0xd0, 0xa7, 0x8c, 0xc0, 0xd0, 0x5b, 0x34, 0xf1, 0x8a, 0x56, 0x0e, 0xb5, 0xe0, 0xfe, + 0x86, 0x4a, 0x12, 0x18, 0x01, 0xe1, 0x2d, 0x99, 0x50, 0xba, 0x72, 0xff, 0x40, 0x65, 0x35, 0xf4, + 0xb9, 0x08, 0x29, 0xf3, 0x0a, 0x26, 0xb2, 0xac, 0x86, 0xc7, 0x7a, 0xe9, 0x36, 0x50, 0x11, 0x4b, + 0x09, 0xca, 0x2b, 0x1a, 0xdd, 0x2e, 0xdc, 0x4d, 0x54, 0xa5, 0x96, 0xce, 0x8f, 0xb0, 0x8c, 0xbc, + 0x92, 0x09, 0xae, 0xa4, 0xda, 0x01, 0x96, 0x91, 0xbb, 0x8d, 0x1a, 0x59, 0x4a, 0x37, 0xe6, 0xc1, + 0x0b, 0x3f, 0x02, 0x1a, 0x46, 0xca, 0x5b, 0x36, 0xa9, 0x6e, 0x1a, 0xdb, 0xd5, 0xa1, 0x03, 0x13, + 0x71, 0xd7, 0x50, 0x59, 0x40, 0x00, 0xf4, 0x12, 0x84, 0x57, 0x36, 0x59, 0x93, 0xb5, 0xfb, 0x37, + 0xaa, 0x67, 0xcf, 0xbe, 0x31, 0xcf, 0xab, 0x98, 0x8c, 0x5a, 0xa6, 0x76, 0xb4, 0xa8, 0x0f, 0x88, + 0x7b, 0x3c, 0x61, 0xca, 0x43, 0xf6, 0x80, 0x76, 0xe5, 0xfe, 0x83, 0x7e, 0x12, 0x10, 0xe3, 0x11, + 0x10, 0xbf, 0x07, 0x52, 0xe2, 0x10, 0xbc, 0x15, 0x93, 0x50, 0x4f, 0xe5, 0x23, 0xab, 0x6a, 0x03, + 0x19, 0x0c, 0x7c, 0xa9, 0xb0, 0x4a, 0xa4, 0x57, 0xb5, 0x06, 0x32, 0x18, 0x9c, 0x1a, 0x41, 0x63, + 0xd8, 0xd0, 0xa4, 0x4c, 0xcd, 0x62, 0x58, 0x35, 0xab, 0xb2, 0x89, 0xaa, 0xd6, 0xd9, 0x94, 0xb5, + 0x6e, 0xed, 0xb1, 0x9a, 0x21, 0x6d, 0xbd, 0x59, 0x44, 0xbf, 0x9b, 0x29, 0x5f, 0x88, 0xe0, 0x9c, + 0xaa, 0x88, 0x08, 0x3c, 0xe8, 0x08, 0xc0, 0xea, 0x5b, 0xce, 0x39, 0xcf, 0x55, 0x98, 0xe3, 0x9a, + 0x9b, 0x6c, 0x71, 0x7e, 0xb2, 0xd3, 0x73, 0x2a, 0x7d, 0x71, 0x4e, 0xcb, 0x9f, 0x9f, 0x53, 0x79, + 0x66, 0x4e, 0xb3, 0xf6, 0x57, 0x72, 0xf6, 0xb7, 0xde, 0x3a, 0xc8, 0xb3, 0xa6, 0x81, 0xc2, 0xdf, + 0xd3, 0xb5, 0x19, 0x4b, 0x0a, 0xf3, 0x96, 0xcc, 0x72, 0x17, 0xf3, 0xdc, 0xef, 0x1c, 0xd4, 0x30, + 0xdc, 0xc7, 0x89, 0xb2, 0xdf, 0x34, 0xa6, 0x71, 0x22, 0xe0, 0xeb, 0x99, 0xd7, 0x11, 0xe2, 0x31, + 0xc9, 0x1a, 0x5b, 0xee, 0x0a, 0x8f, 0x49, 0xfa, 0xbe, 0xce, 0x72, 0x15, 0xee, 0x78, 0x9d, 0x2f, + 0x71, 0x9c, 0x80, 0x9f, 0x4e, 0x87, 0xa4, 0xe8, 0x35, 0xa3, 0x9e, 0xa4, 0xe2, 0x3c, 0xfe, 0x69, + 0x12, 0x04, 0x20, 0xe5, 0x0f, 0x82, 0xff, 0xca, 0x41, 0x6b, 0x06, 0xbf, 0xd3, 0x39, 0x7b, 0xf6, + 0x08, 0xcb, 0x27, 0x82, 0x06, 0x70, 0xc8, 0x02, 0x01, 0x58, 0x02, 0xc9, 0x21, 0x3a, 0x79, 0xc4, + 0xff, 0x91, 0x1b, 0x62, 0xe9, 0xf7, 0xf5, 0x26, 0x9f, 0xa6, 0xbb, 0xd2, 0x93, 0xfc, 0x1c, 0xe6, + 0xaa, 0xe9, 0x1f, 0x0d, 0x26, 0x84, 0x2a, 0xca, 0x19, 0x8e, 0xfd, 0xe7, 0x00, 0xd9, 0xa9, 0xea, + 0xb7, 0xf2, 0x3e, 0x80, 0x6c, 0xc5, 0x68, 0xd5, 0x30, 0xed, 0x9d, 0x74, 0x76, 0xb6, 0xcf, 0x23, + 0xaa, 0x20, 0xa6, 0x52, 0xe9, 0xbf, 0xe6, 0x20, 0x5b, 0xf8, 0x73, 0x58, 0xee, 0x24, 0xd6, 0x99, + 0xf0, 0xfd, 0x85, 0x6a, 0x57, 0x22, 0xd8, 0xd9, 0xf6, 0x31, 0x21, 0x02, 0xa4, 0x4c, 0xd1, 0xaa, + 0x46, 0x7c, 0x68, 0xb5, 0xd6, 0x6b, 0x07, 0xfd, 0x79, 0xdb, 0xae, 0x93, 0x48, 0xc5, 0xc9, 0x68, + 0x3f, 0x61, 0x44, 0x1e, 0xd1, 0x50, 0x60, 0xcd, 0xe5, 0xb6, 0xd1, 0xaa, 0x36, 0x3b, 0xb0, 0xc1, + 0x49, 0x39, 0xdb, 0xf9, 0x17, 0x06, 0x83, 0x74, 0x5b, 0x5a, 0x53, 0x37, 0x86, 0xbb, 0x1a, 0xc3, + 0x54, 0xe3, 0xa9, 0x0f, 0x7d, 0x29, 0xff, 0xa1, 0x4f, 0x9d, 0xae, 0x90, 0x33, 0x7d, 0xf7, 0xf1, + 0xfb, 0x9b, 0xa6, 0x73, 0x7d, 0xd3, 0x74, 0x3e, 0xde, 0x34, 0x9d, 0x97, 0xe3, 0xe6, 0xc2, 0xf5, + 0xb8, 0xb9, 0xf0, 0x61, 0xdc, 0x5c, 0xb8, 0xb8, 0x1f, 0x52, 0x15, 0x25, 0xdd, 0x76, 0xc0, 0x7b, + 0xe6, 0xfe, 0xbd, 0x97, 0xbb, 0x8a, 0x87, 0xd3, 0x97, 0xb1, 0x7e, 0x31, 0x65, 0xb7, 0x64, 0xee, + 0xd5, 0x07, 0x9f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x99, 0x3d, 0xe9, 0xec, 0xba, 0x07, 0x00, 0x00, } func (m *EventInboundFinalized) Marshal() (dAtA []byte, err error) { @@ -1142,6 +1215,57 @@ func (m *EventERC20Whitelist) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *EventERC20CustodyFundsMigration) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventERC20CustodyFundsMigration) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventERC20CustodyFundsMigration) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CctxIndex) > 0 { + i -= len(m.CctxIndex) + copy(dAtA[i:], m.CctxIndex) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CctxIndex))) + i-- + dAtA[i] = 0x22 + } + if len(m.Amount) > 0 { + i -= len(m.Amount) + copy(dAtA[i:], m.Amount) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Amount))) + i-- + dAtA[i] = 0x1a + } + if len(m.Erc20Address) > 0 { + i -= len(m.Erc20Address) + copy(dAtA[i:], m.Erc20Address) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Erc20Address))) + i-- + dAtA[i] = 0x12 + } + if len(m.NewCustodyAddress) > 0 { + i -= len(m.NewCustodyAddress) + copy(dAtA[i:], m.NewCustodyAddress) + i = encodeVarintEvents(dAtA, i, uint64(len(m.NewCustodyAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { offset -= sovEvents(v) base := offset @@ -1388,6 +1512,31 @@ func (m *EventERC20Whitelist) Size() (n int) { return n } +func (m *EventERC20CustodyFundsMigration) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.NewCustodyAddress) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Erc20Address) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.Amount) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.CctxIndex) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + func sovEvents(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -3120,6 +3269,184 @@ func (m *EventERC20Whitelist) Unmarshal(dAtA []byte) error { } return nil } +func (m *EventERC20CustodyFundsMigration) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventERC20CustodyFundsMigration: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventERC20CustodyFundsMigration: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewCustodyAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewCustodyAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Erc20Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipEvents(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/keys.go b/x/crosschain/types/keys.go index 8496ccf608..d9fea4a77d 100644 --- a/x/crosschain/types/keys.go +++ b/x/crosschain/types/keys.go @@ -25,12 +25,6 @@ const ( ProtocolFee = 2000000000000000000 - // TssMigrationGasMultiplierEVM is multiplied to the median gas price to get the gas price for the tss migration . - // This is done to avoid the tss migration tx getting stuck in the mempool - TssMigrationGasMultiplierEVM = "2.5" - // TSSMigrationBufferAmountEVM is the buffer amount added to the gas price for the tss migration transaction - TSSMigrationBufferAmountEVM = "2100000000" - // CCTXIndexLength is the length of a crosschain transaction index CCTXIndexLength = 66 ) diff --git a/x/crosschain/types/message_migrate_erc20_custody_funds.go b/x/crosschain/types/message_migrate_erc20_custody_funds.go new file mode 100644 index 0000000000..da69db1fd8 --- /dev/null +++ b/x/crosschain/types/message_migrate_erc20_custody_funds.go @@ -0,0 +1,68 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethcommon "github.com/ethereum/go-ethereum/common" +) + +const TypeMsgMigrateERC20CustodyFunds = "MigrateERC20CustodyFunds" + +var _ sdk.Msg = &MsgMigrateERC20CustodyFunds{} + +func NewMsgMigrateERC20CustodyFunds( + creator string, + chainID int64, + newCustodyAddress string, + erc20Address string, + amount sdkmath.Uint, +) *MsgMigrateERC20CustodyFunds { + return &MsgMigrateERC20CustodyFunds{ + Creator: creator, + ChainId: chainID, + NewCustodyAddress: newCustodyAddress, + Erc20Address: erc20Address, + Amount: amount, + } +} + +func (msg *MsgMigrateERC20CustodyFunds) Route() string { + return RouterKey +} + +func (msg *MsgMigrateERC20CustodyFunds) Type() string { + return TypeMsgMigrateERC20CustodyFunds +} + +func (msg *MsgMigrateERC20CustodyFunds) GetSigners() []sdk.AccAddress { + creator, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + return []sdk.AccAddress{creator} +} + +func (msg *MsgMigrateERC20CustodyFunds) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(msg) + return sdk.MustSortJSON(bz) +} + +func (msg *MsgMigrateERC20CustodyFunds) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) + } + + switch { + case !ethcommon.IsHexAddress(msg.NewCustodyAddress): + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid new custody address") + case !ethcommon.IsHexAddress(msg.Erc20Address): + return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid erc20 address") + case msg.Amount.IsZero(): + return errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "amount cannot be zero") + } + + return nil +} diff --git a/x/crosschain/types/message_migrate_erc20_custody_funds_test.go b/x/crosschain/types/message_migrate_erc20_custody_funds_test.go new file mode 100644 index 0000000000..9a86b6d141 --- /dev/null +++ b/x/crosschain/types/message_migrate_erc20_custody_funds_test.go @@ -0,0 +1,169 @@ +package types_test + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/pkg/chains" + "github.com/zeta-chain/zetacore/testutil/keeper" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +func TestNewMsgMigrateERC20CustodyFunds_ValidateBasic(t *testing.T) { + keeper.SetConfig(false) + tests := []struct { + name string + msg *types.MsgMigrateERC20CustodyFunds + error bool + }{ + { + name: "invalid creator", + msg: types.NewMsgMigrateERC20CustodyFunds( + "invalid address", + chains.DefaultChainsList()[0].ChainId, + sample.EthAddress().String(), + sample.EthAddress().String(), + sdkmath.NewUintFromString("100000"), + ), + error: true, + }, + { + name: "invalid amount", + msg: types.NewMsgMigrateERC20CustodyFunds( + sample.AccAddress(), + chains.DefaultChainsList()[0].ChainId, + sample.EthAddress().String(), + sample.EthAddress().String(), + sdkmath.NewUintFromString("0"), + ), + error: true, + }, + { + name: "valid msg", + msg: types.NewMsgMigrateERC20CustodyFunds( + sample.AccAddress(), + chains.DefaultChainsList()[0].ChainId, + sample.EthAddress().String(), + sample.EthAddress().String(), + sdkmath.NewUintFromString("100000"), + ), + }, + { + name: "invalid erc20 address", + msg: types.NewMsgMigrateERC20CustodyFunds( + sample.AccAddress(), + chains.DefaultChainsList()[0].ChainId, + sample.EthAddress().String(), + "invalid address", + sdkmath.NewUintFromString("100000"), + ), + error: true, + }, + { + name: "invalid new custody address", + msg: types.NewMsgMigrateERC20CustodyFunds( + sample.AccAddress(), + chains.DefaultChainsList()[0].ChainId, + "invalid address", + sample.EthAddress().String(), + sdkmath.NewUintFromString("100000"), + ), + error: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.msg.ValidateBasic() + if tt.error { + require.Error(t, err) + return + } else { + require.NoError(t, err) + } + }) + } +} + +func TestNewMsgMigrateERC20CustodyFunds_GetSigners(t *testing.T) { + signer := sample.AccAddress() + tests := []struct { + name string + msg types.MsgMigrateERC20CustodyFunds + panics bool + }{ + { + name: "valid signer", + msg: types.MsgMigrateERC20CustodyFunds{ + Creator: signer, + ChainId: chains.DefaultChainsList()[0].ChainId, + NewCustodyAddress: sample.EthAddress().String(), + Erc20Address: sample.EthAddress().String(), + Amount: sdkmath.NewUintFromString("100000"), + }, + panics: false, + }, + { + name: "invalid signer", + msg: types.MsgMigrateERC20CustodyFunds{ + Creator: "invalid_address", + ChainId: chains.DefaultChainsList()[0].ChainId, + NewCustodyAddress: sample.EthAddress().String(), + Erc20Address: sample.EthAddress().String(), + Amount: sdkmath.NewUintFromString("100000"), + }, + panics: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if !tt.panics { + signers := tt.msg.GetSigners() + require.Equal(t, []sdk.AccAddress{sdk.MustAccAddressFromBech32(signer)}, signers) + } else { + require.Panics(t, func() { + tt.msg.GetSigners() + }) + } + }) + } +} + +func TestNewMsgMigrateERC20CustodyFunds_Type(t *testing.T) { + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chains.DefaultChainsList()[0].ChainId, + NewCustodyAddress: sample.EthAddress().String(), + Erc20Address: sample.EthAddress().String(), + Amount: sdkmath.NewUintFromString("100000"), + } + require.Equal(t, types.TypeMsgMigrateERC20CustodyFunds, msg.Type()) +} + +func TestNewMsgMigrateERC20CustodyFunds_Route(t *testing.T) { + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chains.DefaultChainsList()[0].ChainId, + NewCustodyAddress: sample.EthAddress().String(), + Erc20Address: sample.EthAddress().String(), + Amount: sdkmath.NewUintFromString("100000"), + } + require.Equal(t, types.RouterKey, msg.Route()) +} + +func TestNewMsgMigrateERC20CustodyFunds_GetSignBytes(t *testing.T) { + msg := types.MsgMigrateERC20CustodyFunds{ + Creator: sample.AccAddress(), + ChainId: chains.DefaultChainsList()[0].ChainId, + NewCustodyAddress: sample.EthAddress().String(), + Erc20Address: sample.EthAddress().String(), + Amount: sdkmath.NewUintFromString("100000"), + } + require.NotPanics(t, func() { + msg.GetSignBytes() + }) +} diff --git a/x/crosschain/types/tx.pb.go b/x/crosschain/types/tx.pb.go index 4bd154f595..55be051cae 100644 --- a/x/crosschain/types/tx.pb.go +++ b/x/crosschain/types/tx.pb.go @@ -1431,6 +1431,119 @@ func (m *MsgUpdateRateLimiterFlagsResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgUpdateRateLimiterFlagsResponse proto.InternalMessageInfo +type MsgMigrateERC20CustodyFunds struct { + Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` + ChainId int64 `protobuf:"varint,2,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + NewCustodyAddress string `protobuf:"bytes,3,opt,name=new_custody_address,json=newCustodyAddress,proto3" json:"new_custody_address,omitempty"` + Erc20Address string `protobuf:"bytes,4,opt,name=erc20_address,json=erc20Address,proto3" json:"erc20_address,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Uint `protobuf:"bytes,5,opt,name=amount,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Uint" json:"amount"` +} + +func (m *MsgMigrateERC20CustodyFunds) Reset() { *m = MsgMigrateERC20CustodyFunds{} } +func (m *MsgMigrateERC20CustodyFunds) String() string { return proto.CompactTextString(m) } +func (*MsgMigrateERC20CustodyFunds) ProtoMessage() {} +func (*MsgMigrateERC20CustodyFunds) Descriptor() ([]byte, []int) { + return fileDescriptor_15f0860550897740, []int{24} +} +func (m *MsgMigrateERC20CustodyFunds) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMigrateERC20CustodyFunds) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMigrateERC20CustodyFunds.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMigrateERC20CustodyFunds) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMigrateERC20CustodyFunds.Merge(m, src) +} +func (m *MsgMigrateERC20CustodyFunds) XXX_Size() int { + return m.Size() +} +func (m *MsgMigrateERC20CustodyFunds) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMigrateERC20CustodyFunds.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMigrateERC20CustodyFunds proto.InternalMessageInfo + +func (m *MsgMigrateERC20CustodyFunds) GetCreator() string { + if m != nil { + return m.Creator + } + return "" +} + +func (m *MsgMigrateERC20CustodyFunds) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *MsgMigrateERC20CustodyFunds) GetNewCustodyAddress() string { + if m != nil { + return m.NewCustodyAddress + } + return "" +} + +func (m *MsgMigrateERC20CustodyFunds) GetErc20Address() string { + if m != nil { + return m.Erc20Address + } + return "" +} + +type MsgMigrateERC20CustodyFundsResponse struct { + CctxIndex string `protobuf:"bytes,1,opt,name=cctx_index,json=cctxIndex,proto3" json:"cctx_index,omitempty"` +} + +func (m *MsgMigrateERC20CustodyFundsResponse) Reset() { *m = MsgMigrateERC20CustodyFundsResponse{} } +func (m *MsgMigrateERC20CustodyFundsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgMigrateERC20CustodyFundsResponse) ProtoMessage() {} +func (*MsgMigrateERC20CustodyFundsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_15f0860550897740, []int{25} +} +func (m *MsgMigrateERC20CustodyFundsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgMigrateERC20CustodyFundsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgMigrateERC20CustodyFundsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgMigrateERC20CustodyFundsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgMigrateERC20CustodyFundsResponse.Merge(m, src) +} +func (m *MsgMigrateERC20CustodyFundsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgMigrateERC20CustodyFundsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgMigrateERC20CustodyFundsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgMigrateERC20CustodyFundsResponse proto.InternalMessageInfo + +func (m *MsgMigrateERC20CustodyFundsResponse) GetCctxIndex() string { + if m != nil { + return m.CctxIndex + } + return "" +} + func init() { proto.RegisterType((*MsgMigrateTssFunds)(nil), "zetachain.zetacore.crosschain.MsgMigrateTssFunds") proto.RegisterType((*MsgMigrateTssFundsResponse)(nil), "zetachain.zetacore.crosschain.MsgMigrateTssFundsResponse") @@ -1456,6 +1569,8 @@ func init() { proto.RegisterType((*MsgRefundAbortedCCTXResponse)(nil), "zetachain.zetacore.crosschain.MsgRefundAbortedCCTXResponse") proto.RegisterType((*MsgUpdateRateLimiterFlags)(nil), "zetachain.zetacore.crosschain.MsgUpdateRateLimiterFlags") proto.RegisterType((*MsgUpdateRateLimiterFlagsResponse)(nil), "zetachain.zetacore.crosschain.MsgUpdateRateLimiterFlagsResponse") + proto.RegisterType((*MsgMigrateERC20CustodyFunds)(nil), "zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds") + proto.RegisterType((*MsgMigrateERC20CustodyFundsResponse)(nil), "zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFundsResponse") } func init() { @@ -1463,105 +1578,110 @@ func init() { } var fileDescriptor_15f0860550897740 = []byte{ - // 1557 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xcf, 0x36, 0x8e, 0x63, 0x3f, 0xc7, 0x4e, 0xba, 0xa4, 0xa9, 0xb3, 0x69, 0x9c, 0xd4, 0xa5, - 0x21, 0x42, 0xad, 0x9d, 0xba, 0xa5, 0x94, 0x16, 0x01, 0x8d, 0xe9, 0x9f, 0x40, 0xdd, 0x46, 0xdb, - 0x14, 0x10, 0x97, 0xd5, 0x7a, 0x77, 0xb2, 0x5e, 0xd9, 0xde, 0xb1, 0x76, 0xc6, 0x96, 0x53, 0x21, - 0x81, 0x90, 0x90, 0x38, 0x02, 0xe2, 0xc4, 0x81, 0x1b, 0x12, 0x67, 0x3e, 0x45, 0x8f, 0x15, 0x27, - 0xc4, 0xa1, 0x42, 0xed, 0x27, 0x80, 0x4f, 0x80, 0x76, 0x66, 0x76, 0xeb, 0x5d, 0xff, 0x8d, 0x2b, - 0xc4, 0xc5, 0xde, 0x79, 0xf3, 0x7e, 0x6f, 0xde, 0xbf, 0x79, 0xef, 0xed, 0xc2, 0xd6, 0x63, 0x44, - 0x75, 0xa3, 0xa6, 0xdb, 0x4e, 0x91, 0x3d, 0x61, 0x17, 0x15, 0x0d, 0x17, 0x13, 0xc2, 0x69, 0xb4, - 0x5b, 0x68, 0xb9, 0x98, 0x62, 0x79, 0x3d, 0xe0, 0x2b, 0xf8, 0x7c, 0x85, 0x97, 0x7c, 0xca, 0xb2, - 0x85, 0x2d, 0xcc, 0x38, 0x8b, 0xde, 0x13, 0x07, 0x29, 0x6f, 0x0e, 0x10, 0xde, 0xaa, 0x5b, 0x45, - 0x46, 0x22, 0xe2, 0x4f, 0xf0, 0x6e, 0x0d, 0xe3, 0xc5, 0xb6, 0xc3, 0x7e, 0xc6, 0xc8, 0x6c, 0xb9, - 0x18, 0x1f, 0x12, 0xf1, 0x27, 0x78, 0xaf, 0x8e, 0x36, 0xce, 0xd5, 0x29, 0xd2, 0x1a, 0x76, 0xd3, - 0xa6, 0xc8, 0xd5, 0x0e, 0x1b, 0xba, 0x25, 0x70, 0xf9, 0x1f, 0x24, 0x90, 0x2b, 0xc4, 0xaa, 0xd8, - 0x96, 0xc7, 0x72, 0x40, 0xc8, 0xed, 0xb6, 0x63, 0x12, 0x39, 0x0b, 0xf3, 0x86, 0x8b, 0x74, 0x8a, - 0xdd, 0xac, 0xb4, 0x29, 0x6d, 0x27, 0x55, 0x7f, 0x29, 0xaf, 0x42, 0x82, 0x89, 0xd4, 0x6c, 0x33, - 0x7b, 0x62, 0x53, 0xda, 0x9e, 0x55, 0xe7, 0xd9, 0x7a, 0xcf, 0x94, 0xef, 0x40, 0x5c, 0x6f, 0xe2, - 0xb6, 0x43, 0xb3, 0xb3, 0x1e, 0x66, 0xb7, 0xf8, 0xe4, 0xd9, 0xc6, 0xcc, 0x9f, 0xcf, 0x36, 0xde, - 0xb0, 0x6c, 0x5a, 0x6b, 0x57, 0x0b, 0x06, 0x6e, 0x16, 0x0d, 0x4c, 0x9a, 0x98, 0x88, 0xbf, 0x8b, - 0xc4, 0xac, 0x17, 0xe9, 0x51, 0x0b, 0x91, 0xc2, 0x23, 0xdb, 0xa1, 0xaa, 0x80, 0xe7, 0xcf, 0x80, - 0xd2, 0xaf, 0x93, 0x8a, 0x48, 0x0b, 0x3b, 0x04, 0xe5, 0xef, 0xc3, 0x6b, 0x15, 0x62, 0x3d, 0x6a, - 0x99, 0x7c, 0xf3, 0xa6, 0x69, 0xba, 0x88, 0x8c, 0x52, 0x79, 0x1d, 0x80, 0x12, 0xa2, 0xb5, 0xda, - 0xd5, 0x3a, 0x3a, 0x62, 0x4a, 0x27, 0xd5, 0x24, 0x25, 0x64, 0x9f, 0x11, 0xf2, 0xeb, 0xb0, 0x36, - 0x40, 0x5e, 0x70, 0xdc, 0xcf, 0x27, 0x60, 0xb9, 0x42, 0xac, 0x9b, 0xa6, 0xb9, 0xe7, 0x54, 0x71, - 0xdb, 0x31, 0x0f, 0x5c, 0xdd, 0xa8, 0x23, 0x77, 0x3a, 0x1f, 0x9d, 0x86, 0x79, 0xda, 0xd5, 0x6a, - 0x3a, 0xa9, 0x71, 0x27, 0xa9, 0x71, 0xda, 0xbd, 0xab, 0x93, 0x9a, 0xbc, 0x0b, 0x49, 0x2f, 0xf4, - 0x9a, 0xe7, 0x8e, 0x6c, 0x6c, 0x53, 0xda, 0xce, 0x94, 0xce, 0x17, 0x06, 0x64, 0x62, 0xab, 0x6e, - 0x15, 0x58, 0x8e, 0x94, 0xb1, 0xed, 0x1c, 0x1c, 0xb5, 0x90, 0x9a, 0x30, 0xc4, 0x93, 0x7c, 0x1d, - 0xe6, 0x58, 0x52, 0x64, 0xe7, 0x36, 0xa5, 0xed, 0x54, 0xe9, 0xf5, 0x61, 0x78, 0x91, 0x39, 0xfb, - 0xde, 0x9f, 0xca, 0x21, 0x9e, 0x93, 0xaa, 0x0d, 0x6c, 0xd4, 0xb9, 0x6e, 0x71, 0xee, 0x24, 0x46, - 0x61, 0xea, 0xad, 0x42, 0x82, 0x76, 0x35, 0xdb, 0x31, 0x51, 0x37, 0x3b, 0xcf, 0x4d, 0xa2, 0xdd, - 0x3d, 0x6f, 0x99, 0xcf, 0xc1, 0x99, 0x41, 0xfe, 0x09, 0x1c, 0xf8, 0xbb, 0x04, 0x27, 0x2b, 0xc4, - 0xfa, 0xb4, 0x66, 0x53, 0xd4, 0xb0, 0x09, 0xbd, 0xa5, 0x96, 0x4b, 0x3b, 0x23, 0xbc, 0x77, 0x0e, - 0xd2, 0xc8, 0x35, 0x4a, 0x3b, 0x9a, 0xce, 0x23, 0x21, 0x22, 0xb6, 0xc0, 0x88, 0x7e, 0xb4, 0x7b, - 0x5d, 0x3c, 0x1b, 0x76, 0xb1, 0x0c, 0x31, 0x47, 0x6f, 0x72, 0x27, 0x26, 0x55, 0xf6, 0x2c, 0xaf, - 0x40, 0x9c, 0x1c, 0x35, 0xab, 0xb8, 0xc1, 0x5c, 0x93, 0x54, 0xc5, 0x4a, 0x56, 0x20, 0x61, 0x22, - 0xc3, 0x6e, 0xea, 0x0d, 0xc2, 0x6c, 0x4e, 0xab, 0xc1, 0x5a, 0x5e, 0x83, 0xa4, 0xa5, 0x13, 0x7e, - 0x6b, 0x84, 0xcd, 0x09, 0x4b, 0x27, 0xf7, 0xbc, 0x75, 0x5e, 0x83, 0xd5, 0x3e, 0x9b, 0x7c, 0x8b, - 0x3d, 0x0b, 0x1e, 0x87, 0x2c, 0xe0, 0x16, 0x2e, 0x3c, 0xee, 0xb5, 0x60, 0x1d, 0xc0, 0x30, 0x02, - 0x9f, 0x8a, 0xac, 0xf4, 0x28, 0xdc, 0xab, 0x7f, 0x4b, 0x70, 0x8a, 0xbb, 0xf5, 0x41, 0x9b, 0xbe, - 0x7a, 0xde, 0x2d, 0xc3, 0x9c, 0x83, 0x1d, 0x03, 0x31, 0x67, 0xc5, 0x54, 0xbe, 0xe8, 0xcd, 0xc6, - 0x58, 0x28, 0x1b, 0xff, 0x9f, 0x4c, 0x7a, 0x0f, 0xd6, 0x07, 0x9a, 0x1c, 0x38, 0x76, 0x1d, 0xc0, - 0x26, 0x9a, 0x8b, 0x9a, 0xb8, 0x83, 0x4c, 0x66, 0x7d, 0x42, 0x4d, 0xda, 0x44, 0xe5, 0x84, 0x3c, - 0x82, 0x6c, 0x85, 0x58, 0x7c, 0xf5, 0xdf, 0x79, 0x2d, 0x9f, 0x87, 0xcd, 0x61, 0xc7, 0x04, 0x49, - 0xff, 0xab, 0x04, 0x8b, 0x15, 0x62, 0x7d, 0x82, 0x29, 0xba, 0xa3, 0x93, 0x7d, 0xd7, 0x36, 0xd0, - 0xd4, 0x2a, 0xb4, 0x3c, 0xb4, 0xaf, 0x02, 0x5b, 0xc8, 0x67, 0x61, 0xa1, 0xe5, 0xda, 0xd8, 0xb5, - 0xe9, 0x91, 0x76, 0x88, 0x10, 0xf3, 0x72, 0x4c, 0x4d, 0xf9, 0xb4, 0xdb, 0x88, 0xb1, 0xf0, 0x30, - 0x38, 0xed, 0x66, 0x15, 0xb9, 0x2c, 0xc0, 0x31, 0x35, 0xc5, 0x68, 0xf7, 0x19, 0xe9, 0xa3, 0x58, - 0x62, 0x6e, 0x29, 0x9e, 0x5f, 0x85, 0xd3, 0x11, 0x4d, 0x03, 0x2b, 0x7e, 0x89, 0x07, 0x56, 0xf8, - 0x86, 0x8e, 0xb0, 0x62, 0x0d, 0x58, 0xfe, 0xf2, 0xb8, 0xf3, 0x84, 0x4e, 0x78, 0x04, 0x16, 0xf6, - 0x2b, 0xb0, 0x82, 0xab, 0x04, 0xb9, 0x1d, 0x64, 0x6a, 0x58, 0xc8, 0xea, 0xad, 0x83, 0xcb, 0xfe, - 0xae, 0x7f, 0x10, 0x43, 0x95, 0x21, 0xd7, 0x8f, 0x12, 0xd9, 0x85, 0x6c, 0xab, 0x46, 0x85, 0x59, - 0x6b, 0x51, 0xf4, 0x2e, 0xcb, 0x37, 0xc6, 0x22, 0xdf, 0x00, 0xa5, 0x5f, 0x88, 0x77, 0xb5, 0xdb, - 0x04, 0x99, 0x59, 0x60, 0x02, 0x4e, 0x47, 0x05, 0xdc, 0xd1, 0xc9, 0x23, 0x82, 0x4c, 0xf9, 0x2b, - 0x09, 0xce, 0xf7, 0xa3, 0xd1, 0xe1, 0x21, 0x32, 0xa8, 0xdd, 0x41, 0x4c, 0x0e, 0x0f, 0x50, 0x8a, - 0x35, 0xbd, 0x82, 0x68, 0x7a, 0x5b, 0x13, 0x34, 0xbd, 0x3d, 0x87, 0xaa, 0x67, 0xa3, 0x07, 0xdf, - 0xf2, 0x45, 0x07, 0x79, 0xb3, 0x3f, 0x5e, 0x03, 0x5e, 0xa4, 0x16, 0x98, 0x29, 0x23, 0x25, 0xb2, - 0xea, 0x25, 0x63, 0xc8, 0x74, 0xf4, 0x46, 0x1b, 0x69, 0x2e, 0x32, 0x90, 0xed, 0xdd, 0x25, 0x56, - 0x16, 0x77, 0xef, 0x1e, 0xb3, 0x63, 0xff, 0xf3, 0x6c, 0xe3, 0xd4, 0x91, 0xde, 0x6c, 0x5c, 0xcf, - 0x87, 0xc5, 0xe5, 0xd5, 0x34, 0x23, 0xa8, 0x62, 0x2d, 0x7f, 0x08, 0x71, 0x42, 0x75, 0xda, 0xe6, - 0x55, 0x36, 0x53, 0xba, 0x30, 0xb4, 0xb5, 0xf1, 0x41, 0x49, 0x00, 0x1f, 0x32, 0x8c, 0x2a, 0xb0, - 0xf2, 0x79, 0xc8, 0x04, 0xf6, 0x33, 0x46, 0x51, 0x40, 0xd2, 0x3e, 0xb5, 0xec, 0x11, 0xe5, 0x0b, - 0x20, 0x07, 0x6c, 0x5e, 0xe3, 0xe7, 0x57, 0x38, 0xc1, 0x9c, 0xb3, 0xe4, 0xef, 0x1c, 0x10, 0x72, - 0x9f, 0xd5, 0xc0, 0x50, 0xe3, 0x4d, 0x4e, 0xd5, 0x78, 0x7b, 0xae, 0x90, 0xef, 0xf3, 0xe0, 0x0a, - 0xfd, 0x14, 0x83, 0x8c, 0xd8, 0x13, 0xfd, 0x71, 0xc4, 0x0d, 0xf2, 0xda, 0x14, 0x72, 0x4c, 0xe4, - 0x8a, 0xeb, 0x23, 0x56, 0xf2, 0x16, 0x2c, 0xf2, 0x27, 0x2d, 0xd2, 0xf4, 0xd2, 0x9c, 0x5c, 0x16, - 0xc5, 0x42, 0x81, 0x84, 0x08, 0x81, 0x2b, 0x0a, 0x7a, 0xb0, 0xf6, 0x9c, 0xe7, 0x3f, 0x0b, 0xe7, - 0xcd, 0x71, 0x11, 0x3e, 0x95, 0x3b, 0xef, 0xe5, 0x10, 0x17, 0x7f, 0xa5, 0x21, 0xce, 0xb3, 0xb2, - 0x89, 0x08, 0xd1, 0x2d, 0xee, 0xfa, 0xa4, 0xea, 0x2f, 0xbd, 0xca, 0x64, 0x3b, 0x3d, 0x05, 0x20, - 0xc9, 0xb6, 0x53, 0x82, 0xc6, 0xee, 0xfd, 0x0e, 0x2c, 0xfb, 0x2c, 0xa1, 0xdb, 0xce, 0x2f, 0xab, - 0x2c, 0xf6, 0x7a, 0x2f, 0x79, 0xa8, 0x5b, 0xa7, 0x18, 0x5b, 0xd0, 0xad, 0xc3, 0x31, 0x5e, 0x98, - 0x6e, 0xb8, 0x5a, 0x83, 0x24, 0xed, 0x6a, 0xd8, 0xb5, 0x2d, 0xdb, 0xc9, 0xa6, 0xb9, 0x73, 0x69, - 0xf7, 0x01, 0x5b, 0x7b, 0x55, 0x5a, 0x27, 0x04, 0xd1, 0x6c, 0x86, 0x6d, 0xf0, 0x85, 0xbc, 0x01, - 0x29, 0xd4, 0x41, 0x0e, 0x15, 0xdd, 0x6e, 0x91, 0x69, 0x05, 0x8c, 0xc4, 0x1b, 0x5e, 0x16, 0x56, - 0xc2, 0xb9, 0x11, 0xa4, 0xcd, 0x3d, 0x36, 0x33, 0xdd, 0xac, 0x62, 0x97, 0x3e, 0xa4, 0x6d, 0xa3, - 0x5e, 0x2e, 0x1f, 0x7c, 0x36, 0x7a, 0xc4, 0x1d, 0x35, 0x4c, 0xac, 0xb1, 0x69, 0x25, 0x2c, 0x2d, - 0x38, 0xaa, 0xc3, 0xe6, 0x5b, 0x15, 0x1d, 0xb6, 0x1d, 0x93, 0xb1, 0x20, 0xf3, 0x95, 0x4e, 0xe3, - 0x99, 0xe6, 0x49, 0x0b, 0xe6, 0x1f, 0x5e, 0xe2, 0xd3, 0x9c, 0x2a, 0x06, 0x20, 0x31, 0x37, 0xf6, - 0x9d, 0xfb, 0xf2, 0xe6, 0x48, 0x4c, 0x6b, 0x3e, 0x98, 0xab, 0x3a, 0x45, 0xf7, 0xf8, 0xfb, 0xcb, - 0x6d, 0xef, 0xf5, 0x65, 0x84, 0x76, 0x06, 0xc8, 0xfd, 0xaf, 0x3b, 0x4c, 0xcb, 0x54, 0xa9, 0x58, - 0x18, 0xf9, 0x72, 0x57, 0x88, 0x1e, 0xb3, 0x1b, 0xf3, 0xd2, 0x5f, 0x5d, 0x72, 0x23, 0xf4, 0xfc, - 0x39, 0x38, 0x3b, 0x54, 0x37, 0xdf, 0x82, 0xd2, 0x6f, 0x0b, 0x30, 0x5b, 0x21, 0x96, 0xfc, 0xad, - 0x04, 0xf2, 0x80, 0x41, 0xee, 0xca, 0x18, 0x65, 0x06, 0xce, 0x42, 0xca, 0xbb, 0xd3, 0xa0, 0x82, - 0x09, 0xea, 0x1b, 0x09, 0x4e, 0xf6, 0xbf, 0xca, 0x5c, 0x9e, 0x48, 0x66, 0x18, 0xa4, 0xdc, 0x98, - 0x02, 0x14, 0xe8, 0xf1, 0xbd, 0x04, 0xa7, 0x06, 0x0f, 0x6a, 0x6f, 0x8f, 0x17, 0x3b, 0x10, 0xa8, - 0xbc, 0x3f, 0x25, 0x30, 0xd0, 0xa9, 0x03, 0x0b, 0xa1, 0x79, 0xad, 0x30, 0x5e, 0x60, 0x2f, 0xbf, - 0x72, 0xf5, 0x78, 0xfc, 0xd1, 0x73, 0x83, 0x09, 0x6b, 0xc2, 0x73, 0x7d, 0xfe, 0x49, 0xcf, 0x8d, - 0xb6, 0x26, 0x99, 0x40, 0xaa, 0xb7, 0x2d, 0x5d, 0x9c, 0x4c, 0x8c, 0x60, 0x57, 0xde, 0x3a, 0x16, - 0x7b, 0x70, 0xe8, 0x17, 0x90, 0x89, 0xbc, 0x09, 0xee, 0x8c, 0x17, 0x14, 0x46, 0x28, 0xd7, 0x8e, - 0x8b, 0x08, 0x4e, 0xff, 0x5a, 0x82, 0xa5, 0xbe, 0x2f, 0x07, 0xa5, 0xf1, 0xe2, 0xa2, 0x18, 0xe5, - 0xfa, 0xf1, 0x31, 0x81, 0x12, 0x5f, 0xc2, 0x62, 0xf4, 0x7b, 0xcb, 0xa5, 0xf1, 0xe2, 0x22, 0x10, - 0xe5, 0x9d, 0x63, 0x43, 0x7a, 0x63, 0x10, 0xe9, 0x2c, 0x13, 0xc4, 0x20, 0x8c, 0x98, 0x24, 0x06, - 0x83, 0xfb, 0x0d, 0x2b, 0x41, 0xfd, 0xdd, 0xe6, 0xf2, 0x24, 0xb7, 0x37, 0x02, 0x9a, 0xa4, 0x04, - 0x0d, 0xed, 0x2f, 0xf2, 0x8f, 0x12, 0xac, 0x0c, 0x69, 0x2e, 0xd7, 0x26, 0x8d, 0x6e, 0x14, 0xa9, - 0x7c, 0x30, 0x2d, 0xd2, 0x57, 0x6b, 0xf7, 0xe3, 0x27, 0xcf, 0x73, 0xd2, 0xd3, 0xe7, 0x39, 0xe9, - 0xaf, 0xe7, 0x39, 0xe9, 0xbb, 0x17, 0xb9, 0x99, 0xa7, 0x2f, 0x72, 0x33, 0x7f, 0xbc, 0xc8, 0xcd, - 0x7c, 0x7e, 0xa9, 0x67, 0x04, 0xf3, 0x64, 0x5f, 0x8c, 0x7c, 0xef, 0xeb, 0x86, 0x3e, 0x67, 0x7a, - 0x13, 0x59, 0x35, 0xce, 0xbe, 0xf2, 0x5d, 0xfe, 0x37, 0x00, 0x00, 0xff, 0xff, 0xda, 0xb5, 0x92, - 0x0a, 0xfc, 0x14, 0x00, 0x00, + // 1638 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x58, 0x5f, 0x6f, 0xdb, 0x54, + 0x14, 0xaf, 0xd7, 0x34, 0x4d, 0x4e, 0x9a, 0xb6, 0xf3, 0xba, 0x2e, 0x75, 0xd7, 0xb4, 0xcb, 0x58, + 0xa9, 0xd0, 0x96, 0x74, 0xd9, 0x18, 0xa3, 0x43, 0xc0, 0x9a, 0xfd, 0x2b, 0x2c, 0x5b, 0xe5, 0x75, + 0x80, 0x78, 0xb1, 0x1c, 0xfb, 0xd6, 0xb5, 0x9a, 0xf8, 0x46, 0xbe, 0x37, 0x5d, 0x32, 0x21, 0x81, + 0x90, 0x90, 0x78, 0x04, 0xc4, 0xd3, 0x1e, 0x78, 0x43, 0xe2, 0xa3, 0xec, 0x8d, 0x89, 0x27, 0xc4, + 0xc3, 0x84, 0xb6, 0x2f, 0x00, 0x7c, 0x02, 0xe4, 0x7b, 0xaf, 0xdd, 0xd8, 0xf9, 0xdb, 0x54, 0x88, + 0x97, 0xc4, 0xf7, 0xf8, 0xfc, 0xce, 0x3d, 0xff, 0xee, 0x39, 0xe7, 0x1a, 0x56, 0x9f, 0x22, 0xaa, + 0x1b, 0x7b, 0xba, 0xed, 0x14, 0xd8, 0x13, 0x76, 0x51, 0xc1, 0x70, 0x31, 0x21, 0x9c, 0x46, 0x9b, + 0xf9, 0xba, 0x8b, 0x29, 0x96, 0x97, 0x02, 0xbe, 0xbc, 0xcf, 0x97, 0x3f, 0xe4, 0x53, 0xe6, 0x2c, + 0x6c, 0x61, 0xc6, 0x59, 0xf0, 0x9e, 0x38, 0x48, 0x79, 0xab, 0x8b, 0xf0, 0xfa, 0xbe, 0x55, 0x60, + 0x24, 0x22, 0xfe, 0x04, 0xef, 0x6a, 0x2f, 0x5e, 0x6c, 0x3b, 0xec, 0x67, 0x80, 0xcc, 0xba, 0x8b, + 0xf1, 0x2e, 0x11, 0x7f, 0x82, 0xf7, 0x5a, 0x7f, 0xe3, 0x5c, 0x9d, 0x22, 0xad, 0x6a, 0xd7, 0x6c, + 0x8a, 0x5c, 0x6d, 0xb7, 0xaa, 0x5b, 0x02, 0x97, 0xfb, 0x41, 0x02, 0xb9, 0x4c, 0xac, 0xb2, 0x6d, + 0x79, 0x2c, 0x3b, 0x84, 0xdc, 0x69, 0x38, 0x26, 0x91, 0x33, 0x30, 0x69, 0xb8, 0x48, 0xa7, 0xd8, + 0xcd, 0x48, 0x2b, 0xd2, 0x5a, 0x52, 0xf5, 0x97, 0xf2, 0x02, 0x24, 0x98, 0x48, 0xcd, 0x36, 0x33, + 0x27, 0x56, 0xa4, 0xb5, 0x71, 0x75, 0x92, 0xad, 0xb7, 0x4c, 0xf9, 0x2e, 0xc4, 0xf5, 0x1a, 0x6e, + 0x38, 0x34, 0x33, 0xee, 0x61, 0x36, 0x0b, 0xcf, 0x5f, 0x2e, 0x8f, 0xfd, 0xf1, 0x72, 0xf9, 0x4d, + 0xcb, 0xa6, 0x7b, 0x8d, 0x4a, 0xde, 0xc0, 0xb5, 0x82, 0x81, 0x49, 0x0d, 0x13, 0xf1, 0x77, 0x89, + 0x98, 0xfb, 0x05, 0xda, 0xaa, 0x23, 0x92, 0x7f, 0x6c, 0x3b, 0x54, 0x15, 0xf0, 0xdc, 0x59, 0x50, + 0x3a, 0x75, 0x52, 0x11, 0xa9, 0x63, 0x87, 0xa0, 0xdc, 0x03, 0x38, 0x55, 0x26, 0xd6, 0xe3, 0xba, + 0xc9, 0x5f, 0xde, 0x34, 0x4d, 0x17, 0x91, 0x7e, 0x2a, 0x2f, 0x01, 0x50, 0x42, 0xb4, 0x7a, 0xa3, + 0xb2, 0x8f, 0x5a, 0x4c, 0xe9, 0xa4, 0x9a, 0xa4, 0x84, 0x6c, 0x33, 0x42, 0x6e, 0x09, 0x16, 0xbb, + 0xc8, 0x0b, 0xb6, 0xfb, 0xe9, 0x04, 0xcc, 0x95, 0x89, 0x75, 0xd3, 0x34, 0xb7, 0x9c, 0x0a, 0x6e, + 0x38, 0xe6, 0x8e, 0xab, 0x1b, 0xfb, 0xc8, 0x1d, 0xcd, 0x47, 0x67, 0x60, 0x92, 0x36, 0xb5, 0x3d, + 0x9d, 0xec, 0x71, 0x27, 0xa9, 0x71, 0xda, 0xbc, 0xa7, 0x93, 0x3d, 0x79, 0x13, 0x92, 0x5e, 0xe8, + 0x35, 0xcf, 0x1d, 0x99, 0xd8, 0x8a, 0xb4, 0x36, 0x5d, 0xbc, 0x90, 0xef, 0x92, 0x89, 0xf5, 0x7d, + 0x2b, 0xcf, 0x72, 0xa4, 0x84, 0x6d, 0x67, 0xa7, 0x55, 0x47, 0x6a, 0xc2, 0x10, 0x4f, 0xf2, 0x06, + 0x4c, 0xb0, 0xa4, 0xc8, 0x4c, 0xac, 0x48, 0x6b, 0xa9, 0xe2, 0x1b, 0xbd, 0xf0, 0x22, 0x73, 0xb6, + 0xbd, 0x3f, 0x95, 0x43, 0x3c, 0x27, 0x55, 0xaa, 0xd8, 0xd8, 0xe7, 0xba, 0xc5, 0xb9, 0x93, 0x18, + 0x85, 0xa9, 0xb7, 0x00, 0x09, 0xda, 0xd4, 0x6c, 0xc7, 0x44, 0xcd, 0xcc, 0x24, 0x37, 0x89, 0x36, + 0xb7, 0xbc, 0x65, 0x2e, 0x0b, 0x67, 0xbb, 0xf9, 0x27, 0x70, 0xe0, 0x6f, 0x12, 0x9c, 0x2c, 0x13, + 0xeb, 0xd3, 0x3d, 0x9b, 0xa2, 0xaa, 0x4d, 0xe8, 0x6d, 0xb5, 0x54, 0x5c, 0xef, 0xe3, 0xbd, 0xf3, + 0x90, 0x46, 0xae, 0x51, 0x5c, 0xd7, 0x74, 0x1e, 0x09, 0x11, 0xb1, 0x29, 0x46, 0xf4, 0xa3, 0xdd, + 0xee, 0xe2, 0xf1, 0xb0, 0x8b, 0x65, 0x88, 0x39, 0x7a, 0x8d, 0x3b, 0x31, 0xa9, 0xb2, 0x67, 0x79, + 0x1e, 0xe2, 0xa4, 0x55, 0xab, 0xe0, 0x2a, 0x73, 0x4d, 0x52, 0x15, 0x2b, 0x59, 0x81, 0x84, 0x89, + 0x0c, 0xbb, 0xa6, 0x57, 0x09, 0xb3, 0x39, 0xad, 0x06, 0x6b, 0x79, 0x11, 0x92, 0x96, 0x4e, 0xf8, + 0xa9, 0x11, 0x36, 0x27, 0x2c, 0x9d, 0xdc, 0xf7, 0xd6, 0x39, 0x0d, 0x16, 0x3a, 0x6c, 0xf2, 0x2d, + 0xf6, 0x2c, 0x78, 0x1a, 0xb2, 0x80, 0x5b, 0x38, 0xf5, 0xb4, 0xdd, 0x82, 0x25, 0x00, 0xc3, 0x08, + 0x7c, 0x2a, 0xb2, 0xd2, 0xa3, 0x70, 0xaf, 0xfe, 0x2d, 0xc1, 0x69, 0xee, 0xd6, 0x87, 0x0d, 0x7a, + 0xfc, 0xbc, 0x9b, 0x83, 0x09, 0x07, 0x3b, 0x06, 0x62, 0xce, 0x8a, 0xa9, 0x7c, 0xd1, 0x9e, 0x8d, + 0xb1, 0x50, 0x36, 0xfe, 0x3f, 0x99, 0xf4, 0x3e, 0x2c, 0x75, 0x35, 0x39, 0x70, 0xec, 0x12, 0x80, + 0x4d, 0x34, 0x17, 0xd5, 0xf0, 0x01, 0x32, 0x99, 0xf5, 0x09, 0x35, 0x69, 0x13, 0x95, 0x13, 0x72, + 0x08, 0x32, 0x65, 0x62, 0xf1, 0xd5, 0x7f, 0xe7, 0xb5, 0x5c, 0x0e, 0x56, 0x7a, 0x6d, 0x13, 0x24, + 0xfd, 0x2f, 0x12, 0xcc, 0x94, 0x89, 0xf5, 0x09, 0xa6, 0xe8, 0xae, 0x4e, 0xb6, 0x5d, 0xdb, 0x40, + 0x23, 0xab, 0x50, 0xf7, 0xd0, 0xbe, 0x0a, 0x6c, 0x21, 0x9f, 0x83, 0xa9, 0xba, 0x6b, 0x63, 0xd7, + 0xa6, 0x2d, 0x6d, 0x17, 0x21, 0xe6, 0xe5, 0x98, 0x9a, 0xf2, 0x69, 0x77, 0x10, 0x63, 0xe1, 0x61, + 0x70, 0x1a, 0xb5, 0x0a, 0x72, 0x59, 0x80, 0x63, 0x6a, 0x8a, 0xd1, 0x1e, 0x30, 0xd2, 0x47, 0xb1, + 0xc4, 0xc4, 0x6c, 0x3c, 0xb7, 0x00, 0x67, 0x22, 0x9a, 0x06, 0x56, 0xfc, 0x1c, 0x0f, 0xac, 0xf0, + 0x0d, 0xed, 0x63, 0xc5, 0x22, 0xb0, 0xfc, 0xe5, 0x71, 0xe7, 0x09, 0x9d, 0xf0, 0x08, 0x2c, 0xec, + 0x57, 0x61, 0x1e, 0x57, 0x08, 0x72, 0x0f, 0x90, 0xa9, 0x61, 0x21, 0xab, 0xbd, 0x0e, 0xce, 0xf9, + 0x6f, 0xfd, 0x8d, 0x18, 0xaa, 0x04, 0xd9, 0x4e, 0x94, 0xc8, 0x2e, 0x64, 0x5b, 0x7b, 0x54, 0x98, + 0xb5, 0x18, 0x45, 0x6f, 0xb2, 0x7c, 0x63, 0x2c, 0xf2, 0x0d, 0x50, 0x3a, 0x85, 0x78, 0x47, 0xbb, + 0x41, 0x90, 0x99, 0x01, 0x26, 0xe0, 0x4c, 0x54, 0xc0, 0x5d, 0x9d, 0x3c, 0x26, 0xc8, 0x94, 0xbf, + 0x92, 0xe0, 0x42, 0x27, 0x1a, 0xed, 0xee, 0x22, 0x83, 0xda, 0x07, 0x88, 0xc9, 0xe1, 0x01, 0x4a, + 0xb1, 0xa6, 0x97, 0x17, 0x4d, 0x6f, 0x75, 0x88, 0xa6, 0xb7, 0xe5, 0x50, 0xf5, 0x5c, 0x74, 0xe3, + 0xdb, 0xbe, 0xe8, 0x20, 0x6f, 0xb6, 0x07, 0x6b, 0xc0, 0x8b, 0xd4, 0x14, 0x33, 0xa5, 0xaf, 0x44, + 0x56, 0xbd, 0x64, 0x0c, 0xd3, 0x07, 0x7a, 0xb5, 0x81, 0x34, 0x17, 0x19, 0xc8, 0xf6, 0xce, 0x12, + 0x2b, 0x8b, 0x9b, 0xf7, 0x8e, 0xd8, 0xb1, 0xff, 0x79, 0xb9, 0x7c, 0xba, 0xa5, 0xd7, 0xaa, 0x1b, + 0xb9, 0xb0, 0xb8, 0x9c, 0x9a, 0x66, 0x04, 0x55, 0xac, 0xe5, 0x5b, 0x10, 0x27, 0x54, 0xa7, 0x0d, + 0x5e, 0x65, 0xa7, 0x8b, 0x17, 0x7b, 0xb6, 0x36, 0x3e, 0x28, 0x09, 0xe0, 0x23, 0x86, 0x51, 0x05, + 0x56, 0xbe, 0x00, 0xd3, 0x81, 0xfd, 0x8c, 0x51, 0x14, 0x90, 0xb4, 0x4f, 0x2d, 0x79, 0x44, 0xf9, + 0x22, 0xc8, 0x01, 0x9b, 0xd7, 0xf8, 0xf9, 0x11, 0x4e, 0x30, 0xe7, 0xcc, 0xfa, 0x6f, 0x76, 0x08, + 0x79, 0xc0, 0x6a, 0x60, 0xa8, 0xf1, 0x26, 0x47, 0x6a, 0xbc, 0x6d, 0x47, 0xc8, 0xf7, 0x79, 0x70, + 0x84, 0x9e, 0xc5, 0x60, 0x5a, 0xbc, 0x13, 0xfd, 0xb1, 0xcf, 0x09, 0xf2, 0xda, 0x14, 0x72, 0x4c, + 0xe4, 0x8a, 0xe3, 0x23, 0x56, 0xf2, 0x2a, 0xcc, 0xf0, 0x27, 0x2d, 0xd2, 0xf4, 0xd2, 0x9c, 0x5c, + 0x12, 0xc5, 0x42, 0x81, 0x84, 0x08, 0x81, 0x2b, 0x0a, 0x7a, 0xb0, 0xf6, 0x9c, 0xe7, 0x3f, 0x0b, + 0xe7, 0x4d, 0x70, 0x11, 0x3e, 0x95, 0x3b, 0xef, 0x70, 0x88, 0x8b, 0x1f, 0x6b, 0x88, 0xf3, 0xac, + 0xac, 0x21, 0x42, 0x74, 0x8b, 0xbb, 0x3e, 0xa9, 0xfa, 0x4b, 0xaf, 0x32, 0xd9, 0x4e, 0x5b, 0x01, + 0x48, 0xb2, 0xd7, 0x29, 0x41, 0x63, 0xe7, 0x7e, 0x1d, 0xe6, 0x7c, 0x96, 0xd0, 0x69, 0xe7, 0x87, + 0x55, 0x16, 0xef, 0xda, 0x0f, 0x79, 0xa8, 0x5b, 0xa7, 0x18, 0x5b, 0xd0, 0xad, 0xc3, 0x31, 0x9e, + 0x1a, 0x6d, 0xb8, 0x5a, 0x84, 0x24, 0x6d, 0x6a, 0xd8, 0xb5, 0x2d, 0xdb, 0xc9, 0xa4, 0xb9, 0x73, + 0x69, 0xf3, 0x21, 0x5b, 0x7b, 0x55, 0x5a, 0x27, 0x04, 0xd1, 0xcc, 0x34, 0x7b, 0xc1, 0x17, 0xf2, + 0x32, 0xa4, 0xd0, 0x01, 0x72, 0xa8, 0xe8, 0x76, 0x33, 0x4c, 0x2b, 0x60, 0x24, 0xde, 0xf0, 0x32, + 0x30, 0x1f, 0xce, 0x8d, 0x20, 0x6d, 0xee, 0xb3, 0x99, 0xe9, 0x66, 0x05, 0xbb, 0xf4, 0x11, 0x6d, + 0x18, 0xfb, 0xa5, 0xd2, 0xce, 0x67, 0xfd, 0x47, 0xdc, 0x7e, 0xc3, 0xc4, 0x22, 0x9b, 0x56, 0xc2, + 0xd2, 0x82, 0xad, 0x0e, 0xd8, 0x7c, 0xab, 0xa2, 0xdd, 0x86, 0x63, 0x32, 0x16, 0x64, 0x1e, 0x6b, + 0x37, 0x9e, 0x69, 0x9e, 0xb4, 0x60, 0xfe, 0xe1, 0x25, 0x3e, 0xcd, 0xa9, 0x62, 0x00, 0x12, 0x73, + 0x63, 0xc7, 0xbe, 0x87, 0x27, 0x47, 0x62, 0x5a, 0xf3, 0xc1, 0x5c, 0xd5, 0x29, 0xba, 0xcf, 0xef, + 0x2f, 0x77, 0xbc, 0xeb, 0x4b, 0x1f, 0xed, 0x0c, 0x90, 0x3b, 0xaf, 0x3b, 0x4c, 0xcb, 0x54, 0xb1, + 0x90, 0xef, 0x7b, 0xb9, 0xcb, 0x47, 0xb7, 0xd9, 0x8c, 0x79, 0xe9, 0xaf, 0xce, 0xba, 0x11, 0x7a, + 0xee, 0x3c, 0x9c, 0xeb, 0xa9, 0x5b, 0x60, 0xc1, 0x5f, 0x12, 0xbb, 0x5a, 0x88, 0x8b, 0x0c, 0x9b, + 0x11, 0x4b, 0x0d, 0x42, 0xb1, 0xd9, 0x3a, 0xc6, 0x2d, 0x2b, 0x0f, 0xa7, 0x1c, 0xf4, 0x44, 0x33, + 0xb8, 0xa0, 0x88, 0x8b, 0x4f, 0x3a, 0xe8, 0x89, 0xd8, 0xc2, 0x9f, 0x33, 0x3b, 0xc6, 0xe9, 0x58, + 0x97, 0x71, 0xfa, 0xf0, 0xd4, 0x4f, 0x1c, 0xef, 0xea, 0x76, 0x0b, 0xce, 0xf7, 0xb1, 0xb8, 0x7d, + 0x90, 0x6b, 0xcb, 0x20, 0x29, 0x92, 0x41, 0xc5, 0x5f, 0xd3, 0x30, 0x5e, 0x26, 0x96, 0xfc, 0xad, + 0x04, 0x72, 0x97, 0x09, 0xf8, 0xea, 0x80, 0x28, 0x76, 0x1d, 0x22, 0x95, 0xf7, 0x46, 0x41, 0x05, + 0x1a, 0x7f, 0x23, 0xc1, 0xc9, 0xce, 0x3b, 0xe0, 0x95, 0xa1, 0x64, 0x86, 0x41, 0xca, 0x8d, 0x11, + 0x40, 0x81, 0x1e, 0xdf, 0x4b, 0x70, 0xba, 0xfb, 0x84, 0xfb, 0xce, 0x60, 0xb1, 0x5d, 0x81, 0xca, + 0x07, 0x23, 0x02, 0x03, 0x9d, 0x0e, 0x60, 0x2a, 0x34, 0xe8, 0xe6, 0x07, 0x0b, 0x6c, 0xe7, 0x57, + 0xae, 0x1d, 0x8d, 0x3f, 0xba, 0x6f, 0x30, 0x9a, 0x0e, 0xb9, 0xaf, 0xcf, 0x3f, 0xec, 0xbe, 0xd1, + 0x9e, 0x2e, 0x13, 0x48, 0xb5, 0xf7, 0xf3, 0x4b, 0xc3, 0x89, 0x11, 0xec, 0xca, 0xdb, 0x47, 0x62, + 0x0f, 0x36, 0xfd, 0x02, 0xa6, 0x23, 0x57, 0xe8, 0xf5, 0xc1, 0x82, 0xc2, 0x08, 0xe5, 0xfa, 0x51, + 0x11, 0xc1, 0xee, 0x5f, 0x4b, 0x30, 0xdb, 0xf1, 0xc9, 0xa5, 0x38, 0x58, 0x5c, 0x14, 0xa3, 0x6c, + 0x1c, 0x1d, 0x13, 0x28, 0xf1, 0x25, 0xcc, 0x44, 0x3f, 0x54, 0x5d, 0x1e, 0x2c, 0x2e, 0x02, 0x51, + 0xde, 0x3d, 0x32, 0xa4, 0x3d, 0x06, 0x91, 0x96, 0x3c, 0x44, 0x0c, 0xc2, 0x88, 0x61, 0x62, 0xd0, + 0xbd, 0x51, 0xb3, 0x12, 0xd4, 0xd9, 0xa6, 0xaf, 0x0c, 0x73, 0x7a, 0x23, 0xa0, 0x61, 0x4a, 0x50, + 0xcf, 0xc6, 0x2c, 0xff, 0x28, 0xc1, 0x7c, 0x8f, 0xae, 0x7c, 0x7d, 0xd8, 0xe8, 0x46, 0x91, 0xca, + 0x87, 0xa3, 0x22, 0x03, 0xb5, 0x9e, 0x49, 0x90, 0xe9, 0xd9, 0x6a, 0x37, 0x86, 0x0e, 0x7a, 0x07, + 0x56, 0xd9, 0x1c, 0x1d, 0xeb, 0x2b, 0xb7, 0xf9, 0xf1, 0xf3, 0x57, 0x59, 0xe9, 0xc5, 0xab, 0xac, + 0xf4, 0xe7, 0xab, 0xac, 0xf4, 0xdd, 0xeb, 0xec, 0xd8, 0x8b, 0xd7, 0xd9, 0xb1, 0xdf, 0x5f, 0x67, + 0xc7, 0x3e, 0xbf, 0xdc, 0xd6, 0x62, 0x3d, 0xe9, 0x97, 0x22, 0x5f, 0x71, 0x9b, 0xa1, 0x8f, 0xd4, + 0x5e, 0xc7, 0xad, 0xc4, 0xd9, 0xb7, 0xdb, 0x2b, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x89, 0xad, + 0xbd, 0xf8, 0xd2, 0x16, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1588,6 +1708,7 @@ type MsgClient interface { AbortStuckCCTX(ctx context.Context, in *MsgAbortStuckCCTX, opts ...grpc.CallOption) (*MsgAbortStuckCCTXResponse, error) RefundAbortedCCTX(ctx context.Context, in *MsgRefundAbortedCCTX, opts ...grpc.CallOption) (*MsgRefundAbortedCCTXResponse, error) UpdateRateLimiterFlags(ctx context.Context, in *MsgUpdateRateLimiterFlags, opts ...grpc.CallOption) (*MsgUpdateRateLimiterFlagsResponse, error) + MigrateERC20CustodyFunds(ctx context.Context, in *MsgMigrateERC20CustodyFunds, opts ...grpc.CallOption) (*MsgMigrateERC20CustodyFundsResponse, error) } type msgClient struct { @@ -1706,6 +1827,15 @@ func (c *msgClient) UpdateRateLimiterFlags(ctx context.Context, in *MsgUpdateRat return out, nil } +func (c *msgClient) MigrateERC20CustodyFunds(ctx context.Context, in *MsgMigrateERC20CustodyFunds, opts ...grpc.CallOption) (*MsgMigrateERC20CustodyFundsResponse, error) { + out := new(MsgMigrateERC20CustodyFundsResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Msg/MigrateERC20CustodyFunds", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { AddOutboundTracker(context.Context, *MsgAddOutboundTracker) (*MsgAddOutboundTrackerResponse, error) @@ -1720,6 +1850,7 @@ type MsgServer interface { AbortStuckCCTX(context.Context, *MsgAbortStuckCCTX) (*MsgAbortStuckCCTXResponse, error) RefundAbortedCCTX(context.Context, *MsgRefundAbortedCCTX) (*MsgRefundAbortedCCTXResponse, error) UpdateRateLimiterFlags(context.Context, *MsgUpdateRateLimiterFlags) (*MsgUpdateRateLimiterFlagsResponse, error) + MigrateERC20CustodyFunds(context.Context, *MsgMigrateERC20CustodyFunds) (*MsgMigrateERC20CustodyFundsResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -1762,6 +1893,9 @@ func (*UnimplementedMsgServer) RefundAbortedCCTX(ctx context.Context, req *MsgRe func (*UnimplementedMsgServer) UpdateRateLimiterFlags(ctx context.Context, req *MsgUpdateRateLimiterFlags) (*MsgUpdateRateLimiterFlagsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateRateLimiterFlags not implemented") } +func (*UnimplementedMsgServer) MigrateERC20CustodyFunds(ctx context.Context, req *MsgMigrateERC20CustodyFunds) (*MsgMigrateERC20CustodyFundsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MigrateERC20CustodyFunds not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -1983,6 +2117,24 @@ func _Msg_UpdateRateLimiterFlags_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _Msg_MigrateERC20CustodyFunds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgMigrateERC20CustodyFunds) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).MigrateERC20CustodyFunds(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.crosschain.Msg/MigrateERC20CustodyFunds", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).MigrateERC20CustodyFunds(ctx, req.(*MsgMigrateERC20CustodyFunds)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "zetachain.zetacore.crosschain.Msg", HandlerType: (*MsgServer)(nil), @@ -2035,6 +2187,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "UpdateRateLimiterFlags", Handler: _Msg_UpdateRateLimiterFlags_Handler, }, + { + MethodName: "MigrateERC20CustodyFunds", + Handler: _Msg_MigrateERC20CustodyFunds_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "zetachain/zetacore/crosschain/tx.proto", @@ -3052,6 +3208,95 @@ func (m *MsgUpdateRateLimiterFlagsResponse) MarshalToSizedBuffer(dAtA []byte) (i return len(dAtA) - i, nil } +func (m *MsgMigrateERC20CustodyFunds) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMigrateERC20CustodyFunds) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMigrateERC20CustodyFunds) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.Amount.Size() + i -= size + if _, err := m.Amount.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + if len(m.Erc20Address) > 0 { + i -= len(m.Erc20Address) + copy(dAtA[i:], m.Erc20Address) + i = encodeVarintTx(dAtA, i, uint64(len(m.Erc20Address))) + i-- + dAtA[i] = 0x22 + } + if len(m.NewCustodyAddress) > 0 { + i -= len(m.NewCustodyAddress) + copy(dAtA[i:], m.NewCustodyAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.NewCustodyAddress))) + i-- + dAtA[i] = 0x1a + } + if m.ChainId != 0 { + i = encodeVarintTx(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x10 + } + if len(m.Creator) > 0 { + i -= len(m.Creator) + copy(dAtA[i:], m.Creator) + i = encodeVarintTx(dAtA, i, uint64(len(m.Creator))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgMigrateERC20CustodyFundsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgMigrateERC20CustodyFundsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgMigrateERC20CustodyFundsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CctxIndex) > 0 { + i -= len(m.CctxIndex) + copy(dAtA[i:], m.CctxIndex) + i = encodeVarintTx(dAtA, i, uint64(len(m.CctxIndex))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -3519,6 +3764,45 @@ func (m *MsgUpdateRateLimiterFlagsResponse) Size() (n int) { return n } +func (m *MsgMigrateERC20CustodyFunds) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Creator) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if m.ChainId != 0 { + n += 1 + sovTx(uint64(m.ChainId)) + } + l = len(m.NewCustodyAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = len(m.Erc20Address) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Amount.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgMigrateERC20CustodyFundsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CctxIndex) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -6593,6 +6877,287 @@ func (m *MsgUpdateRateLimiterFlagsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgMigrateERC20CustodyFunds) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMigrateERC20CustodyFunds: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMigrateERC20CustodyFunds: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Creator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NewCustodyAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.NewCustodyAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Erc20Address", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Erc20Address = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgMigrateERC20CustodyFundsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgMigrateERC20CustodyFundsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgMigrateERC20CustodyFundsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CctxIndex", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CctxIndex = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/zetaclient/chains/evm/signer/signer.go b/zetaclient/chains/evm/signer/signer.go index 0a89b5a657..874c118e3e 100644 --- a/zetaclient/chains/evm/signer/signer.go +++ b/zetaclient/chains/evm/signer/signer.go @@ -20,11 +20,9 @@ import ( ethrpc "github.com/ethereum/go-ethereum/rpc" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" - "github.com/zeta-chain/zetacore/pkg/constant" "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" "github.com/zeta-chain/zetacore/zetaclient/chains/evm" @@ -367,25 +365,6 @@ func (signer *Signer) SignWithdrawTx(ctx context.Context, txData *OutboundData) return tx, nil } -// SignCommandTx signs a transaction based on the given command includes: -// -// cmd_whitelist_erc20 -// cmd_migrate_tss_funds -func (signer *Signer) SignCommandTx( - ctx context.Context, - txData *OutboundData, - cmd string, - params string, -) (*ethtypes.Transaction, error) { - switch cmd { - case constant.CmdWhitelistERC20: - return signer.SignWhitelistERC20Cmd(ctx, txData, params) - case constant.CmdMigrateTssFunds: - return signer.SignMigrateTssFundsCmd(ctx, txData) - } - return nil, fmt.Errorf("SignCommandTx: unknown command %s", cmd) -} - // TryProcessOutbound - signer interface implementation // This function will attempt to build and sign an evm transaction using the TSS signer. // It will then broadcast the signed transaction to the outbound chain. @@ -488,7 +467,7 @@ func (signer *Signer) TryProcessOutbound( // params field is used to pass input parameters for command requests, currently it is used to pass the ERC20 // contract address when a whitelist command is requested params := msg[1] - tx, err = signer.SignCommandTx(ctx, txData, cmd, params) + tx, err = signer.SignAdminTx(ctx, txData, cmd, params) if err != nil { logger.Warn().Err(err).Msg(ErrorMsg(cctx)) return @@ -726,63 +705,6 @@ func ErrorMsg(cctx *types.CrossChainTx) string { ) } -// SignWhitelistERC20Cmd signs a whitelist command for ERC20 token -// TODO(revamp): move the cmd in a specific file -func (signer *Signer) SignWhitelistERC20Cmd( - ctx context.Context, - txData *OutboundData, - params string, -) (*ethtypes.Transaction, error) { - outboundParams := txData.outboundParams - erc20 := ethcommon.HexToAddress(params) - if erc20 == (ethcommon.Address{}) { - return nil, fmt.Errorf("SignCommandTx: invalid erc20 address %s", params) - } - - custodyAbi, err := erc20custody.ERC20CustodyMetaData.GetAbi() - if err != nil { - return nil, err - } - - data, err := custodyAbi.Pack("whitelist", erc20) - if err != nil { - return nil, fmt.Errorf("whitelist pack error: %w", err) - } - - tx, _, _, err := signer.Sign( - ctx, - data, - txData.to, - zeroValue, - txData.gas, - outboundParams.TssNonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("sign whitelist error: %w", err) - } - - return tx, nil -} - -// SignMigrateTssFundsCmd signs a migrate TSS funds command -// TODO(revamp): move the cmd in a specific file -func (signer *Signer) SignMigrateTssFundsCmd(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { - tx, _, _, err := signer.Sign( - ctx, - nil, - txData.to, - txData.amount, - txData.gas, - txData.nonce, - txData.height, - ) - if err != nil { - return nil, fmt.Errorf("SignMigrateTssFundsCmd error: %w", err) - } - return tx, nil -} - // getEVMRPC is a helper function to set up the client and signer, also initializes a mock client for unit tests func getEVMRPC(ctx context.Context, endpoint string) (interfaces.EVMRPCClient, ethtypes.Signer, error) { if endpoint == mocks.EVMRPCEnabled { diff --git a/zetaclient/chains/evm/signer/signer_admin.go b/zetaclient/chains/evm/signer/signer_admin.go new file mode 100644 index 0000000000..b52e6a769b --- /dev/null +++ b/zetaclient/chains/evm/signer/signer_admin.go @@ -0,0 +1,128 @@ +package signer + +import ( + "context" + "fmt" + "math/big" + "strings" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/zeta-chain/protocol-contracts/pkg/contracts/evm/erc20custody.sol" + + "github.com/zeta-chain/zetacore/pkg/constant" +) + +// SignAdminTx signs a transaction based on the given command includes: +// +// cmd_whitelist_erc20 +// cmd_migrate_erc20_custody_funds +// cmd_migrate_tss_funds +func (signer *Signer) SignAdminTx( + ctx context.Context, + txData *OutboundData, + cmd string, + params string, +) (*ethtypes.Transaction, error) { + switch cmd { + case constant.CmdWhitelistERC20: + return signer.signWhitelistERC20Cmd(ctx, txData, params) + case constant.CmdMigrateERC20CustodyFunds: + return signer.signMigrateERC20CustodyFundsCmd(ctx, txData, params) + case constant.CmdMigrateTssFunds: + return signer.signMigrateTssFundsCmd(ctx, txData) + } + return nil, fmt.Errorf("SignAdminTx: unknown command %s", cmd) +} + +// signWhitelistERC20Cmd signs a whitelist command for ERC20 token +func (signer *Signer) signWhitelistERC20Cmd( + ctx context.Context, + txData *OutboundData, + params string, +) (*ethtypes.Transaction, error) { + erc20 := ethcommon.HexToAddress(params) + if erc20 == (ethcommon.Address{}) { + return nil, fmt.Errorf("SignAdminTx: invalid erc20 address %s", params) + } + custodyAbi, err := erc20custody.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, err + } + data, err := custodyAbi.Pack("whitelist", erc20) + if err != nil { + return nil, fmt.Errorf("whitelist pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + txData.to, + zeroValue, + txData.gas, + txData.outboundParams.TssNonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("sign whitelist error: %w", err) + } + return tx, nil +} + +// signMigrateERC20CustodyFundsCmd signs a migrate ERC20 custody funds command +func (signer *Signer) signMigrateERC20CustodyFundsCmd( + ctx context.Context, + txData *OutboundData, + params string, +) (*ethtypes.Transaction, error) { + paramsArray := strings.Split(params, ",") + if len(paramsArray) != 3 { + return nil, fmt.Errorf("signMigrateERC20CustodyFundsCmd: invalid params %s", params) + } + newCustody := ethcommon.HexToAddress(paramsArray[0]) + erc20 := ethcommon.HexToAddress(paramsArray[1]) + amount, ok := new(big.Int).SetString(paramsArray[2], 10) + if !ok { + return nil, fmt.Errorf("signMigrateERC20CustodyFundsCmd: invalid amount %s", paramsArray[2]) + } + + custodyAbi, err := erc20custody.ERC20CustodyMetaData.GetAbi() + if err != nil { + return nil, err + } + data, err := custodyAbi.Pack("withdraw", newCustody, erc20, amount) + if err != nil { + return nil, fmt.Errorf("withdraw pack error: %w", err) + } + + tx, _, _, err := signer.Sign( + ctx, + data, + txData.to, + zeroValue, + txData.gas, + txData.outboundParams.TssNonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("signMigrateERC20CustodyFundsCmd error: %w", err) + } + return tx, nil +} + +// signMigrateTssFundsCmd signs a migrate TSS funds command +func (signer *Signer) signMigrateTssFundsCmd(ctx context.Context, txData *OutboundData) (*ethtypes.Transaction, error) { + tx, _, _, err := signer.Sign( + ctx, + nil, + txData.to, + txData.amount, + txData.gas, + txData.nonce, + txData.height, + ) + if err != nil { + return nil, fmt.Errorf("signMigrateTssFundsCmd error: %w", err) + } + return tx, nil +} diff --git a/zetaclient/chains/evm/signer/signer_admin_test.go b/zetaclient/chains/evm/signer/signer_admin_test.go new file mode 100644 index 0000000000..820b521892 --- /dev/null +++ b/zetaclient/chains/evm/signer/signer_admin_test.go @@ -0,0 +1,231 @@ +package signer + +import ( + "fmt" + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + "github.com/zeta-chain/zetacore/pkg/constant" + "github.com/zeta-chain/zetacore/testutil/sample" + "github.com/zeta-chain/zetacore/zetaclient/testutils/mocks" + "math/big" + "testing" +) + +func TestSigner_SignAdminTx(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + evmSigner, err := getNewEvmSigner(nil) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("SignAdminTx CmdWhitelistERC20", func(t *testing.T) { + cmd := constant.CmdWhitelistERC20 + params := ConnectorAddress.Hex() + // Call SignAdminTx + tx, err := evmSigner.SignAdminTx(ctx, txData, cmd, params) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Revert tx calls erc20 custody contract with 0 gas token + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, big.NewInt(0)) + }) + + t.Run("SignAdminTx CmdMigrateERC20CustodyFunds", func(t *testing.T) { + cmd := constant.CmdMigrateERC20CustodyFunds + params := fmt.Sprintf( + "%s,%s,%s", + sample.EthAddress().Hex(), + sample.EthAddress().Hex(), + big.NewInt(100).String(), + ) + // Call SignAdminTx + tx, err := evmSigner.SignAdminTx(ctx, txData, cmd, params) + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + // Note: Revert tx calls erc20 custody contract with 0 gas token + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, big.NewInt(0)) + }) + + t.Run("SignAdminTx CmdMigrateTssFunds", func(t *testing.T) { + cmd := constant.CmdMigrateTssFunds + // Call SignAdminTx + tx, err := evmSigner.SignAdminTx(ctx, txData, cmd, "") + require.NoError(t, err) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) + }) +} + +func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + + require.NoError(t, err) + + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.NoError(t, err) + require.False(t, skip) + + t.Run("signWhitelistERC20Cmd - should successfully sign", func(t *testing.T) { + // Call signWhitelistERC20Cmd + tx, err := evmSigner.signWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) + require.NoError(t, err) + require.NotNil(t, tx) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) + }) + + t.Run("signWhitelistERC20Cmd - should fail on invalid erc20 address", func(t *testing.T) { + tx, err := evmSigner.signWhitelistERC20Cmd(ctx, txData, "") + require.Nil(t, tx) + require.ErrorContains(t, err, "invalid erc20 address") + }) + + t.Run("signWhitelistERC20Cmd - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call signWhitelistERC20Cmd + tx, err := evmSigner.signWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) + require.ErrorContains(t, err, "sign whitelist error") + require.Nil(t, tx) + }) +} + +func TestSigner_SignMigrateERC20CustodyFundsCmd(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + + require.NoError(t, err) + + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.NoError(t, err) + require.False(t, skip) + + t.Run("signMigrateERC20CustodyFundsCmd - should successfully sign", func(t *testing.T) { + // Call signWhitelistERC20Cmd + + params := fmt.Sprintf( + "%s,%s,%s", + sample.EthAddress().Hex(), + sample.EthAddress().Hex(), + big.NewInt(100).String(), + ) + + tx, err := evmSigner.signMigrateERC20CustodyFundsCmd(ctx, txData, params) + require.NoError(t, err) + require.NotNil(t, tx) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) + }) + + t.Run("signMigrateERC20CustodyFundsCmd - should fail on invalid params", func(t *testing.T) { + + params := fmt.Sprintf("%s,%s", sample.EthAddress().Hex(), sample.EthAddress().Hex()) + + _, err := evmSigner.signMigrateERC20CustodyFundsCmd(ctx, txData, params) + require.ErrorContains(t, err, "invalid params") + }) + + t.Run("signMigrateERC20CustodyFundsCmd - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + params := fmt.Sprintf( + "%s,%s,%s", + sample.EthAddress().Hex(), + sample.EthAddress().Hex(), + big.NewInt(100).String(), + ) + + // Call signWhitelistERC20Cmd + tx, err := evmSigner.signMigrateERC20CustodyFundsCmd(ctx, txData, params) + require.ErrorContains(t, err, "tss is paused") + require.Nil(t, tx) + }) +} + +func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { + ctx := makeCtx(t) + + // Setup evm signer + tss := mocks.NewTSSMainnet() + evmSigner, err := getNewEvmSigner(tss) + require.NoError(t, err) + + // Setup txData struct + cctx := getCCTX(t) + require.NoError(t, err) + txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) + require.False(t, skip) + require.NoError(t, err) + + t.Run("signMigrateTssFundsCmd - should successfully sign", func(t *testing.T) { + // Call signMigrateTssFundsCmd + tx, err := evmSigner.signMigrateTssFundsCmd(ctx, txData) + require.NoError(t, err) + require.NotNil(t, tx) + + // Verify tx signature + tss := mocks.NewTSSMainnet() + verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) + + // Verify tx body basics + verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) + }) + + t.Run("signMigrateTssFundsCmd - should fail if keysign fails", func(t *testing.T) { + // Pause tss to make keysign fail + tss.Pause() + + // Call signMigrateTssFundsCmd + tx, err := evmSigner.signMigrateTssFundsCmd(ctx, txData) + require.ErrorContains(t, err, "signMigrateTssFundsCmd error") + require.Nil(t, tx) + }) +} diff --git a/zetaclient/chains/evm/signer/signer_test.go b/zetaclient/chains/evm/signer/signer_test.go index d96570a278..570cd57042 100644 --- a/zetaclient/chains/evm/signer/signer_test.go +++ b/zetaclient/chains/evm/signer/signer_test.go @@ -18,7 +18,6 @@ import ( "github.com/zeta-chain/zetacore/zetaclient/keys" "github.com/zeta-chain/zetacore/pkg/chains" - "github.com/zeta-chain/zetacore/pkg/constant" "github.com/zeta-chain/zetacore/testutil/sample" crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" "github.com/zeta-chain/zetacore/zetaclient/chains/base" @@ -372,50 +371,6 @@ func TestSigner_SignWithdrawTx(t *testing.T) { }) } -func TestSigner_SignCommandTx(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - evmSigner, err := getNewEvmSigner(nil) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignCommandTx CmdWhitelistERC20", func(t *testing.T) { - cmd := constant.CmdWhitelistERC20 - params := ConnectorAddress.Hex() - // Call SignCommandTx - tx, err := evmSigner.SignCommandTx(ctx, txData, cmd, params) - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - // Note: Revert tx calls erc20 custody contract with 0 gas token - verifyTxBodyBasics(t, tx, txData.to, txData.nonce, big.NewInt(0)) - }) - - t.Run("SignCommandTx CmdMigrateTssFunds", func(t *testing.T) { - cmd := constant.CmdMigrateTssFunds - // Call SignCommandTx - tx, err := evmSigner.SignCommandTx(ctx, txData, cmd, "") - require.NoError(t, err) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) - }) -} - func TestSigner_SignERC20WithdrawTx(t *testing.T) { ctx := makeCtx(t) @@ -507,87 +462,6 @@ func TestSigner_SignerErrorMsg(t *testing.T) { require.Contains(t, msg, "nonce 68270 chain 56") } -func TestSigner_SignWhitelistERC20Cmd(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.NoError(t, err) - require.False(t, skip) - - t.Run("SignWhitelistERC20Cmd - should successfully sign", func(t *testing.T) { - // Call SignWhitelistERC20Cmd - tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) - require.NoError(t, err) - require.NotNil(t, tx) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - verifyTxBodyBasics(t, tx, txData.to, txData.nonce, zeroValue) - }) - t.Run("SignWhitelistERC20Cmd - should fail on invalid erc20 address", func(t *testing.T) { - tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, "") - require.Nil(t, tx) - require.ErrorContains(t, err, "invalid erc20 address") - }) - t.Run("SignWhitelistERC20Cmd - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignWhitelistERC20Cmd - tx, err := evmSigner.SignWhitelistERC20Cmd(ctx, txData, sample.EthAddress().Hex()) - require.ErrorContains(t, err, "sign whitelist error") - require.Nil(t, tx) - }) -} - -func TestSigner_SignMigrateTssFundsCmd(t *testing.T) { - ctx := makeCtx(t) - - // Setup evm signer - tss := mocks.NewTSSMainnet() - evmSigner, err := getNewEvmSigner(tss) - require.NoError(t, err) - - // Setup txData struct - cctx := getCCTX(t) - txData, skip, err := NewOutboundData(ctx, cctx, 123, zerolog.Logger{}) - require.False(t, skip) - require.NoError(t, err) - - t.Run("SignMigrateTssFundsCmd - should successfully sign", func(t *testing.T) { - // Call SignMigrateTssFundsCmd - tx, err := evmSigner.SignMigrateTssFundsCmd(ctx, txData) - require.NoError(t, err) - require.NotNil(t, tx) - - // Verify tx signature - tss := mocks.NewTSSMainnet() - verifyTxSignature(t, tx, tss.Pubkey(), evmSigner.EvmSigner()) - - // Verify tx body basics - verifyTxBodyBasics(t, tx, txData.to, txData.nonce, txData.amount) - }) - - t.Run("SignMigrateTssFundsCmd - should fail if keysign fails", func(t *testing.T) { - // Pause tss to make keysign fail - tss.Pause() - - // Call SignMigrateTssFundsCmd - tx, err := evmSigner.SignMigrateTssFundsCmd(ctx, txData) - require.ErrorContains(t, err, "SignMigrateTssFundsCmd error") - require.Nil(t, tx) - }) -} func makeCtx(t *testing.T) context.Context { app := zctx.New(config.New(false), zerolog.Nop())