diff --git a/changelog.md b/changelog.md index 31fe5550fc..0f0a2c4318 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ * [2615](https://github.com/zeta-chain/node/pull/2615) - Refactor cleanup of outbound trackers * [2749](https://github.com/zeta-chain/node/pull/2749) - fix all lint errors from govet * [2725](https://github.com/zeta-chain/node/pull/2725) - refactor SetCctxAndNonceToCctxAndInboundHashToCctx to receive tsspubkey as an argument +* [2802](https://github.com/zeta-chain/node/pull/2802) - set default liquidity cap for new ZRC20s ### Tests diff --git a/e2e/txserver/zeta_tx_server.go b/e2e/txserver/zeta_tx_server.go index 41270e2bb4..b0e6cecce2 100644 --- a/e2e/txserver/zeta_tx_server.go +++ b/e2e/txserver/zeta_tx_server.go @@ -39,6 +39,7 @@ import ( "github.com/zeta-chain/node/app" "github.com/zeta-chain/node/cmd/zetacored/config" + "github.com/zeta-chain/node/e2e/utils" "github.com/zeta-chain/node/pkg/chains" "github.com/zeta-chain/node/pkg/coin" authoritytypes "github.com/zeta-chain/node/x/authority/types" @@ -441,7 +442,7 @@ func (zts ZetaTxServer) DeployZRC20s( } // deploy eth zrc20 - _, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + res, err := zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.GoerliLocalnet.ChainId, @@ -454,9 +455,16 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy eth zrc20: %s", err.Error()) } + zrc20, err := fetchZRC20FromDeployResponse(res) + if err != nil { + return "", err + } + if err := zts.initializeLiquidityCap(zrc20); err != nil { + return "", err + } // deploy btc zrc20 - _, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.BitcoinRegtest.ChainId, @@ -469,9 +477,16 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy btc zrc20: %s", err.Error()) } + zrc20, err = fetchZRC20FromDeployResponse(res) + if err != nil { + return "", err + } + if err := zts.initializeLiquidityCap(zrc20); err != nil { + return "", err + } // deploy sol zrc20 - _, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, "", chains.SolanaLocalnet.ChainId, @@ -484,9 +499,16 @@ func (zts ZetaTxServer) DeployZRC20s( if err != nil { return "", fmt.Errorf("failed to deploy sol zrc20: %s", err.Error()) } + zrc20, err = fetchZRC20FromDeployResponse(res) + if err != nil { + return "", err + } + if err := zts.initializeLiquidityCap(zrc20); err != nil { + return "", err + } // deploy erc20 zrc20 - res, err := zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( + res, err = zts.BroadcastTx(deployerAccount, fungibletypes.NewMsgDeployFungibleCoinZRC20( deployerAddr, erc20Addr, chains.GoerliLocalnet.ChainId, @@ -501,12 +523,12 @@ func (zts ZetaTxServer) DeployZRC20s( } // fetch the erc20 zrc20 contract address and remove the quotes - erc20zrc20Addr, err := FetchAttributeFromTxResponse(res, "Contract") + erc20zrc20Addr, err := fetchZRC20FromDeployResponse(res) if err != nil { - return "", fmt.Errorf("failed to fetch erc20 zrc20 contract address: %s, %s", err.Error(), res.String()) + return "", err } - if !ethcommon.IsHexAddress(erc20zrc20Addr) { - return "", fmt.Errorf("invalid address in event: %s", erc20zrc20Addr) + if err := zts.initializeLiquidityCap(erc20zrc20Addr); err != nil { + return "", err } return erc20zrc20Addr, nil @@ -557,6 +579,33 @@ func (zts *ZetaTxServer) SetAuthorityClient(authorityClient authoritytypes.Query zts.authorityClient = authorityClient } +// initializeLiquidityCap initializes the liquidity cap for the given coin with a large value +func (zts ZetaTxServer) initializeLiquidityCap(zrc20 string) error { + liquidityCap := sdktypes.NewUint(1e18).MulUint64(1e12) + + msg := fungibletypes.NewMsgUpdateZRC20LiquidityCap( + zts.MustGetAccountAddressFromName(utils.OperationalPolicyName), + zrc20, + liquidityCap, + ) + _, err := zts.BroadcastTx(utils.OperationalPolicyName, msg) + return err +} + +// fetchZRC20FromDeployResponse fetches the zrc20 address from the response +func fetchZRC20FromDeployResponse(res *sdktypes.TxResponse) (string, error) { + // fetch the erc20 zrc20 contract address and remove the quotes + zrc20Addr, err := FetchAttributeFromTxResponse(res, "Contract") + if err != nil { + return "", fmt.Errorf("failed to fetch zrc20 contract address: %s, %s", err.Error(), res.String()) + } + if !ethcommon.IsHexAddress(zrc20Addr) { + return "", fmt.Errorf("invalid address in event: %s", zrc20Addr) + } + + return zrc20Addr, nil +} + // fetchMessagePermissions fetches the message permissions for a given message // return a bool preV19 to indicate the node is preV19 and the query doesn't exist func (zts ZetaTxServer) fetchMessagePermissions(msg sdktypes.Msg) (authoritytypes.PolicyType, bool, error) { diff --git a/x/fungible/keeper/evm.go b/x/fungible/keeper/evm.go index 3f975a23ea..ab0332dfe3 100644 --- a/x/fungible/keeper/evm.go +++ b/x/fungible/keeper/evm.go @@ -34,12 +34,10 @@ import ( observertypes "github.com/zeta-chain/node/x/observer/types" ) -// TODO USE string constant var ( BigIntZero = big.NewInt(0) ZEVMGasLimitDepositAndCall = big.NewInt(1_000_000) - - ZEVMGasLimitConnectorCall = big.NewInt(1_000_000) + ZEVMGasLimitConnectorCall = big.NewInt(1_000_000) ) // DeployContract deploys a new contract in the ZEVM @@ -98,9 +96,6 @@ func (k Keeper) DeployContract( // DeployZRC20Contract creates and deploys an ERC20 contract on the EVM with the // erc20 module account as owner. Also adds itself to ForeignCoins fungible module state variable -// TODO Unit test for these functions -// https://github.com/zeta-chain/node/issues/864 -// TODO Remove repetitive code func (k Keeper) DeployZRC20Contract( ctx sdk.Context, name, symbol string, @@ -154,17 +149,20 @@ func (k Keeper) DeployZRC20Contract( err.Error(), ) } - coin, _ := k.GetForeignCoins(ctx, contractAddr.Hex()) - coin.CoinType = coinType - coin.Name = name - coin.Symbol = symbol + + // create and set in the store the new foreign coin object + newCoin, _ := k.GetForeignCoins(ctx, contractAddr.Hex()) + newCoin.CoinType = coinType + newCoin.Name = name + newCoin.Symbol = symbol // #nosec G115 uint8 -> uint32 false positive - coin.Decimals = uint32(decimals) - coin.Asset = erc20Contract - coin.Zrc20ContractAddress = contractAddr.Hex() - coin.ForeignChainId = chain.ChainId - coin.GasLimit = gasLimit.Uint64() - k.SetForeignCoins(ctx, coin) + newCoin.Decimals = uint32(decimals) + newCoin.Asset = erc20Contract + newCoin.Zrc20ContractAddress = contractAddr.Hex() + newCoin.ForeignChainId = chain.ChainId + newCoin.GasLimit = gasLimit.Uint64() + newCoin.LiquidityCap = sdk.NewUint(types.DefaultLiquidityCap).MulUint64(uint64(newCoin.Decimals)) + k.SetForeignCoins(ctx, newCoin) return contractAddr, nil } diff --git a/x/fungible/keeper/evm_test.go b/x/fungible/keeper/evm_test.go index 10b15e8401..7c0ef0bf00 100644 --- a/x/fungible/keeper/evm_test.go +++ b/x/fungible/keeper/evm_test.go @@ -316,6 +316,7 @@ func TestKeeper_DeployZRC20Contract(t *testing.T) { require.Equal(t, "bar", foreignCoins.Symbol) require.Equal(t, coin.CoinType_Gas, foreignCoins.CoinType) require.Equal(t, uint64(1000), foreignCoins.GasLimit) + require.True(t, foreignCoins.LiquidityCap.Equal(sdk.NewUint(types.DefaultLiquidityCap).MulUint64(8))) // can get the zrc20 data zrc20Data, err := k.QueryZRC20Data(ctx, addr) diff --git a/x/fungible/keeper/gas_coin_and_pool_test.go b/x/fungible/keeper/gas_coin_and_pool_test.go index 1b7315393c..186828c8b4 100644 --- a/x/fungible/keeper/gas_coin_and_pool_test.go +++ b/x/fungible/keeper/gas_coin_and_pool_test.go @@ -43,6 +43,13 @@ func setupGasCoin( ) require.NoError(t, err) assertContractDeployment(t, evmk, ctx, addr) + + // increase the default liquidity cap + foreignCoin, found := k.GetForeignCoins(ctx, addr.Hex()) + require.True(t, found) + foreignCoin.LiquidityCap = sdk.NewUint(1e18).MulUint64(1e12) + k.SetForeignCoins(ctx, foreignCoin) + return addr } @@ -68,6 +75,13 @@ func deployZRC20( ) require.NoError(t, err) assertContractDeployment(t, evmk, ctx, addr) + + // increase the default liquidity cap + foreignCoin, found := k.GetForeignCoins(ctx, addr.Hex()) + require.True(t, found) + foreignCoin.LiquidityCap = sdk.NewUint(1e18).MulUint64(1e12) + k.SetForeignCoins(ctx, foreignCoin) + return addr } diff --git a/x/fungible/types/zrc20.go b/x/fungible/types/zrc20.go index 3b6865f8fd..97c94ddcdc 100644 --- a/x/fungible/types/zrc20.go +++ b/x/fungible/types/zrc20.go @@ -2,6 +2,11 @@ package types import ethcommon "github.com/ethereum/go-ethereum/common" +// DefaultLiquidityCap is the default value set for the liquidity cap of a new ZRC20 when deployed +// for security reason, this value is low. An arbitrary value should be set during the process of deploying a new ZRC20 +// The value is represented in the base unit of the ZRC20, final value is calculated by multiplying this value by 10^decimals +const DefaultLiquidityCap = uint64(1000) + // ZRC20Data represents the ZRC4 token details used to map // the token to a Cosmos Coin type ZRC20Data struct {