diff --git a/changelog.md b/changelog.md index 78b9ab0b59..1f49f12ecb 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ * [2904](https://github.com/zeta-chain/node/pull/2904) - integrate authenticated calls smart contract functionality into protocol * [2919](https://github.com/zeta-chain/node/pull/2919) - add inbound sender to revert context * [2957](https://github.com/zeta-chain/node/pull/2957) - enable Bitcoin inscription support on testnet +* [2979](https://github.com/zeta-chain/node/pull/2979) - add fungible keeper ability to lock/unlock ZRC20 tokens ### Refactor diff --git a/e2e/e2etests/test_precompiles_bank.go b/e2e/e2etests/test_precompiles_bank.go index 2517410339..1afd52d9cf 100644 --- a/e2e/e2etests/test_precompiles_bank.go +++ b/e2e/e2etests/test_precompiles_bank.go @@ -10,6 +10,7 @@ import ( "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/precompiles/bank" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" ) func TestPrecompilesBank(r *runner.E2ERunner, args []string) { @@ -29,7 +30,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { // Reset the allowance to 0; this is needed when running upgrade tests where // this test runs twice. - tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, big.NewInt(0)) + tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, fungibletypes.ModuleAddressZEVM, big.NewInt(0)) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Resetting allowance failed") @@ -59,7 +60,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(0), cosmosBalance.Uint64(), "spender cosmos coin balance should be 0") // Approve allowance of 500 ERC20ZRC20 tokens for the bank contract. Should pass. - tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, depositAmount) + tx, err := r.ERC20ZRC20.Approve(r.ZEVMAuth, fungibletypes.ModuleAddressZEVM, depositAmount) require.NoError(r, err) receipt := utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Approve ETHZRC20 bank allowance tx failed") @@ -72,7 +73,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { utils.RequiredTxFailed(r, receipt, "Depositting an amount higher than allowed should fail") // Approve allowance of 1000 ERC20ZRC20 tokens. - tx, err = r.ERC20ZRC20.Approve(r.ZEVMAuth, bank.ContractAddress, big.NewInt(1e3)) + tx, err = r.ERC20ZRC20.Approve(r.ZEVMAuth, fungibletypes.ModuleAddressZEVM, big.NewInt(1e3)) require.NoError(r, err) receipt = utils.MustWaitForTxReceipt(r.Ctx, r.ZEVMClient, tx, r.Logger, r.ReceiptTimeout) utils.RequireTxSuccessful(r, receipt, "Approve ETHZRC20 bank allowance tx failed") @@ -103,7 +104,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(500), cosmosBalance.Uint64(), "spender cosmos coin balance should be 500") // Bank: ERC20ZRC20 balance should be 500 tokens locked. - bankZRC20Balance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, fungibletypes.ModuleAddressZEVM) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(500), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 500") @@ -115,7 +116,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { // Bank: ERC20ZRC20 balance should be 500 tokens locked after a failed withdraw. // No tokens should be unlocked with a failed withdraw. - bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, fungibletypes.ModuleAddressZEVM) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(500), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 500") @@ -143,7 +144,7 @@ func TestPrecompilesBank(r *runner.E2ERunner, args []string) { require.Equal(r, uint64(1000), zrc20Balance.Uint64(), "spender ERC20ZRC20 balance should be 1000") // Bank: ERC20ZRC20 balance should be 0 tokens locked. - bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, bank.ContractAddress) + bankZRC20Balance, err = r.ERC20ZRC20.BalanceOf(&bind.CallOpts{Context: r.Ctx}, fungibletypes.ModuleAddressZEVM) require.NoError(r, err, "Call ERC20ZRC20.BalanceOf") require.Equal(r, uint64(0), bankZRC20Balance.Uint64(), "bank ERC20ZRC20 balance should be 0") } @@ -158,7 +159,7 @@ func TestPrecompilesBankNonZRC20(r *runner.E2ERunner, args []string) { r.ZEVMAuth.GasLimit = previousGasLimit }() - spender, bankAddr := r.EVMAddress(), bank.ContractAddress + spender, bankAddr := r.EVMAddress(), fungibletypes.ModuleAddressZEVM // Create a bank contract caller. bankContract, err := bank.NewIBank(bank.ContractAddress, r.ZEVMClient) diff --git a/e2e/e2etests/test_precompiles_bank_through_contract.go b/e2e/e2etests/test_precompiles_bank_through_contract.go index 480663284e..178db56c1d 100644 --- a/e2e/e2etests/test_precompiles_bank_through_contract.go +++ b/e2e/e2etests/test_precompiles_bank_through_contract.go @@ -12,6 +12,7 @@ import ( "github.com/zeta-chain/node/e2e/runner" "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/precompiles/bank" + fungibletypes "github.com/zeta-chain/node/x/fungible/types" ) func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { @@ -59,7 +60,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Check initial balances. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Deposit without previous alllowance should fail. receipt = depositThroughTestBank(r, testBank, zrc20Address, oneThousand) @@ -68,10 +69,10 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Check balances, should be the same. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Allow 500 ZRC20 to bank precompile. - approveAllowance(r, bank.ContractAddress, fiveHundred) + approveAllowance(r, fungibletypes.ModuleAddressZEVM, fiveHundred) // Deposit 501 ERC20ZRC20 tokens to the bank contract, through TestBank. // It's higher than allowance but lower than balance, should fail. @@ -81,10 +82,10 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Allow 1000 ZRC20 to bank precompile. - approveAllowance(r, bank.ContractAddress, oneThousand) + approveAllowance(r, fungibletypes.ModuleAddressZEVM, oneThousand) // Deposit 1001 ERC20ZRC20 tokens to the bank contract. // It's higher than spender balance but within approved allowance, should fail. @@ -94,7 +95,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Deposit 500 ERC20ZRC20 tokens to the bank contract, it's within allowance and balance. Should pass. receipt = depositThroughTestBank(r, testBank, zrc20Address, fiveHundred) @@ -103,7 +104,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances should be transferred. Bank now locks 500 ZRC20 tokens. balanceShouldBe(r, 500, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 500, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 500, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 500, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Check the deposit event. eventDeposit, err := bankPrecompileCaller.ParseDeposit(*receipt.Logs[0]) @@ -119,7 +120,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances shouldn't change. balanceShouldBe(r, 500, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 500, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 500, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 500, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Try to withdraw 500 ERC20ZRC20 tokens. Should pass. receipt = withdrawThroughTestBank(r, testBank, zrc20Address, fiveHundred) @@ -128,7 +129,7 @@ func TestPrecompilesBankThroughContract(r *runner.E2ERunner, args []string) { // Balances should be reverted to initial state. balanceShouldBe(r, 0, checkCosmosBalance(r, testBank, zrc20Address, spender)) balanceShouldBe(r, 1000, checkZRC20Balance(r, spender)) - balanceShouldBe(r, 0, checkZRC20Balance(r, bank.ContractAddress)) + balanceShouldBe(r, 0, checkZRC20Balance(r, fungibletypes.ModuleAddressZEVM)) // Check the withdraw event. eventWithdraw, err := bankPrecompileCaller.ParseWithdraw(*receipt.Logs[0]) diff --git a/precompiles/bank/method_deposit.go b/precompiles/bank/method_deposit.go index 37fdd53da4..62cb59dd51 100644 --- a/precompiles/bank/method_deposit.go +++ b/precompiles/bank/method_deposit.go @@ -18,7 +18,7 @@ import ( // The caller cosmos address will be calculated from the EVM caller address. by executing toAddr := sdk.AccAddress(addr.Bytes()). // This function can be think of a permissionless way of minting cosmos coins. // This is how deposit works: -// - The caller has to allow the bank contract to spend a certain amount ZRC20 token coins on its behalf. This is mandatory. +// - The caller has to allow the Fungible ZEVM address to spend a certain amount ZRC20 token coins on its behalf. This is mandatory. // - Then, the caller calls deposit(ZRC20 address, amount), to deposit the amount and receive cosmos coins. // - The bank will check there's enough balance, the caller is not a blocked address, and the token is a not paused ZRC20. // - Then the cosmos coins "zrc20/0x12345" will be minted and sent to the caller's cosmos address. diff --git a/precompiles/bank/method_test.go b/precompiles/bank/method_test.go index 9ce21732ae..b556c57720 100644 --- a/precompiles/bank/method_test.go +++ b/precompiles/bank/method_test.go @@ -28,7 +28,7 @@ import ( func Test_Methods(t *testing.T) { t.Run("should fail when trying to run deposit as read only method", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM methodID := ts.bankABI.Methods[DepositMethodName] // Set CallerAddress and evm.Origin to the caller address. @@ -55,7 +55,7 @@ func Test_Methods(t *testing.T) { t.Run("should fail when trying to run withdraw as read only method", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM methodID := ts.bankABI.Methods[WithdrawMethodName] // Set CallerAddress and evm.Origin to the caller address. @@ -82,7 +82,7 @@ func Test_Methods(t *testing.T) { t.Run("should fail when caller has 0 token balance", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM methodID := ts.bankABI.Methods[DepositMethodName] // Set CallerAddress and evm.Origin to the caller address. @@ -116,7 +116,7 @@ func Test_Methods(t *testing.T) { t.Run("should fail when bank has 0 token allowance", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) methodID := ts.bankABI.Methods[DepositMethodName] @@ -152,7 +152,7 @@ func Test_Methods(t *testing.T) { t.Run("should fail when trying to deposit more than allowed to bank", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) // Allow bank to spend 500 ZRC20 tokens. @@ -200,7 +200,7 @@ func Test_Methods(t *testing.T) { t.Run("should fail when trying to deposit more than user balance", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) // Allow bank to spend 500 ZRC20 tokens. @@ -254,7 +254,7 @@ func Test_Methods(t *testing.T) { t.Run("should deposit tokens and retrieve balance of cosmos coin", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) methodID := ts.bankABI.Methods[DepositMethodName] @@ -300,7 +300,7 @@ func Test_Methods(t *testing.T) { t.Run("should deposit tokens, withdraw and check with balanceOf", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) methodID := ts.bankABI.Methods[DepositMethodName] @@ -379,7 +379,7 @@ func Test_Methods(t *testing.T) { t.Run("should deposit tokens and fail when withdrawing more than depositted", func(t *testing.T) { ts := setupChain(t) - caller := fungibletypes.ModuleAddressEVM + caller := fungibletypes.ModuleAddressZEVM ts.fungibleKeeper.DepositZRC20(ts.ctx, ts.zrc20Address, caller, big.NewInt(1000)) methodID := ts.bankABI.Methods[DepositMethodName] @@ -548,7 +548,7 @@ func allowBank(t *testing.T, ts testSuite, amount *big.Int) { ts.ctx, ts.fungibleKeeper, &ts.zrc20ABI, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, ts.zrc20Address, "approve", []interface{}{ts.bankContract.Address(), amount}, diff --git a/x/fungible/keeper/zrc20_check_allowance.go b/x/fungible/keeper/zrc20_check_allowance.go index 19a9846817..538f985c47 100644 --- a/x/fungible/keeper/zrc20_check_allowance.go +++ b/x/fungible/keeper/zrc20_check_allowance.go @@ -33,11 +33,11 @@ func (k Keeper) CheckFungibleZRC20Allowance( return fmt.Errorf("zrc20 address cannot be zero") } - args := []interface{}{from, fungibletypes.ModuleAddressEVM} + args := []interface{}{from, fungibletypes.ModuleAddressZEVM} res, err := k.CallEVM( ctx, *zrc20ABI, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, zrc20Address, big.NewInt(0), nil, diff --git a/x/fungible/keeper/zrc20_check_balance.go b/x/fungible/keeper/zrc20_check_balance.go index a9f0272c99..34e393d6b9 100644 --- a/x/fungible/keeper/zrc20_check_balance.go +++ b/x/fungible/keeper/zrc20_check_balance.go @@ -32,14 +32,14 @@ func (k Keeper) CheckFungibleZRC20Balance( res, err := k.CallEVM( ctx, *zrc20ABI, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, zrc20Address, big.NewInt(0), nil, true, true, balanceOf, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, ) if err != nil { return err diff --git a/x/fungible/keeper/zrc20_lock_token.go b/x/fungible/keeper/zrc20_lock_token.go index 8d79843ef6..1f96e20e55 100644 --- a/x/fungible/keeper/zrc20_lock_token.go +++ b/x/fungible/keeper/zrc20_lock_token.go @@ -43,11 +43,11 @@ func (k Keeper) LockZRC20( return err } - args := []interface{}{from, fungibletypes.ModuleAddressEVM, amount} + args := []interface{}{from, fungibletypes.ModuleAddressZEVM, amount} res, err := k.CallEVM( ctx, *zrc20ABI, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, zrc20Address, big.NewInt(0), nil, diff --git a/x/fungible/keeper/zrc20_unlock_token.go b/x/fungible/keeper/zrc20_unlock_token.go index 9d6243b2da..26f917fe82 100644 --- a/x/fungible/keeper/zrc20_unlock_token.go +++ b/x/fungible/keeper/zrc20_unlock_token.go @@ -44,7 +44,7 @@ func (k Keeper) UnlockZRC20( res, err := k.CallEVM( ctx, *zrc20ABI, - fungibletypes.ModuleAddressEVM, + fungibletypes.ModuleAddressZEVM, zrc20Address, big.NewInt(0), nil, @@ -61,7 +61,7 @@ func (k Keeper) UnlockZRC20( return fmt.Errorf("%s", res.VmError) } - ret, err := zrc20ABI.Methods[transferFrom].Outputs.Unpack(res.Ret) + ret, err := zrc20ABI.Methods[transfer].Outputs.Unpack(res.Ret) if err != nil { return err } diff --git a/x/fungible/types/keys.go b/x/fungible/types/keys.go index 7e81328c15..339cc63560 100644 --- a/x/fungible/types/keys.go +++ b/x/fungible/types/keys.go @@ -29,7 +29,10 @@ func KeyPrefix(p string) []byte { var ( ModuleAddress = authtypes.NewModuleAddress(ModuleName) ModuleAddressEVM = common.BytesToAddress(ModuleAddress.Bytes()) - AdminAddress = "zeta1rx9r8hff0adaqhr5tuadkzj4e7ns2ntg446vtt" + // ModuleAddressZEVM is calculated in the same way as ModuleAddressEVM. + // Maintain it for legibility in functions calling fungible module address from zEVM. + ModuleAddressZEVM = common.BytesToAddress(ModuleAddress.Bytes()) + AdminAddress = "zeta1rx9r8hff0adaqhr5tuadkzj4e7ns2ntg446vtt" ) const (