diff --git a/cmd/zetae2e/config/clients.go b/cmd/zetae2e/config/clients.go index ac4686fc23..4bdf8a6d3d 100644 --- a/cmd/zetae2e/config/clients.go +++ b/cmd/zetae2e/config/clients.go @@ -43,8 +43,8 @@ type E2EClients struct { ZevmAuth *bind.TransactOpts } -// zetaChainClients contains all the RPC clients and gRPC clients for ZetaChain -type zetaChainClients struct { +// ZetaChainClients contains all the RPC clients and gRPC clients for ZetaChain +type ZetaChainClients struct { AuthorityClient authoritytypes.QueryClient CctxClient crosschaintypes.QueryClient FungibleClient fungibletypes.QueryClient @@ -73,7 +73,7 @@ func getClientsFromConfig(ctx context.Context, conf config.Config, account confi if err != nil { return E2EClients{}, fmt.Errorf("failed to get evm client: %w", err) } - zetaChainClients, err := getZetaClients( + zetaChainClients, err := GetZetaClients( conf.RPCs.ZetaCoreGRPC, ) if err != nil { @@ -152,14 +152,14 @@ func getEVMClient( return evmClient, evmAuth, nil } -// getZetaClients get zeta clients -func getZetaClients(rpc string) ( - zetaChainClients, +// GetZetaClients get zeta clients +func GetZetaClients(rpc string) ( + ZetaChainClients, error, ) { grpcConn, err := grpc.Dial(rpc, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - return zetaChainClients{}, err + return ZetaChainClients{}, err } authorityClient := authoritytypes.NewQueryClient(grpcConn) @@ -170,7 +170,7 @@ func getZetaClients(rpc string) ( observerClient := observertypes.NewQueryClient(grpcConn) lightclientClient := lightclienttypes.NewQueryClient(grpcConn) - return zetaChainClients{ + return ZetaChainClients{ AuthorityClient: authorityClient, CctxClient: cctxClient, FungibleClient: fungibleClient, diff --git a/cmd/zetae2e/run.go b/cmd/zetae2e/run.go index 9c36d8e3d2..fe0ef1b4ff 100644 --- a/cmd/zetae2e/run.go +++ b/cmd/zetae2e/run.go @@ -16,10 +16,14 @@ import ( "github.com/zeta-chain/zetacore/e2e/config" "github.com/zeta-chain/zetacore/e2e/e2etests" "github.com/zeta-chain/zetacore/e2e/runner" + fungibletypes "github.com/zeta-chain/zetacore/x/fungible/types" + observertypes "github.com/zeta-chain/zetacore/x/observer/types" ) const flagVerbose = "verbose" const flagConfig = "config" +const flagERC20ChainName = "erc20-chain-name" +const flagERC20Symbol = "erc20-symbol" // NewRunCmd returns the run command // which runs the E2E from a config file describing the tests, networks, and accounts @@ -41,6 +45,9 @@ For example: zetae2e run deposit:1000 withdraw: --config config.yml`, os.Exit(1) } + cmd.Flags().String(flagERC20ChainName, "", "chain_name from /zeta-chain/observer/supportedChains") + cmd.Flags().String(flagERC20Symbol, "", "symbol from /zeta-chain/fungible/foreign_coins") + // Retain the verbose flag cmd.Flags().Bool(flagVerbose, false, "set to true to enable verbose logging") @@ -67,6 +74,29 @@ func runE2ETest(cmd *cobra.Command, args []string) error { // initialize logger logger := runner.NewLogger(verbose, color.FgHiCyan, "e2e") + // update config with dynamic ERC20 + erc20ChainName, err := cmd.Flags().GetString(flagERC20ChainName) + if err != nil { + return err + } + erc20Symbol, err := cmd.Flags().GetString(flagERC20Symbol) + if err != nil { + return err + } + if erc20ChainName != "" && erc20Symbol != "" { + erc20Asset, zrc20ContractAddress, err := findERC20( + cmd.Context(), + conf.RPCs.ZetaCoreGRPC, + erc20ChainName, + erc20Symbol, + ) + if err != nil { + return err + } + conf.Contracts.EVM.ERC20 = config.DoubleQuotedString(erc20Asset) + conf.Contracts.ZEVM.ERC20ZRC20Addr = config.DoubleQuotedString(zrc20ContractAddress) + } + // set config app.SetConfig() @@ -99,11 +129,6 @@ func runE2ETest(cmd *cobra.Command, args []string) error { testRunner.CctxTimeout = 60 * time.Minute testRunner.ReceiptTimeout = 60 * time.Minute - balancesBefore, err := testRunner.GetAccountBalances(true) - if err != nil { - return err - } - // parse test names and arguments from cmd args and run them userTestsConfigs, err := parseCmdArgsToE2ETestRunConfig(args) if err != nil { @@ -119,15 +144,9 @@ func runE2ETest(cmd *cobra.Command, args []string) error { return err } - balancesAfter, err := testRunner.GetAccountBalances(true) - if err != nil { - return err - } - // Print tests completion info logger.Print("tests finished successfully in %s", time.Since(testStartTime).String()) testRunner.Logger.SetColor(color.FgHiRed) - testRunner.PrintTotalDiff(runner.GetAccountBalancesDiff(balancesBefore, balancesAfter)) testRunner.Logger.SetColor(color.FgHiGreen) testRunner.PrintTestReports(reports) @@ -157,3 +176,43 @@ func parseCmdArgsToE2ETestRunConfig(args []string) ([]runner.E2ETestRunConfig, e } return tests, nil } + +// findERC20 loads ERC20 addresses via gRPC given CLI flags +func findERC20(ctx context.Context, zetaCoreGRPCURL, erc20ChainName, erc20Symbol string) (string, string, error) { + clients, err := zetae2econfig.GetZetaClients(zetaCoreGRPCURL) + if err != nil { + return "", "", fmt.Errorf("get zeta clients: %w", err) + } + + supportedChainsRes, err := clients.ObserverClient.SupportedChains(ctx, &observertypes.QuerySupportedChains{}) + if err != nil { + return "", "", fmt.Errorf("get chain params: %w", err) + } + + chainID := int64(0) + for _, chain := range supportedChainsRes.Chains { + if chain.Name == erc20ChainName { + chainID = chain.ChainId + break + } + } + if chainID == 0 { + return "", "", fmt.Errorf("chain %s not found", erc20ChainName) + } + + foreignCoinsRes, err := clients.FungibleClient.ForeignCoinsAll(ctx, &fungibletypes.QueryAllForeignCoinsRequest{}) + if err != nil { + return "", "", fmt.Errorf("get foreign coins: %w", err) + } + + for _, coin := range foreignCoinsRes.ForeignCoins { + if coin.ForeignChainId != chainID { + continue + } + // sometimes symbol is USDT, sometimes it's like USDT.SEPOLIA + if strings.HasPrefix(coin.Symbol, erc20Symbol) || strings.HasSuffix(coin.Symbol, erc20Symbol) { + return coin.Asset, coin.Zrc20ContractAddress, nil + } + } + return "", "", fmt.Errorf("erc20 %s not found on %s", erc20Symbol, erc20ChainName) +} diff --git a/e2e/runner/balances.go b/e2e/runner/balances.go index f7ab0938c1..f1e1fe3f22 100644 --- a/e2e/runner/balances.go +++ b/e2e/runner/balances.go @@ -8,8 +8,11 @@ import ( "github.com/btcsuite/btcutil" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/pkg/errors" + "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" ) +var errNilZRC20 = errors.New("zrc20 contract is nil") + // AccountBalances is a struct that contains the balances of the accounts used in the E2E test type AccountBalances struct { ZetaETH *big.Int @@ -31,6 +34,13 @@ type AccountBalancesDiff struct { ERC20 *big.Int } +func (r *E2ERunner) getZRC20BalanceSafe(z *zrc20.ZRC20) (*big.Int, error) { + if z == nil { + return new(big.Int), errNilZRC20 + } + return z.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) +} + // GetAccountBalances returns the account balances of the accounts used in the E2E test func (r *E2ERunner) GetAccountBalances(skipBTC bool) (AccountBalances, error) { // zevm @@ -42,21 +52,21 @@ func (r *E2ERunner) GetAccountBalances(skipBTC bool) (AccountBalances, error) { if err != nil { return AccountBalances{}, err } - zetaEth, err := r.ETHZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + zetaEth, err := r.getZRC20BalanceSafe(r.ETHZRC20) if err != nil { return AccountBalances{}, err } - zetaErc20, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + zetaErc20, err := r.getZRC20BalanceSafe(r.ERC20ZRC20) if err != nil { return AccountBalances{}, err } - zetaBtc, err := r.BTCZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + zetaBtc, err := r.getZRC20BalanceSafe(r.BTCZRC20) if err != nil { return AccountBalances{}, err } - zetaSol, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + zetaSol, err := r.getZRC20BalanceSafe(r.SOLZRC20) if err != nil { - return AccountBalances{}, err + r.Logger.Error("get SOL balance: %v", err) } // evm diff --git a/e2e/runner/report.go b/e2e/runner/report.go index 31cd085fc6..d80024e0dd 100644 --- a/e2e/runner/report.go +++ b/e2e/runner/report.go @@ -59,7 +59,7 @@ func (r *E2ERunner) PrintTestReports(tr TestReports) { if err != nil { r.Logger.Print("Error rendering test report: %s", err) } - r.Logger.PrintNoPrefix(table, "") + r.Logger.PrintNoPrefix(table) } // NetworkReport is a struct that contains the report for the network used after running e2e tests