Skip to content

Commit

Permalink
feat(e2e): add support for dynamically resolving erc20 addresses (#2783)
Browse files Browse the repository at this point in the history
* feat(e2e): add support for dynamically resolving erc20 addresses

* fix lint

* review feedback
  • Loading branch information
gartnera authored Aug 29, 2024
1 parent 26c4fb1 commit 994f1ca
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 25 deletions.
16 changes: 8 additions & 8 deletions cmd/zetae2e/config/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down
81 changes: 70 additions & 11 deletions cmd/zetae2e/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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")

Expand All @@ -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()

Expand Down Expand Up @@ -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 {
Expand All @@ -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)

Expand Down Expand Up @@ -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)
}
20 changes: 15 additions & 5 deletions e2e/runner/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion e2e/runner/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 994f1ca

Please sign in to comment.