Skip to content

Commit 7039dcc

Browse files
lumtisskosito
andauthored
feat: implement MsgUpdateERC20CustodyPauseStatus to pause or unpause ERC20 Custody (#2681)
* refactor cmd cctx creation * initialize migrate funds message * implement migrate message * zetaclient implementation * fix event * fix params * add e2e test * generate * changelog * zetaclient test * fix message test * initialize cctx test * cmd cctx type tests * message test * Update zetaclient/chains/evm/signer/admin_cmd.go Co-authored-by: skosito <[email protected]> * tanmay comments * stefan comments * admin commands * make generate * initialize message * some message fixes * implement logic on ZetaClient * update pause status * add in authorization list * E2E test * add test in admin workflow * add event for pausing * changelogs * Update x/crosschain/types/cmd_cctxs.go Co-authored-by: skosito <[email protected]> * Update x/crosschain/types/cmd_cctxs.go Co-authored-by: skosito <[email protected]> * fix * remove rate limiter test --------- Co-authored-by: skosito <[email protected]>
1 parent e2351ce commit 7039dcc

24 files changed

+2032
-162
lines changed

changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* [2634](https://github.com/zeta-chain/node/pull/2634) - add support for EIP-1559 gas fees
1111
* [2597](https://github.com/zeta-chain/node/pull/2597) - Add generic rpc metrics to zetaclient
1212
* [2538](https://github.com/zeta-chain/node/pull/2538) - add background worker routines to shutdown zetaclientd when needed for tss migration
13+
* [2681](https://github.com/zeta-chain/node/pull/2681) - implement `MsgUpdateERC20CustodyPauseStatus` to pause or unpause ERC20 Custody contract (to be used for the migration process for smart contract V2)
1314
* [2644](https://github.com/zeta-chain/node/pull/2644) - add created_timestamp to cctx status
1415

1516
### Refactor

cmd/zetae2e/local/local.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -296,14 +296,20 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
296296
if testAdmin {
297297
eg.Go(adminTestRoutine(conf, deployerRunner, verbose,
298298
e2etests.TestWhitelistERC20Name,
299-
e2etests.TestRateLimiterName,
300299
e2etests.TestPauseZRC20Name,
301300
e2etests.TestUpdateBytecodeZRC20Name,
302301
e2etests.TestUpdateBytecodeConnectorName,
303302
e2etests.TestDepositEtherLiquidityCapName,
304303
e2etests.TestCriticalAdminTransactionsName,
304+
e2etests.TestPauseERC20CustodyName,
305305
e2etests.TestMigrateERC20CustodyFundsName,
306306

307+
// Test the rate limiter functionalities
308+
// this test is currently incomplete and takes 10m to run
309+
// TODO: define assertion, and make more optimized
310+
// https://github.com/zeta-chain/node/issues/2090
311+
//e2etests.TestRateLimiterName,
312+
307313
// TestMigrateChainSupportName tests EVM chain migration. Currently this test doesn't work with Anvil because pre-EIP1559 txs are not supported
308314
// See issue below for details
309315
// TODO: renenable this test as per the issue below

docs/openapi/openapi.swagger.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -57242,6 +57242,11 @@ definitions:
5724257242
type: object
5724357243
crosschainMsgRemoveOutboundTrackerResponse:
5724457244
type: object
57245+
crosschainMsgUpdateERC20CustodyPauseStatusResponse:
57246+
type: object
57247+
properties:
57248+
cctx_index:
57249+
type: string
5724557250
crosschainMsgUpdateRateLimiterFlagsResponse:
5724657251
type: object
5724757252
crosschainMsgUpdateTssAddressResponse:

docs/spec/crosschain/messages.md

+12
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,15 @@ message MsgMigrateERC20CustodyFunds {
286286
}
287287
```
288288

289+
## MsgUpdateERC20CustodyPauseStatus
290+
291+
UpdateERC20CustodyPauseStatus creates a admin cmd cctx to update the pause status of the ERC20 custody contract
292+
293+
```proto
294+
message MsgUpdateERC20CustodyPauseStatus {
295+
string creator = 1;
296+
int64 chain_id = 2;
297+
bool pause = 3;
298+
}
299+
```
300+

e2e/e2etests/e2etests.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ const (
108108
TestUpdateBytecodeConnectorName = "update_bytecode_connector"
109109
TestRateLimiterName = "rate_limiter"
110110
TestCriticalAdminTransactionsName = "critical_admin_transactions"
111+
TestPauseERC20CustodyName = "pause_erc20_custody"
111112
TestMigrateERC20CustodyFundsName = "migrate_erc20_custody_funds"
112-
113-
TestMigrateTSSName = "migrate_TSS"
113+
TestMigrateTSSName = "migrate_TSS"
114114

115115
/*
116116
Special tests
@@ -573,6 +573,12 @@ var AllE2ETests = []runner.E2ETest{
573573
[]runner.ArgDefinition{},
574574
TestCriticalAdminTransactions,
575575
),
576+
runner.NewE2ETest(
577+
TestPauseERC20CustodyName,
578+
"pausing ERC20 custody on ZetaChain",
579+
[]runner.ArgDefinition{},
580+
TestPauseERC20Custody,
581+
),
576582
runner.NewE2ETest(
577583
TestMigrateERC20CustodyFundsName,
578584
"migrate ERC20 custody funds",
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package e2etests
2+
3+
import (
4+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
5+
"github.com/stretchr/testify/require"
6+
7+
"github.com/zeta-chain/zetacore/e2e/runner"
8+
"github.com/zeta-chain/zetacore/e2e/txserver"
9+
"github.com/zeta-chain/zetacore/e2e/utils"
10+
crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types"
11+
)
12+
13+
// TestPauseERC20Custody tests the pausing and unpausing of ERC20 custody contracts on the EVM chain
14+
func TestPauseERC20Custody(r *runner.E2ERunner, _ []string) {
15+
// get EVM chain ID
16+
chainID, err := r.EVMClient.ChainID(r.Ctx)
17+
require.NoError(r, err)
18+
19+
// check ERC20 custody contract is not paused
20+
paused, err := r.ERC20Custody.Paused(&bind.CallOpts{})
21+
require.NoError(r, err)
22+
require.False(r, paused)
23+
24+
// Part 1: Pause ERC20 custody contract
25+
26+
// send command for pausing ERC20 custody contract
27+
msg := crosschaintypes.NewMsgUpdateERC20CustodyPauseStatus(
28+
r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName),
29+
chainID.Int64(),
30+
true,
31+
)
32+
res, err := r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg)
33+
require.NoError(r, err)
34+
35+
// fetch cctx index from tx response
36+
cctxIndex, err := txserver.FetchAttributeFromTxResponse(res, "cctx_index")
37+
require.NoError(r, err)
38+
39+
cctxRes, err := r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: cctxIndex})
40+
require.NoError(r, err)
41+
42+
cctx := cctxRes.CrossChainTx
43+
r.Logger.CCTX(*cctx, "pausing")
44+
45+
// wait for the cctx to be mined
46+
r.WaitForMinedCCTXFromIndex(cctxIndex)
47+
48+
// check ERC20 custody contract is paused
49+
paused, err = r.ERC20Custody.Paused(&bind.CallOpts{})
50+
require.NoError(r, err)
51+
require.True(r, paused)
52+
53+
// Part 2: Unpause ERC20 custody contract
54+
55+
// send command for unpausing ERC20 custody contract
56+
msg = crosschaintypes.NewMsgUpdateERC20CustodyPauseStatus(
57+
r.ZetaTxServer.MustGetAccountAddressFromName(utils.AdminPolicyName),
58+
chainID.Int64(),
59+
false,
60+
)
61+
res, err = r.ZetaTxServer.BroadcastTx(utils.AdminPolicyName, msg)
62+
require.NoError(r, err)
63+
64+
// fetch cctx index from tx response
65+
cctxIndex, err = txserver.FetchAttributeFromTxResponse(res, "cctx_index")
66+
require.NoError(r, err)
67+
68+
cctxRes, err = r.CctxClient.Cctx(r.Ctx, &crosschaintypes.QueryGetCctxRequest{Index: cctxIndex})
69+
require.NoError(r, err)
70+
71+
cctx = cctxRes.CrossChainTx
72+
r.Logger.CCTX(*cctx, "unpausing")
73+
74+
// wait for the cctx to be mined
75+
r.WaitForMinedCCTXFromIndex(cctxIndex)
76+
77+
// check ERC20 custody contract is unpaused
78+
paused, err = r.ERC20Custody.Paused(&bind.CallOpts{})
79+
require.NoError(r, err)
80+
require.False(r, paused)
81+
}

pkg/constant/constant.go

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const (
1717
// CmdMigrateERC20CustodyFunds is used for CCTX of type cmd to give the instruction to the TSS to transfer its funds on a new address
1818
CmdMigrateERC20CustodyFunds = "cmd_migrate_erc20_custody_funds"
1919

20+
// CmdUpdateERC20CustodyPauseStatus is used for CCTX of type cmd to give the instruction to the TSS to update the pause status of the ERC20 custody contract
21+
CmdUpdateERC20CustodyPauseStatus = "cmd_update_erc20_custody_pause_status"
22+
2023
// CmdMigrateTssFunds is used for CCTX of type cmd to give the instruction to the TSS to transfer its funds on a new address
2124
CmdMigrateTssFunds = "cmd_migrate_tss_funds"
2225

@@ -28,4 +31,10 @@ const (
2831
// The Solana protocol sets minimum rent exempt to 890880 lamports but we set it to 1_000_000 lamports (0.001 SOL)
2932
// The number 890880 comes from CLI command `solana rent 0` and has been verified on devnet gateway program
3033
SolanaWalletRentExempt = 1_000_000
34+
35+
// OptionPause is the argument used in CmdUpdateERC20CustodyPauseStatus to pause the ERC20 custody contract
36+
OptionPause = "pause"
37+
38+
// OptionUnpause is the argument used in CmdUpdateERC20CustodyPauseStatus to unpause the ERC20 custody contract
39+
OptionUnpause = "unpause"
3140
)

proto/zetachain/zetacore/crosschain/events.proto

+6
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,9 @@ message EventERC20CustodyFundsMigration {
7575
string amount = 3;
7676
string cctx_index = 4;
7777
}
78+
79+
message EventERC20CustodyPausing {
80+
int64 chain_id = 1;
81+
bool pause = 2;
82+
string cctx_index = 3;
83+
}

proto/zetachain/zetacore/crosschain/tx.proto

+14
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ service Msg {
3636

3737
rpc MigrateERC20CustodyFunds(MsgMigrateERC20CustodyFunds)
3838
returns (MsgMigrateERC20CustodyFundsResponse);
39+
40+
rpc UpdateERC20CustodyPauseStatus(MsgUpdateERC20CustodyPauseStatus)
41+
returns (MsgUpdateERC20CustodyPauseStatusResponse);
3942
}
4043

4144
message MsgMigrateTssFunds {
@@ -204,3 +207,14 @@ message MsgMigrateERC20CustodyFunds {
204207
}
205208

206209
message MsgMigrateERC20CustodyFundsResponse { string cctx_index = 1; }
210+
211+
message MsgUpdateERC20CustodyPauseStatus {
212+
string creator = 1;
213+
int64 chain_id = 2;
214+
215+
// pause or unpause
216+
// true = pause, false = unpause
217+
bool pause = 3;
218+
}
219+
220+
message MsgUpdateERC20CustodyPauseStatusResponse { string cctx_index = 1; }

typescript/zetachain/zetacore/crosschain/events_pb.d.ts

+34
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,37 @@ export declare class EventERC20CustodyFundsMigration extends Message<EventERC20C
393393
static equals(a: EventERC20CustodyFundsMigration | PlainMessage<EventERC20CustodyFundsMigration> | undefined, b: EventERC20CustodyFundsMigration | PlainMessage<EventERC20CustodyFundsMigration> | undefined): boolean;
394394
}
395395

396+
/**
397+
* @generated from message zetachain.zetacore.crosschain.EventERC20CustodyPausing
398+
*/
399+
export declare class EventERC20CustodyPausing extends Message<EventERC20CustodyPausing> {
400+
/**
401+
* @generated from field: int64 chain_id = 1;
402+
*/
403+
chainId: bigint;
404+
405+
/**
406+
* @generated from field: bool pause = 2;
407+
*/
408+
pause: boolean;
409+
410+
/**
411+
* @generated from field: string cctx_index = 3;
412+
*/
413+
cctxIndex: string;
414+
415+
constructor(data?: PartialMessage<EventERC20CustodyPausing>);
416+
417+
static readonly runtime: typeof proto3;
418+
static readonly typeName = "zetachain.zetacore.crosschain.EventERC20CustodyPausing";
419+
static readonly fields: FieldList;
420+
421+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): EventERC20CustodyPausing;
422+
423+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): EventERC20CustodyPausing;
424+
425+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): EventERC20CustodyPausing;
426+
427+
static equals(a: EventERC20CustodyPausing | PlainMessage<EventERC20CustodyPausing> | undefined, b: EventERC20CustodyPausing | PlainMessage<EventERC20CustodyPausing> | undefined): boolean;
428+
}
429+

typescript/zetachain/zetacore/crosschain/tx_pb.d.ts

+61
Original file line numberDiff line numberDiff line change
@@ -894,3 +894,64 @@ export declare class MsgMigrateERC20CustodyFundsResponse extends Message<MsgMigr
894894
static equals(a: MsgMigrateERC20CustodyFundsResponse | PlainMessage<MsgMigrateERC20CustodyFundsResponse> | undefined, b: MsgMigrateERC20CustodyFundsResponse | PlainMessage<MsgMigrateERC20CustodyFundsResponse> | undefined): boolean;
895895
}
896896

897+
/**
898+
* @generated from message zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatus
899+
*/
900+
export declare class MsgUpdateERC20CustodyPauseStatus extends Message<MsgUpdateERC20CustodyPauseStatus> {
901+
/**
902+
* @generated from field: string creator = 1;
903+
*/
904+
creator: string;
905+
906+
/**
907+
* @generated from field: int64 chain_id = 2;
908+
*/
909+
chainId: bigint;
910+
911+
/**
912+
* pause or unpause
913+
* true = pause, false = unpause
914+
*
915+
* @generated from field: bool pause = 3;
916+
*/
917+
pause: boolean;
918+
919+
constructor(data?: PartialMessage<MsgUpdateERC20CustodyPauseStatus>);
920+
921+
static readonly runtime: typeof proto3;
922+
static readonly typeName = "zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatus";
923+
static readonly fields: FieldList;
924+
925+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MsgUpdateERC20CustodyPauseStatus;
926+
927+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MsgUpdateERC20CustodyPauseStatus;
928+
929+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MsgUpdateERC20CustodyPauseStatus;
930+
931+
static equals(a: MsgUpdateERC20CustodyPauseStatus | PlainMessage<MsgUpdateERC20CustodyPauseStatus> | undefined, b: MsgUpdateERC20CustodyPauseStatus | PlainMessage<MsgUpdateERC20CustodyPauseStatus> | undefined): boolean;
932+
}
933+
934+
/**
935+
* @generated from message zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatusResponse
936+
*/
937+
export declare class MsgUpdateERC20CustodyPauseStatusResponse extends Message<MsgUpdateERC20CustodyPauseStatusResponse> {
938+
/**
939+
* @generated from field: string cctx_index = 1;
940+
*/
941+
cctxIndex: string;
942+
943+
constructor(data?: PartialMessage<MsgUpdateERC20CustodyPauseStatusResponse>);
944+
945+
static readonly runtime: typeof proto3;
946+
static readonly typeName = "zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatusResponse";
947+
static readonly fields: FieldList;
948+
949+
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): MsgUpdateERC20CustodyPauseStatusResponse;
950+
951+
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): MsgUpdateERC20CustodyPauseStatusResponse;
952+
953+
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): MsgUpdateERC20CustodyPauseStatusResponse;
954+
955+
static equals(a: MsgUpdateERC20CustodyPauseStatusResponse | PlainMessage<MsgUpdateERC20CustodyPauseStatusResponse> | undefined, b: MsgUpdateERC20CustodyPauseStatusResponse | PlainMessage<MsgUpdateERC20CustodyPauseStatusResponse> | undefined): boolean;
956+
}
957+

x/authority/types/authorization_list.go

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var (
2323
}
2424
// AdminPolicyMessages keeps track of the message URLs that can, by default, only be executed by admin policy address
2525
AdminPolicyMessages = []string{
26+
"/zetachain.zetacore.crosschain.MsgUpdateERC20CustodyPauseStatus",
2627
"/zetachain.zetacore.crosschain.MsgMigrateERC20CustodyFunds",
2728
"/zetachain.zetacore.crosschain.MsgMigrateTssFunds",
2829
"/zetachain.zetacore.crosschain.MsgUpdateTssAddress",

x/authority/types/authorization_list_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ func TestDefaultAuthorizationsList(t *testing.T) {
414414

415415
// AdminPolicyMessageList is a list of messages that can be authorized by the admin policy
416416
var AdminPolicyMessageList = []string{
417+
sdk.MsgTypeURL(&crosschaintypes.MsgUpdateERC20CustodyPauseStatus{}),
417418
sdk.MsgTypeURL(&crosschaintypes.MsgMigrateERC20CustodyFunds{}),
418419
sdk.MsgTypeURL(&crosschaintypes.MsgMigrateTssFunds{}),
419420
sdk.MsgTypeURL(&crosschaintypes.MsgUpdateTssAddress{}),

x/crosschain/keeper/msg_server_migrate_erc20_custody_funds_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
sdk "github.com/cosmos/cosmos-sdk/types"
66
"github.com/stretchr/testify/require"
77
"github.com/zeta-chain/zetacore/pkg/coin"
8+
"github.com/zeta-chain/zetacore/pkg/constant"
89
testkeeper "github.com/zeta-chain/zetacore/testutil/keeper"
910
"github.com/zeta-chain/zetacore/testutil/sample"
1011
authoritytypes "github.com/zeta-chain/zetacore/x/authority/types"
@@ -58,6 +59,7 @@ func TestKeeper_MigrateERC20CustodyFunds(t *testing.T) {
5859
cctx, found := k.GetCrossChainTx(ctx, res.CctxIndex)
5960
require.True(t, found)
6061
require.Equal(t, coin.CoinType_Cmd, cctx.InboundParams.CoinType)
62+
require.Contains(t, cctx.RelayedMessage, constant.CmdMigrateERC20CustodyFunds)
6163
require.Len(t, cctx.OutboundParams, 1)
6264
require.EqualValues(
6365
t,

0 commit comments

Comments
 (0)