diff --git a/chain/ethereum/ethererum_chain.go b/chain/ethereum/ethererum_chain.go index 2c96e663a..010bd0068 100644 --- a/chain/ethereum/ethererum_chain.go +++ b/chain/ethereum/ethererum_chain.go @@ -2,16 +2,12 @@ package ethereum import ( "context" - "encoding/json" "fmt" "io" - "os" - "path" - "path/filepath" - "strconv" - "strings" + "time" sdkmath "cosmossdk.io/math" + dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/volume" @@ -22,75 +18,45 @@ import ( "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/strangelove-ventures/interchaintest/v8/testutil" "go.uber.org/zap" -) -var _ ibc.Chain = &EthereumChain{} + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" +) const ( - blockTime = 2 // seconds - rpcPort = "8545/tcp" + rpcPort = "8545/tcp" ) +var natPorts = nat.PortMap{ + nat.Port(rpcPort): {}, +} + var ( GWEI = sdkmath.NewInt(1_000_000_000) ETHER = GWEI.MulRaw(1_000_000_000) ) -var natPorts = nat.PortMap{ - nat.Port(rpcPort): {}, -} - type EthereumChain struct { testName string cfg ibc.ChainConfig log *zap.Logger - VolumeName string - NetworkID string - DockerClient *dockerclient.Client + volumeName string + networkID string + dockerClient *dockerclient.Client containerLifecycle *dockerutil.ContainerLifecycle hostRPCPort string - - genesisWallets GenesisWallets - - keystoreMap map[string]string -} - -func DefaultEthereumAnvilChainConfig( - name string, -) ibc.ChainConfig { - return ibc.ChainConfig{ - Type: "ethereum", - Name: name, - ChainID: "31337", // default anvil chain-id - Bech32Prefix: "n/a", - CoinType: "60", - Denom: "wei", - GasPrices: "0", - GasAdjustment: 0, - TrustingPeriod: "0", - NoHostMount: false, - Images: []ibc.DockerImage{ - { - Repository: "ghcr.io/foundry-rs/foundry", - Version: "latest", - UidGid: "1000:1000", - }, - }, - Bin: "anvil", - } + rpcClient *ethclient.Client } func NewEthereumChain(testName string, chainConfig ibc.ChainConfig, log *zap.Logger) *EthereumChain { return &EthereumChain{ - testName: testName, - cfg: chainConfig, - log: log, - genesisWallets: NewGenesisWallet(), - keystoreMap: make(map[string]string), + testName: testName, + cfg: chainConfig, + log: log, } } @@ -115,9 +81,9 @@ func (c *EthereumChain) Initialize(ctx context.Context, testName string, cli *do if err != nil { return fmt.Errorf("creating volume for chain node: %w", err) } - c.VolumeName = v.Name - c.NetworkID = networkID - c.DockerClient = cli + c.volumeName = v.Name + c.networkID = networkID + c.dockerClient = cli if err := dockerutil.SetVolumeOwner(ctx, dockerutil.VolumeOwnerOptions{ Log: c.log, @@ -136,19 +102,15 @@ func (c *EthereumChain) Initialize(ctx context.Context, testName string, cli *do } func (c *EthereumChain) Name() string { - return fmt.Sprintf("anvil-%s-%s", c.cfg.ChainID, dockerutil.SanitizeContainerName(c.testName)) + return fmt.Sprintf("%s-%s-%s-%s", c.cfg.Name, c.cfg.Bin, c.cfg.ChainID, dockerutil.SanitizeContainerName(c.testName)) } func (c *EthereumChain) HomeDir() string { - return "/home/foundry" -} - -func (c *EthereumChain) KeystoreDir() string { - return path.Join(c.HomeDir(), ".foundry", "keystores") + return "/home/ethereum" } func (c *EthereumChain) Bind() []string { - return []string{fmt.Sprintf("%s:%s", c.VolumeName, c.HomeDir())} + return []string{fmt.Sprintf("%s:%s", c.volumeName, c.HomeDir())} } func (c *EthereumChain) pullImages(ctx context.Context, cli *dockerclient.Client) { @@ -171,48 +133,7 @@ func (c *EthereumChain) pullImages(ctx context.Context, cli *dockerclient.Client } } -func (c *EthereumChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { - // TODO: - // * add support for different denom configuration, ether, gwei or wei, - // this will affect SendFunds, SendFundsWithNote, GetBalance and anything other than wei will lose precision for GetBalance - // * add support for modifying genesis amount config, default is 10 ether - // * add support for ConfigFileOverrides - // * block time - // * add support for custom chain id, must be an int? - // * add support for custom gas-price - // Maybe add code-size-limit configuration for larger contracts - - // IBC support, add when necessary - // * add additionalGenesisWallet support for relayer wallet, either add genesis accounts or tx after chain starts - - cmd := []string{c.cfg.Bin, - "--host", "0.0.0.0", // Anyone can call - "--block-time", "2", // 2 second block times - "--accounts", "10", // We current only use the first account for the faucet, but tests may expect the default - "--balance", "10000000", // Genesis accounts loaded with 10mil ether, change as needed - "--no-cors", - "--gas-price", "20000000000", - "--block-base-fee-per-gas", "0", - } - - var mounts []mount.Mount - if loadState, ok := c.cfg.ConfigFileOverrides["--load-state"].(string); ok { - pwd, err := os.Getwd() - if err != nil { - return err - } - localJsonFile := filepath.Join(pwd, loadState) - dockerJsonFile := path.Join(c.HomeDir(), path.Base(loadState)) - mounts = []mount.Mount{ - { - Type: mount.TypeBind, - Source: localJsonFile, - Target: dockerJsonFile, - }, - } - cmd = append(cmd, "--load-state", dockerJsonFile) - } - +func (c *EthereumChain) Start(ctx context.Context, cmd []string, mount []mount.Mount) error { usingPorts := nat.PortMap{} for k, v := range natPorts { usingPorts[k] = v @@ -230,7 +151,7 @@ func (c *EthereumChain) Start(testName string, ctx context.Context, additionalGe fmt.Printf("Port Overrides: %v. Using: %v\n", c.cfg.HostPortOverride, usingPorts) } - err := c.containerLifecycle.CreateContainer(ctx, c.testName, c.NetworkID, c.cfg.Images[0], usingPorts, c.Bind(), mounts, c.HostName(), cmd, nil, []string{}) + err := c.containerLifecycle.CreateContainer(ctx, c.testName, c.networkID, c.cfg.Images[0], usingPorts, c.Bind(), mount, c.HostName(), cmd, nil, []string{}) if err != nil { return err } @@ -247,7 +168,19 @@ func (c *EthereumChain) Start(testName string, ctx context.Context, additionalGe } c.hostRPCPort = hostPorts[0] - fmt.Println("Host RPC port: ", c.hostRPCPort) + + // wait for rpc to come up + time.Sleep(time.Second * 2) + + // dial the rpc host + c.rpcClient, err = ethclient.Dial(c.GetHostRPCAddress()) + if err != nil { + time.Sleep(time.Second * 2) + c.rpcClient, err = ethclient.Dial(c.GetHostRPCAddress()) + if err != nil { + return fmt.Errorf("failed to dial ETH rpc host(%s): %w", c.GetHostRPCAddress(), err) + } + } return testutil.WaitForBlocks(ctx, 2, c) } @@ -256,12 +189,12 @@ func (c *EthereumChain) HostName() string { return dockerutil.CondenseHostName(c.Name()) } +func (c *EthereumChain) NewJob() *dockerutil.Image { + return dockerutil.NewImage(c.Logger(), c.dockerClient, c.networkID, c.testName, c.cfg.Images[0].Repository, c.cfg.Images[0].Version) +} + func (c *EthereumChain) Exec(ctx context.Context, cmd []string, env []string) (stdout, stderr []byte, err error) { - logger := zap.NewNop() - if cmd[1] != "block-number" { // too much logging, maybe switch to an rpc lib in the future - logger = c.logger() - } - job := dockerutil.NewImage(logger, c.DockerClient, c.NetworkID, c.testName, c.cfg.Images[0].Repository, c.cfg.Images[0].Version) + job := c.NewJob() opts := dockerutil.ContainerOptions{ Env: env, Binds: c.Bind(), @@ -270,7 +203,7 @@ func (c *EthereumChain) Exec(ctx context.Context, cmd []string, env []string) (s return res.Stdout, res.Stderr, res.Err } -func (c *EthereumChain) logger() *zap.Logger { +func (c *EthereumChain) Logger() *zap.Logger { return c.log.With( zap.String("chain_id", c.cfg.ChainID), zap.String("test", c.testName), @@ -293,184 +226,19 @@ func (c *EthereumChain) GetHostWSAddress() string { return "ws://" + c.hostRPCPort } -type NewWalletOutput struct { - Address string `json:"address"` - Path string `json:"path"` -} - -func (c *EthereumChain) MakeKeystoreDir(ctx context.Context) error { - cmd := []string{"mkdir", "-p", c.KeystoreDir()} - _, _, err := c.Exec(ctx, cmd, nil) - return err -} - -func (c *EthereumChain) CreateKey(ctx context.Context, keyName string) error { - err := c.MakeKeystoreDir(ctx) // Ensure keystore directory is created - if err != nil { - return err - } - - _, ok := c.keystoreMap[keyName] - if ok { - return fmt.Errorf("Keyname (%s) already used", keyName) - } - - cmd := []string{"cast", "wallet", "new", c.KeystoreDir(), "--unsafe-password", "", "--json"} - stdout, _, err := c.Exec(ctx, cmd, nil) - if err != nil { - return err - } - - newWallet := []NewWalletOutput{} - err = json.Unmarshal(stdout, &newWallet) - if err != nil { - return err - } - - c.keystoreMap[keyName] = newWallet[0].Path - - return nil -} - -// cast wallet import requires a password prompt which docker isn't properly handling. For now, we only use CreateKey(). -// Can re-add/support with this commit: https://github.com/foundry-rs/foundry/pull/6671 -func (c *EthereumChain) RecoverKey(ctx context.Context, keyName, mnemonic string) error { - err := c.MakeKeystoreDir(ctx) // Ensure keystore directory is created - if err != nil { - return err - } - - cmd := []string{"cast", "wallet", "import", keyName, "--keystore-dir", c.KeystoreDir(), "--mnemonic", mnemonic, "--unsafe-password", ""} - _, _, err = c.Exec(ctx, cmd, nil) - if err != nil { - return err - } - - // This is needed for CreateKey() since that keystore path does not use the keyname - c.keystoreMap[keyName] = path.Join(c.KeystoreDir(), keyName) - - return nil -} - -// Get address of account, cast to a string to use -func (c *EthereumChain) GetAddress(ctx context.Context, keyName string) ([]byte, error) { - keystore, ok := c.keystoreMap[keyName] - if !ok { - return nil, fmt.Errorf("Keyname (%s) not found", keyName) - } - - cmd := []string{"cast", "wallet", "address", "--keystore", keystore, "--password", ""} - stdout, _, err := c.Exec(ctx, cmd, nil) - if err != nil { - return nil, err - } - return []byte(strings.TrimSpace(string(stdout))), nil -} - -func (c *EthereumChain) SendFunds(ctx context.Context, keyName string, amount ibc.WalletAmount) error { - cmd := []string{"cast", "send", amount.Address, "--value", amount.Amount.String()} - if keyName == "faucet" { - cmd = append(cmd, - "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - "--rpc-url", c.GetRPCAddress(), - ) - - } else { - keystore, ok := c.keystoreMap[keyName] - if !ok { - return fmt.Errorf("keyname (%s) not found", keyName) - } - cmd = append(cmd, - "--keystore", keystore, - "--password", "", - "--rpc-url", c.GetRPCAddress(), - ) - } - _, _, err := c.Exec(ctx, cmd, nil) - return err -} - -type TransactionReceipt struct { - TxHash string `json:"transactionHash"` -} - -func (c *EthereumChain) SendFundsWithNote(ctx context.Context, keyName string, amount ibc.WalletAmount, note string) (string, error) { - cmd := []string{"cast", "send", amount.Address, hexutil.Encode([]byte(note)), "--value", amount.Amount.String(), "--json"} - if keyName == "faucet" { - cmd = append(cmd, - "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - "--rpc-url", c.GetRPCAddress(), - ) - - } else { - keystore, ok := c.keystoreMap[keyName] - if !ok { - return "", fmt.Errorf("Keyname (%s) not found", keyName) - } - cmd = append(cmd, - "--keystore", keystore, - "--password", "", - "--rpc-url", c.GetRPCAddress(), - ) - } - stdout, _, err := c.Exec(ctx, cmd, nil) - if err != nil { - return "", err - } - - var txReceipt TransactionReceipt - if err = json.Unmarshal(stdout, &txReceipt); err != nil { - return "", err - } - - return txReceipt.TxHash, nil -} - func (c *EthereumChain) Height(ctx context.Context) (int64, error) { - cmd := []string{"cast", "block-number", "--rpc-url", c.GetRPCAddress()} - stdout, _, err := c.Exec(ctx, cmd, nil) + time.Sleep(time.Millisecond * 200) // TODO: slow down WaitForBlocks instead of here + height, err := c.rpcClient.BlockNumber(ctx) if err != nil { - return 0, err + return 0, fmt.Errorf("failed to get height: %w", err) } - return strconv.ParseInt(strings.TrimSpace(string(stdout)), 10, 64) + return int64(height), nil } func (c *EthereumChain) GetBalance(ctx context.Context, address string, denom string) (sdkmath.Int, error) { - cmd := []string{"cast", "balance", "--rpc-url", c.GetRPCAddress(), address} - stdout, _, err := c.Exec(ctx, cmd, nil) - if err != nil { - return sdkmath.ZeroInt(), err - } - balance, ok := sdkmath.NewIntFromString(strings.TrimSpace(string(stdout))) - if !ok { - return sdkmath.ZeroInt(), fmt.Errorf("Error parsing string to sdk int") - } - return balance, nil -} - -func (c *EthereumChain) BuildWallet(ctx context.Context, keyName string, mnemonic string) (ibc.Wallet, error) { - if mnemonic != "" { - err := c.RecoverKey(ctx, keyName, mnemonic) - if err != nil { - return nil, err - } - } else { - // Use the genesis account - if keyName == "faucet" { - // TODO: implement RecoverKey() so faucet can be saved to keystore - return c.genesisWallets.GetFaucetWallet(keyName), nil - } else { - // Create new account - err := c.CreateKey(ctx, keyName) - if err != nil { - return nil, err - } - } - } - - address, err := c.GetAddress(ctx, keyName) + balance, err := c.rpcClient.BalanceAt(ctx, common.Address(hexutil.MustDecode(address)), nil) if err != nil { - return nil, err + return sdkmath.Int{}, fmt.Errorf("failed to get height: %w", err) } - return NewWallet(keyName, string(address)), nil + return sdkmath.NewIntFromBigInt(balance), nil } diff --git a/chain/ethereum/foundry/anvil_chain.go b/chain/ethereum/foundry/anvil_chain.go new file mode 100644 index 000000000..d42b8c7ef --- /dev/null +++ b/chain/ethereum/foundry/anvil_chain.go @@ -0,0 +1,228 @@ +package foundry + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + "strings" + "sync" + + "github.com/docker/docker/api/types/mount" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "go.uber.org/zap" +) + +var _ ibc.Chain = &AnvilChain{} + +type AnvilChain struct { + *ethereum.EthereumChain + + keystoreMap map[string]*NodeWallet +} + +func NewAnvilChain(testName string, chainConfig ibc.ChainConfig, log *zap.Logger) *AnvilChain { + return &AnvilChain{ + EthereumChain: ethereum.NewEthereumChain(testName, chainConfig, log), + keystoreMap: make(map[string]*NodeWallet), + } +} + +func (c *AnvilChain) KeystoreDir() string { + return path.Join(c.HomeDir(), ".foundry", "keystores") +} + +func (c *AnvilChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { + cmd := []string{c.Config().Bin, + "--host", "0.0.0.0", // Anyone can call + "--no-cors", + "--gas-price", c.Config().GasPrices, + } + + cmd = append(cmd, c.Config().AdditionalStartArgs...) + + var mounts []mount.Mount + if loadState, ok := c.Config().ConfigFileOverrides["--load-state"].(string); ok { + pwd, err := os.Getwd() + if err != nil { + return err + } + localJsonFile := filepath.Join(pwd, loadState) + dockerJsonFile := path.Join(c.HomeDir(), path.Base(loadState)) + mounts = []mount.Mount{ + { + Type: mount.TypeBind, + Source: localJsonFile, + Target: dockerJsonFile, + }, + } + cmd = append(cmd, "--load-state", dockerJsonFile) + } + + return c.EthereumChain.Start(ctx, cmd, mounts) +} + +type NewWalletOutput struct { + Address string `json:"address"` + Path string `json:"path"` +} + +func (c *AnvilChain) MakeKeystoreDir(ctx context.Context) error { + cmd := []string{"mkdir", "-p", c.KeystoreDir()} + _, _, err := c.Exec(ctx, cmd, nil) + return err +} + +func (c *AnvilChain) CreateKey(ctx context.Context, keyName string) error { + err := c.MakeKeystoreDir(ctx) // Ensure keystore directory is created + if err != nil { + return err + } + + _, ok := c.keystoreMap[keyName] + if ok { + return fmt.Errorf("keyname (%s) already used", keyName) + } + + cmd := []string{"cast", "wallet", "new", c.KeystoreDir(), "--unsafe-password", "", "--json"} + stdout, _, err := c.Exec(ctx, cmd, nil) + if err != nil { + return err + } + + newWallet := []NewWalletOutput{} + err = json.Unmarshal(stdout, &newWallet) + if err != nil { + return err + } + + c.keystoreMap[keyName] = &NodeWallet{ + keystore: newWallet[0].Path, + } + + return nil +} + +func (c *AnvilChain) RecoverKey(ctx context.Context, keyName, mnemonic string) error { + err := c.MakeKeystoreDir(ctx) // Ensure keystore directory is created + if err != nil { + return err + } + + cmd := []string{"cast", "wallet", "import", keyName, "--keystore-dir", c.KeystoreDir(), "--mnemonic", mnemonic, "--unsafe-password", ""} + _, _, err = c.Exec(ctx, cmd, nil) + if err != nil { + return err + } + + // This is needed for CreateKey() since that keystore path does not use the keyname + c.keystoreMap[keyName] = &NodeWallet{ + keystore: path.Join(c.KeystoreDir(), keyName), + } + + return nil +} + +// Get address of account, cast to a string to use +func (c *AnvilChain) GetAddress(ctx context.Context, keyName string) ([]byte, error) { + account, ok := c.keystoreMap[keyName] + if !ok { + return nil, fmt.Errorf("keyname (%s) not found", keyName) + } + + if account.address != "" { + return hexutil.MustDecode(account.address), nil + } + + cmd := []string{"cast", "wallet", "address", "--keystore", account.keystore, "--password", ""} + stdout, _, err := c.Exec(ctx, cmd, nil) + if err != nil { + return nil, err + } + + addr := strings.TrimSpace(string(stdout)) + account.address = addr + return hexutil.MustDecode(addr), nil +} + +func (c *AnvilChain) SendFunds(ctx context.Context, keyName string, amount ibc.WalletAmount) error { + _, err := c.SendFundsWithNote(ctx, keyName, amount, "") + return err +} + +type TransactionReceipt struct { + TxHash string `json:"transactionHash"` +} + +func (c *AnvilChain) SendFundsWithNote(ctx context.Context, keyName string, amount ibc.WalletAmount, note string) (string, error) { + var cmd []string + if len(note) > 0 { + cmd = []string{"cast", "send", amount.Address, hexutil.Encode([]byte(note)), "--value", amount.Amount.String(), "--json"} + } else { + cmd = []string{"cast", "send", amount.Address, "--value", amount.Amount.String(), "--json"} + } + + account, ok := c.keystoreMap[keyName] + if !ok { + return "", fmt.Errorf("keyname (%s) not found", keyName) + } + cmd = append(cmd, + "--keystore", account.keystore, + "--password", "", + "--rpc-url", c.GetRPCAddress(), + ) + + account.txLock.Lock() + defer account.txLock.Unlock() + stdout, _, err := c.Exec(ctx, cmd, nil) + if err != nil { + return "", fmt.Errorf("send funds, exec, %w", err) + } + + var txReceipt TransactionReceipt + if err = json.Unmarshal([]byte(strings.TrimSpace(string(stdout))), &txReceipt); err != nil { + return "", fmt.Errorf("tx receipt unmarshal:\n %s\nerror: %w", string(stdout), err) + } + + return txReceipt.TxHash, nil +} + +func (c *AnvilChain) BuildWallet(ctx context.Context, keyName string, mnemonic string) (ibc.Wallet, error) { + if mnemonic != "" { + err := c.RecoverKey(ctx, keyName, mnemonic) + if err != nil { + return nil, err + } + } else { + // Use the genesis account + if keyName == "faucet" { + mnemonic = "test test test test test test test test test test test junk" + err := c.RecoverKey(ctx, keyName, mnemonic) + if err != nil { + return nil, err + } + } else { + // Create new account + err := c.CreateKey(ctx, keyName) + if err != nil { + return nil, err + } + } + } + + address, err := c.GetAddress(ctx, keyName) + if err != nil { + return nil, err + } + return ethereum.NewWallet(keyName, address, mnemonic), nil +} + +type NodeWallet struct { + address string + keystore string + txLock sync.Mutex +} diff --git a/chain/ethereum/foundry/default_configs.go b/chain/ethereum/foundry/default_configs.go new file mode 100644 index 000000000..498f132ae --- /dev/null +++ b/chain/ethereum/foundry/default_configs.go @@ -0,0 +1,36 @@ +package foundry + +import ( + "github.com/strangelove-ventures/interchaintest/v8/ibc" +) + +func DefaultEthereumAnvilChainConfig( + name string, +) ibc.ChainConfig { + return ibc.ChainConfig{ + Type: "ethereum", + Name: name, + ChainID: "31337", // default anvil chain-id + Bech32Prefix: "n/a", + CoinType: "60", + Denom: "wei", + GasPrices: "20000000000", // 20 gwei + GasAdjustment: 0, + TrustingPeriod: "0", + NoHostMount: false, + Images: []ibc.DockerImage{ + { + Repository: "ghcr.io/foundry-rs/foundry", + Version: "latest", + UidGid: "1000:1000", + }, + }, + Bin: "anvil", + AdditionalStartArgs: []string{ + "--block-time", "2", // 2 second block times + "--accounts", "10", // We current only use the first account for the faucet, but tests may expect the default + "--balance", "10000000", // Genesis accounts loaded with 10mil ether, change as needed + "--block-base-fee-per-gas", "0", + }, + } +} diff --git a/chain/ethereum/forge.go b/chain/ethereum/foundry/forge.go similarity index 83% rename from chain/ethereum/forge.go rename to chain/ethereum/foundry/forge.go index 0de826523..a37084e83 100644 --- a/chain/ethereum/forge.go +++ b/chain/ethereum/foundry/forge.go @@ -1,7 +1,8 @@ -package ethereum +package foundry import ( "context" + "fmt" "os" "path" "path/filepath" @@ -21,19 +22,15 @@ type ForgeScriptOpts struct { } // Add private-key or keystore to cmd -func (c *EthereumChain) AddKey(cmd []string, keyName string) []string { - // choose whether to use private-key or keystore - if keyName == "faucet" { - cmd = append(cmd, - "--private-key", "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", - ) - - } else { - cmd = append(cmd, - "--keystores", c.keystoreMap[keyName], - "--password", "", - ) +func (c *AnvilChain) AddKey(cmd []string, keyName string) []string { + account, ok := c.keystoreMap[keyName] + if !ok { + panic(fmt.Sprintf("Keyname (%s) not found", keyName)) } + cmd = append(cmd, + "--keystores", account.keystore, + "--password", "", + ) return cmd } @@ -78,7 +75,13 @@ func WriteConfigFile(configFile string, localContractRootDir string, solidityCon // Run "forge script" // see: https://book.getfoundry.sh/reference/forge/forge-script -func (c *EthereumChain) ForgeScript(ctx context.Context, keyName string, opts ForgeScriptOpts) (stdout, stderr []byte, err error) { +func (c *AnvilChain) ForgeScript(ctx context.Context, keyName string, opts ForgeScriptOpts) (stdout, stderr []byte, err error) { + account, ok := c.keystoreMap[keyName] + if !ok { + return nil, nil, fmt.Errorf("keyname (%s) not found", keyName) + } + account.txLock.Lock() + defer account.txLock.Unlock() pwd, err := os.Getwd() if err != nil { return nil, nil, err @@ -96,7 +99,7 @@ func (c *EthereumChain) ForgeScript(ctx context.Context, keyName string, opts Fo return nil, nil, err } - job := dockerutil.NewImage(c.logger(), c.DockerClient, c.NetworkID, c.testName, c.cfg.Images[0].Repository, c.cfg.Images[0].Version) + job := c.NewJob() containerOpts := dockerutil.ContainerOptions{ Binds: c.Bind(), Mounts: []mount.Mount{ diff --git a/chain/ethereum/geth/default_configs.go b/chain/ethereum/geth/default_configs.go new file mode 100644 index 000000000..a38235c8f --- /dev/null +++ b/chain/ethereum/geth/default_configs.go @@ -0,0 +1,78 @@ +package geth + +import ( + "github.com/strangelove-ventures/interchaintest/v8/ibc" +) + +func DefaultEthereumGethChainConfig( + name string, +) ibc.ChainConfig { + return ibc.ChainConfig{ + Type: "ethereum", + Name: name, + ChainID: "1337", // default geth chain-id + Bech32Prefix: "n/a", + CoinType: "60", + Denom: "wei", + GasPrices: "2000000000", // 2gwei, default 1M + GasAdjustment: 0, + TrustingPeriod: "0", + NoHostMount: false, + Images: []ibc.DockerImage{ + { + Repository: "ethereum/client-go", + Version: "v1.14.7", + UidGid: "1025:1025", + }, + }, + Bin: "geth", + AdditionalStartArgs: []string{ + "--dev.period", "2", // 2 second block time + "--verbosity", "4", // Level = debug + "--networkid", "15", + "--rpc.txfeecap", "50.0", // 50 eth + "--rpc.gascap", "30000000", //30M + "--gpo.percentile", "150", // default 60 + "--gpo.ignoreprice", "1000000000", // 1gwei, default 2 + "--dev.gaslimit", "30000000", // 30M, default 11.5M + "--rpc.enabledeprecatedpersonal", // required (in this version) for recover key and unlocking accounts + }, + } +} + +func DefaultBscChainConfig( + name string, +) ibc.ChainConfig { + return ibc.ChainConfig{ + Type: "ethereum", + Name: name, + ChainID: "11337", + Bech32Prefix: "n/a", + CoinType: "60", + Denom: "wei", + GasPrices: "2000000000", // 2gwei, default 1M + GasAdjustment: 0, + TrustingPeriod: "0", + NoHostMount: false, + Images: []ibc.DockerImage{ + { + Repository: "ghcr.io/bnb-chain/bsc", + Version: "1.2.13", // same version as other sim tests + //Version: "1.4.13", // this version does not work in dev mode (1.3.x+) + UidGid: "1000:1000", + }, + }, + Bin: "geth", + AdditionalStartArgs: []string{ + "-mine", + "--dev.period", "2", // 2 second block time + "--verbosity", "4", // Level = debug + "--networkid", "15", + "--rpc.txfeecap", "50.0", // 50 eth + "--rpc.gascap", "30000000", //30M + "--gpo.percentile", "150", // default 60 + "--gpo.ignoreprice", "1000000000", // 1gwei, default 2 + "--dev.gaslimit", "30000000", // 30M, default 11.5M + }, + } +} diff --git a/chain/ethereum/geth/geth_chain.go b/chain/ethereum/geth/geth_chain.go new file mode 100644 index 000000000..cf9538870 --- /dev/null +++ b/chain/ethereum/geth/geth_chain.go @@ -0,0 +1,266 @@ +package geth + +import ( + "context" + "encoding/hex" + "fmt" + "strings" + "sync" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/hd" + + "github.com/docker/docker/api/types/mount" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + "github.com/strangelove-ventures/interchaintest/v8/testutil" + "go.uber.org/zap" +) + +var _ ibc.Chain = &GethChain{} + +type GethChain struct { + *ethereum.EthereumChain + + keynameToAccountMap map[string]*NodeWallet + nextAcctNum int +} + +func NewGethChain(testName string, chainConfig ibc.ChainConfig, log *zap.Logger) *GethChain { + return &GethChain{ + EthereumChain: ethereum.NewEthereumChain(testName, chainConfig, log), + keynameToAccountMap: map[string]*NodeWallet{ + "faucet": { + accountNum: 0, + }, + }, + nextAcctNum: 1, + } +} + +func (c *GethChain) Start(testName string, ctx context.Context, additionalGenesisWallets ...ibc.WalletAmount) error { + cmd := []string{c.Config().Bin, + "--dev", "--datadir", c.HomeDir(), "-http", "--http.addr", "0.0.0.0", "--http.port", "8545", "--allow-insecure-unlock", + "--http.api", "eth,net,web3,miner,personal,txpool,debug", "--http.corsdomain", "*", "-nodiscover", "--http.vhosts=*", + "--miner.gasprice", c.Config().GasPrices, + "--rpc.allow-unprotected-txs", + } + + cmd = append(cmd, c.Config().AdditionalStartArgs...) + + return c.EthereumChain.Start(ctx, cmd, []mount.Mount{}) +} + +// JavaScriptExec() - Execute web3 code via geth's attach command +func (c *GethChain) JavaScriptExec(ctx context.Context, jsCmd string) (stdout, stderr []byte, err error) { + cmd := []string{c.Config().Bin, "--exec", jsCmd, "--datadir", c.HomeDir(), "attach"} + return c.Exec(ctx, cmd, nil) +} + +// JavaScriptExecTx() - Execute a tx via web3, function ensures account is unlocked and blocks multiple txs +func (c *GethChain) JavaScriptExecTx(ctx context.Context, account *NodeWallet, jsCmd string) (stdout, stderr []byte, err error) { + if err := c.UnlockAccount(ctx, account); err != nil { + return nil, nil, err + } + + account.txLock.Lock() + defer account.txLock.Unlock() + stdout, stderr, err = c.JavaScriptExec(ctx, jsCmd) + if err != nil { + return nil, nil, err + } + + err = testutil.WaitForBlocks(ctx, 2, c) + return stdout, stderr, err +} + +func (c *GethChain) CreateKey(ctx context.Context, keyName string) error { + _, ok := c.keynameToAccountMap[keyName] + if ok { + return fmt.Errorf("keyname (%s) already used", keyName) + } + + cmd := []string{ + "sh", + "-c", + fmt.Sprintf(`cat < 3 { + return nil, fmt.Errorf("getAddress(): Keyname (%s) with account (%d) not found", + keyName, account.accountNum) + } + } + + return hexutil.MustDecode(strings.Trim(strings.TrimSpace(string(stdout)), "\"")), nil +} + +// UnlockAccount() unlocks a non-default account for use. We will unlock when sending funds and deploying contracts. +// Accounts are unlocked for 100 seconds which is plenty of time for the transaction. +func (c *GethChain) UnlockAccount(ctx context.Context, account *NodeWallet) error { + // shouldn't need to unlock the default account + if account.accountNum == 0 { + return nil + } + + _, _, err := c.JavaScriptExec(ctx, fmt.Sprintf("personal.unlockAccount(eth.accounts[%d], \"\", 100)", account.accountNum)) + + return err +} + +func (c *GethChain) SendFunds(ctx context.Context, keyName string, amount ibc.WalletAmount) error { + _, err := c.SendFundsWithNote(ctx, keyName, amount, "") + return err +} + +func (c *GethChain) SendFundsWithNote(ctx context.Context, keyName string, amount ibc.WalletAmount, note string) (string, error) { + account, found := c.keynameToAccountMap[keyName] + if !found { + return "", fmt.Errorf("keyname (%s) not found", keyName) + } + + var cmd string + if len(note) > 0 { + cmd = fmt.Sprintf("eth.sendTransaction({from: eth.accounts[%d],to: %q,value: %s,data: \"%s\"});", + account.accountNum, amount.Address, amount.Amount, hexutil.Encode([]byte(note))) + } else { + cmd = fmt.Sprintf("eth.sendTransaction({from: eth.accounts[%d],to: %q,value: %s});", + account.accountNum, amount.Address, amount.Amount) + } + stdout, _, err := c.JavaScriptExecTx(ctx, account, cmd) + if err != nil { + return "", err + } + return strings.Trim(strings.TrimSpace(string(stdout)), "\""), nil +} + +// DeployContract creates a new contract on-chain, returning the contract address +// Constructor params are appended to the byteCode +func (c *GethChain) DeployContract(ctx context.Context, keyName string, abi []byte, byteCode []byte) (string, error) { + account, found := c.keynameToAccountMap[keyName] + if !found { + return "", fmt.Errorf("SendFundsWithNote(): Keyname (%s) not found", keyName) + } + + stdout, _, err := c.JavaScriptExecTx(ctx, account, + fmt.Sprintf("eth.contract(abi=%s).new({from: eth.accounts[%d], data: %q, gas: 20000000}).transactionHash", + abi, account.accountNum, byteCode), + ) + if err != nil { + return "", err + } + + txHash := strings.TrimSpace(string(stdout)) + status, _, err := c.JavaScriptExec(ctx, fmt.Sprintf("eth.getTransactionReceipt(%s).status", txHash)) + if err != nil { + return "", err + } + + if strings.Trim(strings.TrimSpace(string(status)), "\"") == "0x0" { + return "", fmt.Errorf("contract deployment failed") + } + + stdout, _, err = c.JavaScriptExec(ctx, fmt.Sprintf("eth.getTransactionReceipt(%s).contractAddress", txHash)) + if err != nil { + return "", err + } + + return strings.Trim(strings.TrimSpace(string(stdout)), "\""), nil +} + +func (c *GethChain) BuildWallet(ctx context.Context, keyName string, mnemonic string) (ibc.Wallet, error) { + if mnemonic != "" { + err := c.RecoverKey(ctx, keyName, mnemonic) + if err != nil { + return nil, err + } + } else { + // faucet is created when the chain starts and will be account #0 + if keyName == "faucet" { + return ethereum.NewWallet(keyName, []byte{}, mnemonic), nil + } else { + // Create new account + err := c.CreateKey(ctx, keyName) + if err != nil { + return nil, err + } + } + } + + address, err := c.GetAddress(ctx, keyName) + if err != nil { + return nil, err + } + return ethereum.NewWallet(keyName, address, mnemonic), nil +} + +type NodeWallet struct { + accountNum int + address string + txLock sync.Mutex +} diff --git a/chain/ethereum/wallet.go b/chain/ethereum/wallet.go index 385da7343..86e6688a7 100644 --- a/chain/ethereum/wallet.go +++ b/chain/ethereum/wallet.go @@ -8,14 +8,16 @@ import ( var _ ibc.Wallet = &EthereumWallet{} type EthereumWallet struct { - address string - keyName string + address []byte + keyName string + mnemonic string } -func NewWallet(keyname string, address string) ibc.Wallet { +func NewWallet(keyname string, address []byte, mnemonic string) ibc.Wallet { return &EthereumWallet{ - address: address, - keyName: keyname, + address: address, + keyName: keyname, + mnemonic: mnemonic, } } @@ -25,29 +27,15 @@ func (w *EthereumWallet) KeyName() string { // Get formatted address, passing in a prefix func (w *EthereumWallet) FormattedAddress() string { - return w.address + return hexutil.Encode(w.address) } // Get mnemonic, only used for relayer wallets func (w *EthereumWallet) Mnemonic() string { - return "" + return w.mnemonic } // Get Address with chain's prefix func (w *EthereumWallet) Address() []byte { - return hexutil.MustDecode(w.address) -} - -type GenesisWallets struct { - total uint32 -} - -func NewGenesisWallet() GenesisWallets { - return GenesisWallets{ - total: 2, // Start with 2 at genesis, one for faucet, one for relayer - } -} - -func (w *GenesisWallets) GetFaucetWallet(keyname string) ibc.Wallet { - return NewWallet(keyname, "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") + return w.address } diff --git a/chain/thorchain/thornode.go b/chain/thorchain/thornode.go index 06c9134c7..b7c8d9309 100644 --- a/chain/thorchain/thornode.go +++ b/chain/thorchain/thornode.go @@ -395,6 +395,7 @@ func (tn *ChainNode) SetPeers(ctx context.Context, peers string) error { } func (tn *ChainNode) Height(ctx context.Context) (int64, error) { + time.Sleep(time.Millisecond * 200) // TODO: slow down WaitForBlocks instead of here res, err := tn.Client.Status(ctx) if err != nil { return 0, fmt.Errorf("tendermint rpc client status: %w", err) diff --git a/chain/utxo/cli.go b/chain/utxo/cli.go index d5b701a8d..84138c22f 100644 --- a/chain/utxo/cli.go +++ b/chain/utxo/cli.go @@ -185,7 +185,7 @@ func (c *UtxoChain) SetAccount(ctx context.Context, addr string, keyName string) // sendToMwebAddress is used for creating the mweb transaction needed at block 431 // no other use case is currently supported func (c *UtxoChain) sendToMwebAddress(ctx context.Context, keyName string, addr string, amount float64) error { - _, err := c.getWalletForUse(keyName) + wallet, err := c.getWalletForUse(keyName) if err != nil { return err } @@ -200,7 +200,9 @@ func (c *UtxoChain) sendToMwebAddress(ctx context.Context, keyName string, addr fmt.Sprintf("amount=%.8f", amount), ) + wallet.txLock.Lock() _, _, err = c.Exec(ctx, cmd, nil) + wallet.txLock.Unlock() if err != nil { return err } diff --git a/chain/utxo/default_configs.go b/chain/utxo/default_configs.go index 45af5e7f3..cd42df4d3 100644 --- a/chain/utxo/default_configs.go +++ b/chain/utxo/default_configs.go @@ -19,7 +19,7 @@ func DefaultBitcoinChainConfig( CoinType: "0", Denom: "sat", GasPrices: "0.00001", // min fee / kb - GasAdjustment: 2, // min fee multiplier + GasAdjustment: 4, // min fee multiplier TrustingPeriod: "0", NoHostMount: false, Images: []ibc.DockerImage{ @@ -52,7 +52,7 @@ func DefaultBitcoinCashChainConfig( CoinType: "145", Denom: "sat", GasPrices: "0.00001", // min fee / kb - GasAdjustment: 2, // min fee multiplier + GasAdjustment: 4, // min fee multiplier TrustingPeriod: "0", NoHostMount: false, Images: []ibc.DockerImage{ @@ -85,7 +85,7 @@ func DefaultLitecoinChainConfig( CoinType: "2", Denom: "sat", GasPrices: "0.0001", // min fee / kb - GasAdjustment: 2, // min fee multiplier + GasAdjustment: 4, // min fee multiplier TrustingPeriod: "0", NoHostMount: false, Images: []ibc.DockerImage{ @@ -118,7 +118,7 @@ func DefaultDogecoinChainConfig( CoinType: "3", Denom: "sat", GasPrices: "0.01", // min fee / kb - GasAdjustment: 2, // min fee multiplier + GasAdjustment: 4, // min fee multiplier TrustingPeriod: "0", NoHostMount: false, Images: []ibc.DockerImage{ diff --git a/chain/utxo/utxo_chain.go b/chain/utxo/utxo_chain.go index 17d285555..bc981ce73 100644 --- a/chain/utxo/utxo_chain.go +++ b/chain/utxo/utxo_chain.go @@ -415,6 +415,13 @@ func (c *UtxoChain) SendFundsWithNote(ctx context.Context, keyName string, amoun return "", err } + wallet, err := c.getWalletForUse(keyName) + if err != nil { + return "", err + } + wallet.txLock.Lock() + defer wallet.txLock.Unlock() + // get utxo listUtxo, err := c.ListUnspent(ctx, keyName) if err != nil { @@ -447,6 +454,7 @@ func (c *UtxoChain) SendFundsWithNote(ctx context.Context, keyName string, amoun } func (c *UtxoChain) Height(ctx context.Context) (int64, error) { + time.Sleep(time.Millisecond * 200) // TODO: slow down WaitForBlocks instead of here cmd := append(c.BaseCli, "getblockcount") stdout, _, err := c.Exec(ctx, cmd, nil) if err != nil { diff --git a/chain/utxo/wallet.go b/chain/utxo/wallet.go index 26c00d2b0..3cacf32e0 100644 --- a/chain/utxo/wallet.go +++ b/chain/utxo/wallet.go @@ -44,6 +44,7 @@ type NodeWallet struct { keyName string address string mu sync.Mutex + txLock sync.Mutex loadCount int ready bool } diff --git a/chainfactory.go b/chainfactory.go index fef1e1e0a..124b8a19d 100644 --- a/chainfactory.go +++ b/chainfactory.go @@ -8,7 +8,8 @@ import ( "sync" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/foundry" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/geth" "github.com/strangelove-ventures/interchaintest/v8/chain/penumbra" "github.com/strangelove-ventures/interchaintest/v8/chain/polkadot" "github.com/strangelove-ventures/interchaintest/v8/chain/thorchain" @@ -157,7 +158,14 @@ func buildChain(log *zap.Logger, testName string, cfg ibc.ChainConfig, numValida return nil, fmt.Errorf("unexpected error, unknown polkadot parachain: %s", cfg.Name) } case "ethereum": - return ethereum.NewEthereumChain(testName, cfg, log), nil + switch cfg.Bin { + case "anvil": + return foundry.NewAnvilChain(testName, cfg, log), nil + case "geth": + return geth.NewGethChain(testName, cfg, log), nil + default: + return nil, fmt.Errorf("unknown binary: %s for ethereum chain type, must be anvil or geth", cfg.Bin) + } case "thorchain": return thorchain.NewThorchain(testName, cfg, nv, nf, log), nil case "utxo": diff --git a/examples/ethereum/start_test.go b/examples/ethereum/foundry_test.go similarity index 94% rename from examples/ethereum/start_test.go rename to examples/ethereum/foundry_test.go index 7e4278dc0..12a38cca5 100644 --- a/examples/ethereum/start_test.go +++ b/examples/ethereum/foundry_test.go @@ -9,6 +9,7 @@ import ( "cosmossdk.io/math" "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/foundry" "github.com/strangelove-ventures/interchaintest/v8/ibc" "github.com/strangelove-ventures/interchaintest/v8/testreporter" @@ -16,7 +17,7 @@ import ( "go.uber.org/zap/zaptest" ) -func TestEthereum(t *testing.T) { +func TestFoundry(t *testing.T) { if testing.Short() { t.Skip() @@ -36,7 +37,7 @@ func TestEthereum(t *testing.T) { ctx := context.Background() // Get default ethereum chain config for anvil - anvilConfig := ethereum.DefaultEthereumAnvilChainConfig("ethereum") + anvilConfig := foundry.DefaultEthereumAnvilChainConfig("ethereum") // add --load-state config (this step is not required for tests that don't require an existing state) configFileOverrides := make(map[string]any) @@ -55,7 +56,7 @@ func TestEthereum(t *testing.T) { chains, err := cf.Chains(t.Name()) require.NoError(t, err) - ethereumChain := chains[0].(*ethereum.EthereumChain) + ethereumChain := chains[0].(*foundry.AnvilChain) ic := interchaintest.NewInterchain(). AddChain(ethereumChain) diff --git a/examples/ethereum/geth_test.go b/examples/ethereum/geth_test.go new file mode 100644 index 000000000..e3232fdc0 --- /dev/null +++ b/examples/ethereum/geth_test.go @@ -0,0 +1,95 @@ +package ethereum_test + +import ( + "context" + + "fmt" + "strings" + "testing" + "time" + + "github.com/strangelove-ventures/interchaintest/v8" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/geth" + "github.com/strangelove-ventures/interchaintest/v8/ibc" + + "github.com/strangelove-ventures/interchaintest/v8/testreporter" + "github.com/stretchr/testify/require" + "go.uber.org/zap/zaptest" +) + +func TestGeth(t *testing.T) { + + if testing.Short() { + t.Skip() + } + + t.Parallel() + + client, network := interchaintest.DockerSetup(t) + + // Log location + f, err := interchaintest.CreateLogFile(fmt.Sprintf("%d.json", time.Now().Unix())) + require.NoError(t, err) + // Reporter/logs + rep := testreporter.NewReporter(f) + eRep := rep.RelayerExecReporter(t) + + ctx := context.Background() + + // Get default ethereum chain config for geth + gethConfig := geth.DefaultEthereumGethChainConfig("ethereum") + + cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ + {ChainConfig: gethConfig}, + }) + + chains, err := cf.Chains(t.Name()) + require.NoError(t, err) + + ethereumChain := chains[0].(*geth.GethChain) + + ic := interchaintest.NewInterchain(). + AddChain(ethereumChain) + + require.NoError(t, ic.Build(ctx, eRep, interchaintest.InterchainBuildOptions{ + TestName: t.Name(), + Client: client, + NetworkID: network, + SkipPathCreation: true, // Skip path creation, so we can have granular control over the process + })) + fmt.Println("Interchain built") + + // Create and fund a user using GetAndFundTestUsers + ethUserInitialAmount := ethereum.ETHER.MulRaw(1000) + users := interchaintest.GetAndFundTestUsers(t, ctx, "user", ethUserInitialAmount, ethereumChain) + ethUser := users[0] + + // Check balances of user + balance, err := ethereumChain.GetBalance(ctx, ethUser.FormattedAddress(), "") + require.NoError(t, err) + require.True(t, balance.Equal(ethUserInitialAmount)) + + ethUser2, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, "user2", strings.Repeat("dog ", 23)+"fossil", ethUserInitialAmount, ethereumChain) + require.NoError(t, err) + + balance, err = ethereumChain.GetBalance(ctx, ethUser2.FormattedAddress(), "") + require.NoError(t, err) + require.True(t, balance.Equal(ethUserInitialAmount)) + + txHash, err := ethereumChain.SendFundsWithNote(ctx, ethUser2.KeyName(), ibc.WalletAmount{ + Address: ethUser.FormattedAddress(), + Amount: ethUserInitialAmount.QuoRaw(10), + Denom: ethereumChain.Config().Denom, + }, "memo") + require.NoError(t, err) + require.NotEmpty(t, txHash) + + balance, err = ethereumChain.GetBalance(ctx, ethUser.FormattedAddress(), "") + require.NoError(t, err) + require.True(t, balance.Equal(ethUserInitialAmount.Add(ethUserInitialAmount.QuoRaw(10)))) +} + +type ContractOutput struct { + TxHash string `json:"transactionHash"` +} diff --git a/examples/thorchain/chainspec_exochains.go b/examples/thorchain/chainspec_exochains.go index 4b98f1ba5..002889e76 100644 --- a/examples/thorchain/chainspec_exochains.go +++ b/examples/thorchain/chainspec_exochains.go @@ -3,7 +3,8 @@ package thorchain_test import ( "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/foundry" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/geth" "github.com/strangelove-ventures/interchaintest/v8/chain/thorchain/common" "github.com/strangelove-ventures/interchaintest/v8/chain/utxo" "github.com/strangelove-ventures/interchaintest/v8/ibc" @@ -15,7 +16,6 @@ type ExoChain struct { chain ibc.Chain lpers []ibc.Wallet savers []ibc.Wallet - arbers []ibc.Wallet genWallets []ibc.Wallet } @@ -58,14 +58,28 @@ func GaiaChainSpec() *interchaintest.ChainSpec { } } -func EthChainSpec() *interchaintest.ChainSpec { +func EthChainSpec(chainType string) *interchaintest.ChainSpec { ethChainName := common.ETHChain.String() // must use this name for test + chainConfig := geth.DefaultEthereumGethChainConfig(ethChainName) + if chainType == "anvil" { + chainConfig = foundry.DefaultEthereumAnvilChainConfig(ethChainName) + } + return &interchaintest.ChainSpec{ ChainName: ethChainName, Name: ethChainName, - Version: "latest", - ChainConfig: ethereum.DefaultEthereumAnvilChainConfig(ethChainName), + ChainConfig: chainConfig, + } +} + +func BscChainSpec() *interchaintest.ChainSpec { + bscChainName := common.BSCChain.String() // must use this name for test + + return &interchaintest.ChainSpec{ + ChainName: bscChainName, + Name: bscChainName, + ChainConfig: geth.DefaultBscChainConfig(bscChainName), } } @@ -75,7 +89,6 @@ func BtcChainSpec() *interchaintest.ChainSpec { return &interchaintest.ChainSpec{ ChainName: btcChainName, Name: btcChainName, - Version: "26.2", ChainConfig: utxo.DefaultBitcoinChainConfig(btcChainName, "thorchain", "password"), } } @@ -86,7 +99,6 @@ func BchChainSpec() *interchaintest.ChainSpec { return &interchaintest.ChainSpec{ ChainName: bchChainName, Name: bchChainName, - Version: "27.1.0", ChainConfig: utxo.DefaultBitcoinCashChainConfig(bchChainName, "thorchain", "password"), } } @@ -97,7 +109,6 @@ func LtcChainSpec() *interchaintest.ChainSpec { return &interchaintest.ChainSpec{ ChainName: liteChainName, Name: liteChainName, - Version: "0.21", ChainConfig: utxo.DefaultLitecoinChainConfig(liteChainName, "thorchain", "password"), } } @@ -108,7 +119,6 @@ func DogeChainSpec() *interchaintest.ChainSpec { return &interchaintest.ChainSpec{ ChainName: dogeChainName, Name: dogeChainName, - Version: "dogecoin-daemon-1.14.7", ChainConfig: utxo.DefaultDogecoinChainConfig(dogeChainName, "thorchain", "password"), } } diff --git a/examples/thorchain/chainspec_thorchain.go b/examples/thorchain/chainspec_thorchain.go index 50095487c..de750d711 100644 --- a/examples/thorchain/chainspec_thorchain.go +++ b/examples/thorchain/chainspec_thorchain.go @@ -25,20 +25,23 @@ type ChainContract struct { Router string `json:"router"` } -func ThorchainDefaultChainSpec(testName string, numVals int, numFn int, ethRouter string, thornodeEnvOverrides, bifrostEnvOverrides map[string]string) *interchaintest.ChainSpec { +func ThorchainDefaultChainSpec(testName string, numVals int, numFn int, ethRouter string, bscRouter string, thornodeEnvOverrides, bifrostEnvOverrides map[string]string) *interchaintest.ChainSpec { chainID := "thorchain" name := common.THORChain.String() // Must use this name for test chainImage := ibc.NewDockerImage("thorchain", "local", "1025:1025") genesisKVMods := []thorchain.GenesisKV{ thorchain.NewGenesisKV("app_state.bank.params.default_send_enabled", false), // disable bank module transfers - thorchain.NewGenesisKV("app_state.transfer.params.send_enabled", false), // disable ibc transfer sends thorchain.NewGenesisKV("app_state.thorchain.reserve", "22000000000000000"), // mint to reserve for mocknet (220M) thorchain.NewGenesisKV("app_state.thorchain.chain_contracts", []ChainContract{ { Chain: "ETH", Router: ethRouter, }, - }), // mint to reserve for mocknet (220M) + { + Chain: "BSC", + Router: bscRouter, + }, + }), } thornodeEnv := thornodeDefaults @@ -93,7 +96,7 @@ func ThorchainDefaultChainSpec(testName string, numVals int, numFn int, ethRoute HomeDir: "/var/data/bifrost", Ports: []string{"5040", "6040", "9000"}, //StartCmd: []string{"bifrost", "-p"}, - StartCmd: []string{"bifrost", "-p", "-l", "debug"}, + StartCmd: []string{"bifrost", "-p", "-l", "debug"}, Env: bifrostEnv, PreStart: false, ValidatorProcess: true, @@ -162,11 +165,6 @@ var ( "BIFROST_CHAINS_AVAX_BLOCK_SCANNER_MAX_GAS_LIMIT=80000", "BIFROST_CHAINS_BSC_BLOCK_SCANNER_MAX_GAS_LIMIT=80000", - // enable bsc - //"BIFROST_CHAINS_BSC_DISABLED=false", // todo change to false once brought in - //"BIFROST_CHAINS_BSC_RPC_HOST: ${BSC_HOST:-http://binance-smart:8545} - //"BIFROST_CHAINS_BSC_BLOCK_SCANNER_RPC_HOST: ${BSC_HOST:-http://binance-smart:8545} - // set fixed gas rate for evm chains "BIFROST_CHAINS_ETH_BLOCK_SCANNER_FIXED_GAS_RATE=30000000000", // 30 gwei "BIFROST_CHAINS_AVAX_BLOCK_SCANNER_FIXED_GAS_RATE=100_000_000_000", // 100 navax diff --git a/examples/thorchain/contracts/eth-router-abi.json b/examples/thorchain/contracts/eth-router-abi.json new file mode 100644 index 000000000..17026c0bb --- /dev/null +++ b/examples/thorchain/contracts/eth-router-abi.json @@ -0,0 +1,308 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "rune", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVault", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferAllowance", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferOut", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "finalAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferOutAndCall", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVault", + "type": "address" + }, + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "indexed": false, + "internalType": "struct THORChain_Router.Coin[]", + "name": "coins", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "VaultTransfer", + "type": "event" + }, + { + "inputs": [], + "name": "RUNE", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "vault", "type": "address" }, + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "memo", "type": "string" } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "vault", "type": "address" }, + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "memo", "type": "string" }, + { "internalType": "uint256", "name": "expiration", "type": "uint256" } + ], + "name": "depositWithExpiry", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "router", "type": "address" }, + { + "internalType": "address payable", + "name": "asgard", + "type": "address" + }, + { + "components": [ + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "internalType": "struct THORChain_Router.Coin[]", + "name": "coins", + "type": "tuple[]" + }, + { "internalType": "string", "name": "memo", "type": "string" } + ], + "name": "returnVaultAssets", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "address", "name": "newVault", "type": "address" }, + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "memo", "type": "string" } + ], + "name": "transferAllowance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address payable", "name": "to", "type": "address" }, + { "internalType": "address", "name": "asset", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "string", "name": "memo", "type": "string" } + ], + "name": "transferOut", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "target", + "type": "address" + }, + { "internalType": "address", "name": "finalToken", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amountOutMin", "type": "uint256" }, + { "internalType": "string", "name": "memo", "type": "string" } + ], + "name": "transferOutAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "vault", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "vaultAllowance", + "outputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/examples/thorchain/contracts/eth-router-bytecode.txt b/examples/thorchain/contracts/eth-router-bytecode.txt new file mode 100644 index 000000000..ce3557b53 --- /dev/null +++ b/examples/thorchain/contracts/eth-router-bytecode.txt @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b5060405161149e38038061149e83398101604081905261002f91610059565b600080546001600160a01b0319166001600160a01b03929092169190911790556001600255610089565b60006020828403121561006b57600080fd5b81516001600160a01b038116811461008257600080fd5b9392505050565b611406806100986000396000f3fe60806040526004361061007b5760003560e01c80634039fd4b1161004e5780634039fd4b1461012157806344bc937b14610134578063574da7171461014757806393e4eaa91461015a57600080fd5b806303b6a673146100805780631b738b32146100d95780631fece7b4146100fb5780632923e82e1461010e575b600080fd5b34801561008c57600080fd5b506100c661009b366004610d91565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6040519081526020015b60405180910390f35b3480156100e557600080fd5b506100f96100f4366004610eaa565b610192565b005b6100f9610109366004610f29565b610248565b6100f961011c366004610f95565b61048d565b6100f961012f366004610eaa565b610611565b6100f96101423660046110a8565b61079d565b6100f9610155366004610f29565b6107ff565b34801561016657600080fd5b5060005461017a906001600160a01b031681565b6040516001600160a01b0390911681526020016100d0565b60028054036101bc5760405162461bcd60e51b81526004016101b39061111c565b60405180910390fd5b60028055306001600160a01b0386160361022f576101db8484846109ee565b836001600160a01b0316336001600160a01b03167f05b90458f953d3fcb2d7fb25616a2fddeca749d0c47cc5c9832d0266b5346eea858585604051610222939291906111af565b60405180910390a361023c565b61023c8585858585610a67565b50506001600255505050565b60028054036102695760405162461bcd60e51b81526004016101b39061111c565b6002805560006001600160a01b0384166102b9575060405134906000906001600160a01b0387169083156108fc0290849084818181858888f193505050509050806102b357600080fd5b50610434565b34156102f85760405162461bcd60e51b815260206004820152600e60248201526d0eadccaf0e0cac6e8cac840cae8d60931b60448201526064016101b3565b6000546001600160a01b03908116908516036103e857506000546040516302ccb1b360e41b81523060048201526024810184905283916001600160a01b031690632ccb1b30906044016020604051808303816000875af1158015610360573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061038491906111df565b50600054604051630852cd8d60e31b8152600481018590526001600160a01b03909116906342966c6890602401600060405180830381600087803b1580156103cb57600080fd5b505af11580156103df573d6000803e3d6000fd5b50505050610434565b6103f28484610bb8565b6001600160a01b03808716600090815260016020908152604080832093891683529290529081208054929350839290919061042e90849061121e565b90915550505b836001600160a01b0316856001600160a01b03167fef519b7eb82aaf6ac376a6df2d793843ebfd593de5f1a0601d3cc6ab49ebb3958385604051610479929190611236565b60405180910390a350506001600255505050565b60028054036104ae5760405162461bcd60e51b81526004016101b39061111c565b60028055306001600160a01b038516036105785760005b825181101561052557610513848483815181106104e4576104e4611257565b60200260200101516000015185848151811061050257610502611257565b6020026020010151602001516109ee565b8061051d8161126d565b9150506104c5565b50826001600160a01b0316336001600160a01b03167f281daef48d91e5cd3d32db0784f6af69cd8d8d2e8c612a3568dca51ded51e08f848460405161056b929190611286565b60405180910390a36105df565b60005b82518110156105dd576105cb858585848151811061059b5761059b611257565b6020026020010151600001518685815181106105b9576105b9611257565b60200260200101516020015186610a67565b806105d58161126d565b91505061057b565b505b6040516000906001600160a01b038516903480156108fc029184818181858888f1935050505090508061023c57600080fd5b60028054036106325760405162461bcd60e51b81526004016101b39061111c565b600280556040516001600160a01b0385811660248301528481166044830152606482018490523491600091881690839060840160408051601f198184030181529181526020820180516001600160e01b0316631230c53d60e21b1790525161069a91906112f1565b60006040518083038185875af1925050503d80600081146106d7576040519150601f19603f3d011682016040523d82523d6000602084013e6106dc565b606091505b5050905080610744576040516000906001600160a01b0387169084156108fc0290859084818181858888f1935050505090508061074257604051339084156108fc029085906000818181858888f19350505050158015610740573d6000803e3d6000fd5b505b505b336001600160a01b03167f8e5841bcd195b858d53b38bcf91b38d47f3bc800469b6812d35451ab619c6f6c8884898989896040516107879695949392919061130d565b60405180910390a2505060016002555050505050565b8042106107ec5760405162461bcd60e51b815260206004820152601960248201527f54484f52436861696e5f526f757465723a20657870697265640000000000000060448201526064016101b3565b6107f885858585610248565b5050505050565b60028054036108205760405162461bcd60e51b81526004016101b39061111c565b6002805560006001600160a01b03841661089a575060405134906000906001600160a01b0387169083156108fc0290849084818181858888f1935050505090508061089457604051339083156108fc029084906000818181858888f19350505050158015610892573d6000803e3d6000fd5b505b506109a7565b3360009081526001602090815260408083206001600160a01b0388168452909152812080548592906108cd90849061135b565b90915550506040516001600160a01b03868116602483015260448201859052600091829187169060640160408051601f198184030181529181526020820180516001600160e01b031663a9059cbb60e01b1790525161092c91906112f1565b6000604051808303816000865af19150503d8060008114610969576040519150601f19603f3d011682016040523d82523d6000602084013e61096e565b606091505b509150915081801561099857508051158061099857508080602001905181019061099891906111df565b6109a157600080fd5b84925050505b846001600160a01b0316336001600160a01b03167fa9cd03aa3c1b4515114539cd53d22085129d495cb9e9f9af77864526240f1bf7868486604051610479939291906111af565b3360009081526001602090815260408083206001600160a01b038616845290915281208054839290610a2190849061135b565b90915550506001600160a01b03808416600090815260016020908152604080832093861683529290529081208054839290610a5d90849061121e565b9091555050505050565b3360009081526001602090815260408083206001600160a01b038716845290915281208054849290610a9a90849061135b565b90915550506040516001600160a01b038681166024830152604482018490526000919085169060640160408051601f198184030181529181526020820180516001600160e01b031663095ea7b360e01b17905251610af891906112f1565b6000604051808303816000865af19150503d8060008114610b35576040519150601f19603f3d011682016040523d82523d6000602084013e610b3a565b606091505b5050905080610b4857600080fd5b6040516344bc937b60e01b81526001600160a01b038716906344bc937b90610b7e90889088908890889060001990600401611372565b600060405180830381600087803b158015610b9857600080fd5b505af1158015610bac573d6000803e3d6000fd5b50505050505050505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa158015610c01573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2591906113b7565b6040513360248201523060448201526064810185905290915060009081906001600160a01b0387169060840160408051601f198184030181529181526020820180516001600160e01b03166323b872dd60e01b17905251610c8691906112f1565b6000604051808303816000865af19150503d8060008114610cc3576040519150601f19603f3d011682016040523d82523d6000602084013e610cc8565b606091505b5091509150818015610cf2575080511580610cf2575080806020019051810190610cf291906111df565b610cfb57600080fd5b6040516370a0823160e01b815230600482015283906001600160a01b038816906370a0823190602401602060405180830381865afa158015610d41573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d6591906113b7565b610d6f919061135b565b9695505050505050565b6001600160a01b0381168114610d8e57600080fd5b50565b60008060408385031215610da457600080fd5b8235610daf81610d79565b91506020830135610dbf81610d79565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715610e0357610e03610dca565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610e3257610e32610dca565b604052919050565b600082601f830112610e4b57600080fd5b813567ffffffffffffffff811115610e6557610e65610dca565b610e78601f8201601f1916602001610e09565b818152846020838601011115610e8d57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a08688031215610ec257600080fd5b8535610ecd81610d79565b94506020860135610edd81610d79565b93506040860135610eed81610d79565b925060608601359150608086013567ffffffffffffffff811115610f1057600080fd5b610f1c88828901610e3a565b9150509295509295909350565b60008060008060808587031215610f3f57600080fd5b8435610f4a81610d79565b93506020850135610f5a81610d79565b925060408501359150606085013567ffffffffffffffff811115610f7d57600080fd5b610f8987828801610e3a565b91505092959194509250565b60008060008060808587031215610fab57600080fd5b8435610fb681610d79565b9350602085810135610fc781610d79565b935060408681013567ffffffffffffffff80821115610fe557600080fd5b818901915089601f830112610ff957600080fd5b81358181111561100b5761100b610dca565b611019858260051b01610e09565b81815260069190911b8301850190858101908c83111561103857600080fd5b938601935b828510156110815785858e0312156110555760008081fd5b61105d610de0565b853561106881610d79565b815285880135888201528252938501939086019061103d565b97505050606089013593508084111561109957600080fd5b505050610f8987828801610e3a565b600080600080600060a086880312156110c057600080fd5b85356110cb81610d79565b945060208601356110db81610d79565b935060408601359250606086013567ffffffffffffffff8111156110fe57600080fd5b61110a88828901610e3a565b95989497509295608001359392505050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b60005b8381101561116e578181015183820152602001611156565b8381111561117d576000848401525b50505050565b6000815180845261119b816020860160208601611153565b601f01601f19169290920160200192915050565b60018060a01b03841681528260208201526060604082015260006111d66060830184611183565b95945050505050565b6000602082840312156111f157600080fd5b8151801515811461120157600080fd5b9392505050565b634e487b7160e01b600052601160045260246000fd5b6000821982111561123157611231611208565b500190565b82815260406020820152600061124f6040830184611183565b949350505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161127f5761127f611208565b5060010190565b60408082528351828201819052600091906020906060850190828801855b828110156112d257815180516001600160a01b031685528501518585015292850192908401906001016112a4565b505050848103828601526112e68187611183565b979650505050505050565b60008251611303818460208701611153565b9190910192915050565b6001600160a01b038781168252602082018790528581166040830152841660608201526080810183905260c060a0820181905260009061134f90830184611183565b98975050505050505050565b60008282101561136d5761136d611208565b500390565b6001600160a01b038681168252851660208201526040810184905260a0606082018190526000906113a590830185611183565b90508260808301529695505050505050565b6000602082840312156113c957600080fd5b505191905056fea264697066735822122081bfb5152612852b02c6e856e49c9abbe953e45e649e4ea942477a8b50a3773064736f6c634300080d0033 \ No newline at end of file diff --git a/examples/thorchain/contracts/router-abi.json b/examples/thorchain/contracts/router-abi.json new file mode 100644 index 000000000..3c638983e --- /dev/null +++ b/examples/thorchain/contracts/router-abi.json @@ -0,0 +1,423 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVault", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferAllowance", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferOut", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "target", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "finalAsset", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "TransferOutAndCall", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldVault", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newVault", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct AvaxRouter.Coin[]", + "name": "coins", + "type": "tuple[]" + }, + { + "indexed": false, + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "VaultTransfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "vault", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "vault", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + }, + { + "internalType": "uint256", + "name": "expiration", + "type": "uint256" + } + ], + "name": "depositWithExpiry", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "address payable", + "name": "asgard", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct AvaxRouter.Coin[]", + "name": "coins", + "type": "tuple[]" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "returnVaultAssets", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "router", + "type": "address" + }, + { + "internalType": "address", + "name": "newVault", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "transferAllowance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "to", + "type": "address" + }, + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "transferOut", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address payable", + "name": "target", + "type": "address" + }, + { + "internalType": "address", + "name": "finalToken", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "string", + "name": "memo", + "type": "string" + } + ], + "name": "transferOutAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "vault", + "type": "address" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + } + ], + "name": "vaultAllowance", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/examples/thorchain/contracts/router-bytecode.txt b/examples/thorchain/contracts/router-bytecode.txt new file mode 100644 index 000000000..c516942b7 --- /dev/null +++ b/examples/thorchain/contracts/router-bytecode.txt @@ -0,0 +1 @@ +0x608060405234801561001057600080fd5b50600180556118ec806100246000396000f3fe6080604052600436106100705760003560e01c80632923e82e1161004e5780632923e82e1461010d5780634039fd4b1461012057806344bc937b14610133578063574da7171461014657600080fd5b806303b6a673146100755780631b738b32146100d85780631fece7b4146100fa575b600080fd5b34801561008157600080fd5b506100c66100903660046111b7565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526020818152604080832093909416825291909152205490565b60405190815260200160405180910390f35b3480156100e457600080fd5b506100f86100f3366004611325565b610159565b005b6100f86101083660046113a4565b61027f565b6100f861011b366004611410565b610488565b6100f861012e366004611325565b61068e565b6100f8610141366004611523565b6108d9565b6100f86101543660046113a4565b610955565b600260015414156101cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600260015573ffffffffffffffffffffffffffffffffffffffff8516301415610267576101f9848484610c29565b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f05b90458f953d3fcb2d7fb25616a2fddeca749d0c47cc5c9832d0266b5346eea85858560405161025a93929190611611565b60405180910390a3610274565b6102748585858585610cb8565b505060018055505050565b600260015414156102ec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016101c2565b6002600155600073ffffffffffffffffffffffffffffffffffffffff84166103575750604051349060009073ffffffffffffffffffffffffffffffffffffffff87169083156108fc0290849084818181858888f1935050505090508061035157600080fd5b50610416565b34156103bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f756e65787065637465642061766178000000000000000000000000000000000060448201526064016101c2565b6103c98484610eb1565b73ffffffffffffffffffffffffffffffffffffffff80871660009081526020818152604080832093891683529290529081208054929350839290919061041090849061167e565b90915550505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fef519b7eb82aaf6ac376a6df2d793843ebfd593de5f1a0601d3cc6ab49ebb3958385604051610475929190611696565b60405180910390a3505060018055505050565b600260015414156104f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016101c2565b600260015573ffffffffffffffffffffffffffffffffffffffff84163014156105e85760005b825181101561057b576105698484838151811061053a5761053a6116b7565b602002602001015160000151858481518110610558576105586116b7565b602002602001015160200151610c29565b80610573816116e6565b91505061051b565b508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f281daef48d91e5cd3d32db0784f6af69cd8d8d2e8c612a3568dca51ded51e08f84846040516105db92919061171f565b60405180910390a361064f565b60005b825181101561064d5761063b858585848151811061060b5761060b6116b7565b602002602001015160000151868581518110610629576106296116b7565b60200260200101516020015186610cb8565b80610645816116e6565b9150506105eb565b505b60405160009073ffffffffffffffffffffffffffffffffffffffff8516903480156108fc029184818181858888f1935050505090508061027457600080fd5b600260015414156106fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016101c2565b600260015560405173ffffffffffffffffffffffffffffffffffffffff858116602483015284811660448301526064820184905234916000918816908390608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f48c314f400000000000000000000000000000000000000000000000000000000179052516107bd9190611797565b60006040518083038185875af1925050503d80600081146107fa576040519150601f19603f3d011682016040523d82523d6000602084013e6107ff565b606091505b50509050806108745760405160009073ffffffffffffffffffffffffffffffffffffffff87169084156108fc0290859084818181858888f1935050505090508061087257604051339084156108fc029085906000818181858888f19350505050158015610870573d6000803e3d6000fd5b505b505b3373ffffffffffffffffffffffffffffffffffffffff167f8e5841bcd195b858d53b38bcf91b38d47f3bc800469b6812d35451ab619c6f6c8884898989896040516108c4969594939291906117b3565b60405180910390a25050600180555050505050565b804210610942576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f54484f52436861696e5f526f757465723a20657870697265640000000000000060448201526064016101c2565b61094e8585858561027f565b5050505050565b600260015414156109c2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016101c2565b6002600155600073ffffffffffffffffffffffffffffffffffffffff8416610a575750604051349060009073ffffffffffffffffffffffffffffffffffffffff87169083156108fc0290849084818181858888f19350505050905080610a5157604051339083156108fc029084906000818181858888f19350505050158015610a4f573d6000803e3d6000fd5b505b50610bc8565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8816845290915281208054859290610a9590849061180c565b909155505060405173ffffffffffffffffffffffffffffffffffffffff8681166024830152604482018590526000918291871690606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905251610b4d9190611797565b6000604051808303816000865af19150503d8060008114610b8a576040519150601f19603f3d011682016040523d82523d6000602084013e610b8f565b606091505b5091509150818015610bb9575080511580610bb9575080806020019051810190610bb99190611823565b610bc257600080fd5b84925050505b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fa9cd03aa3c1b4515114539cd53d22085129d495cb9e9f9af77864526240f1bf786848660405161047593929190611611565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8616845290915281208054839290610c6790849061180c565b909155505073ffffffffffffffffffffffffffffffffffffffff80841660009081526020818152604080832093861683529290529081208054839290610cae90849061167e565b9091555050505050565b3360009081526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281208054849290610cf690849061180c565b909155505060405173ffffffffffffffffffffffffffffffffffffffff86811660248301526044820184905260009190851690606401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905251610dad9190611797565b6000604051808303816000865af19150503d8060008114610dea576040519150601f19603f3d011682016040523d82523d6000602084013e610def565b606091505b5050905080610dfd57600080fd5b6040517f44bc937b00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8716906344bc937b90610e779088908890889088907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060040161184c565b600060405180830381600087803b158015610e9157600080fd5b505af1158015610ea5573d6000803e3d6000fd5b50505050505050505050565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600090819073ffffffffffffffffffffffffffffffffffffffff8516906370a082319060240160206040518083038186803b158015610f1b57600080fd5b505afa158015610f2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f53919061189d565b60405133602482015230604482015260648101859052909150600090819073ffffffffffffffffffffffffffffffffffffffff871690608401604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f23b872dd000000000000000000000000000000000000000000000000000000001790525161100d9190611797565b6000604051808303816000865af19150503d806000811461104a576040519150601f19603f3d011682016040523d82523d6000602084013e61104f565b606091505b50915091508180156110795750805115806110795750808060200190518101906110799190611823565b6110df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4661696c656420546f205472616e7366657246726f6d0000000000000000000060448201526064016101c2565b6040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152839073ffffffffffffffffffffffffffffffffffffffff8816906370a082319060240160206040518083038186803b15801561114657600080fd5b505afa15801561115a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061117e919061189d565b611188919061180c565b9695505050505050565b73ffffffffffffffffffffffffffffffffffffffff811681146111b457600080fd5b50565b600080604083850312156111ca57600080fd5b82356111d581611192565b915060208301356111e581611192565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611242576112426111f0565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561128f5761128f6111f0565b604052919050565b600082601f8301126112a857600080fd5b813567ffffffffffffffff8111156112c2576112c26111f0565b6112f360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611248565b81815284602083860101111561130857600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080600060a0868803121561133d57600080fd5b853561134881611192565b9450602086013561135881611192565b9350604086013561136881611192565b925060608601359150608086013567ffffffffffffffff81111561138b57600080fd5b61139788828901611297565b9150509295509295909350565b600080600080608085870312156113ba57600080fd5b84356113c581611192565b935060208501356113d581611192565b925060408501359150606085013567ffffffffffffffff8111156113f857600080fd5b61140487828801611297565b91505092959194509250565b6000806000806080858703121561142657600080fd5b843561143181611192565b935060208581013561144281611192565b935060408681013567ffffffffffffffff8082111561146057600080fd5b818901915089601f83011261147457600080fd5b813581811115611486576114866111f0565b611494858260051b01611248565b81815260069190911b8301850190858101908c8311156114b357600080fd5b938601935b828510156114fc5785858e0312156114d05760008081fd5b6114d861121f565b85356114e381611192565b81528588013588820152825293850193908601906114b8565b97505050606089013593508084111561151457600080fd5b50505061140487828801611297565b600080600080600060a0868803121561153b57600080fd5b853561154681611192565b9450602086013561155681611192565b935060408601359250606086013567ffffffffffffffff81111561157957600080fd5b61158588828901611297565b95989497509295608001359392505050565b60005b838110156115b257818101518382015260200161159a565b838111156115c1576000848401525b50505050565b600081518084526115df816020860160208601611597565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b73ffffffffffffffffffffffffffffffffffffffff8416815282602082015260606040820152600061164660608301846115c7565b95945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156116915761169161164f565b500190565b8281526040602082015260006116af60408301846115c7565b949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156117185761171861164f565b5060010190565b60408082528351828201819052600091906020906060850190828801855b82811015611778578151805173ffffffffffffffffffffffffffffffffffffffff16855285015185850152928501929084019060010161173d565b5050508481038286015261178c81876115c7565b979650505050505050565b600082516117a9818460208701611597565b9190910192915050565b600073ffffffffffffffffffffffffffffffffffffffff8089168352876020840152808716604084015280861660608401525083608083015260c060a083015261180060c08301846115c7565b98975050505050505050565b60008282101561181e5761181e61164f565b500390565b60006020828403121561183557600080fd5b8151801515811461184557600080fd5b9392505050565b600073ffffffffffffffffffffffffffffffffffffffff808816835280871660208401525084604083015260a0606083015261188b60a08301856115c7565b90508260808301529695505050505050565b6000602082840312156118af57600080fd5b505191905056fea2646970667358221220f11847a348f63b399358a3736e1f295761b905637e0cec3e15f85765e2d3dac564736f6c63430008090033 \ No newline at end of file diff --git a/examples/thorchain/contracts/token-abi.json b/examples/thorchain/contracts/token-abi.json new file mode 100644 index 000000000..ba48a0da6 --- /dev/null +++ b/examples/thorchain/contracts/token-abi.json @@ -0,0 +1,229 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "success", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/examples/thorchain/contracts/token-bytecode.txt b/examples/thorchain/contracts/token-bytecode.txt new file mode 100644 index 000000000..1a7d13087 --- /dev/null +++ b/examples/thorchain/contracts/token-bytecode.txt @@ -0,0 +1 @@ +0x60806040526012600255600254600a0a620f42400260035534801561002357600080fd5b50600354600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506040518060400160405280600581526020017f546f6b656e000000000000000000000000000000000000000000000000000000815250600090805190602001906100b592919061016f565b506040518060400160405280600381526020017f544b4e00000000000000000000000000000000000000000000000000000000008152506001908051906020019061010192919061016f565b503373ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6003546040518082815260200191505060405180910390a3610214565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106101b057805160ff19168380011785556101de565b828001600101855582156101de579182015b828111156101dd5782518255916020019190600101906101c2565b5b5090506101eb91906101ef565b5090565b61021191905b8082111561020d5760008160009055506001016101f5565b5090565b90565b610c34806102236000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461022557806370a082311461024357806395d89b411461029b578063a9059cbb1461031e578063dd62ed3e1461038457610093565b806306fdde0314610098578063095ea7b31461011b57806318160ddd1461018157806323b872dd1461019f575b600080fd5b6100a06103fc565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100e05780820151818401526020810190506100c5565b50505050905090810190601f16801561010d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101676004803603604081101561013157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049a565b604051808215151515815260200191505060405180910390f35b61018961058c565b6040518082815260200191505060405180910390f35b61020b600480360360608110156101b557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610592565b604051808215151515815260200191505060405180910390f35b61022d6106bd565b6040518082815260200191505060405180910390f35b6102856004803603602081101561025957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106c3565b6040518082815260200191505060405180910390f35b6102a36106db565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102e35780820151818401526020810190506102c8565b50505050905090810190601f1680156103105780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61036a6004803603604081101561033457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610779565b604051808215151515815260200191505060405180910390f35b6103e66004803603604081101561039a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610790565b6040518082815260200191505060405180910390f35b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104925780601f1061046757610100808354040283529160200191610492565b820191906000526020600020905b81548152906001019060200180831161047557829003601f168201915b505050505081565b600081600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60035481565b6000600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561061d57600080fd5b81600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055506106b28484846107b5565b600190509392505050565b60025481565b60046020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156107715780601f1061074657610100808354040283529160200191610771565b820191906000526020600020905b81548152906001019060200180831161075457829003601f168201915b505050505081565b60006107863384846107b5565b6001905092915050565b6005602052816000526040600020602052806000526040600020600091509150505481565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156107ef57600080fd5b80600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561083b57600080fd5b600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546108cd82600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610a6c90919063ffffffff16565b10156108d857600080fd5b61092a81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610af490919063ffffffff16565b600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506109bf81600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610a6c90919063ffffffff16565b600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a3505050565b600080828401905083811015610aea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f536166654d6174683a206164646974696f6e206f766572666c6f77000000000081525060200191505060405180910390fd5b8091505092915050565b6000610b3683836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610b3e565b905092915050565b6000838311158290610beb576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b83811015610bb0578082015181840152602081019050610b95565b50505050905090810190601f168015610bdd5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506000838503905080915050939250505056fea26469706673582212208621a61a72b5773eedaaccd88413213c489e071f7a4afc9c87236e049e046e9064736f6c63430006050033 \ No newline at end of file diff --git a/examples/thorchain/features/dual_lper.go b/examples/thorchain/features/dual_lper.go index 13fbd9561..c418c5e4b 100644 --- a/examples/thorchain/features/dual_lper.go +++ b/examples/thorchain/features/dual_lper.go @@ -19,34 +19,34 @@ func DualLp( fmt.Println("#### Dual Lper:", exoChain.Config().Name) users, err := GetAndFundTestUsers(t, ctx, fmt.Sprintf("%s-DualLper", exoChain.Config().Name), thorchain, exoChain) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, fund users (%s), %w", exoChain.Config().Name, err) } thorUser, exoUser = users[0], users[1] exoChainType, err := common.NewChain(exoChain.Config().Name) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, chain type (%s), %w", exoChain.Config().Name, err) } exoAsset := exoChainType.GetGasAsset() thorUserBalance, err := thorchain.GetBalance(ctx, thorUser.FormattedAddress(), thorchain.Config().Denom) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, thor balance (%s), %w", exoChain.Config().Name, err) } memo := fmt.Sprintf("+:%s:%s", exoAsset, exoUser.FormattedAddress()) err = thorchain.Deposit(ctx, thorUser.KeyName(), thorUserBalance.QuoRaw(100).MulRaw(90), thorchain.Config().Denom, memo) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, thor deposit (%s), %w", exoChain.Config().Name, err) } exoUserBalance, err := exoChain.GetBalance(ctx, exoUser.FormattedAddress(), exoChain.Config().Denom) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, exo balance (%s), %w", exoChain.Config().Name, err) } memo = fmt.Sprintf("+:%s:%s", exoAsset, thorUser.FormattedAddress()) exoInboundAddr, _, err := thorchain.ApiGetInboundAddress(exoChainType.String()) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, inbound addr (%s), %w", exoChain.Config().Name, err) } _, err = exoChain.SendFundsWithNote(ctx, exoUser.KeyName(), ibc.WalletAmount{ Address: exoInboundAddr, @@ -54,12 +54,12 @@ func DualLp( Amount: exoUserBalance.QuoRaw(100).MulRaw(90), // LP 90% of balance }, memo) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, exo send funds (%s), %w", exoChain.Config().Name, err) } err = PollForPool(ctx, thorchain, 60, exoAsset) if err != nil { - return thorUser, exoUser, err + return thorUser, exoUser, fmt.Errorf("duallp, poll for pool (%s), %w", exoChain.Config().Name, err) } return thorUser, exoUser, err diff --git a/examples/thorchain/features/helpers.go b/examples/thorchain/features/helpers.go index 3c90992ac..f8d3206f4 100644 --- a/examples/thorchain/features/helpers.go +++ b/examples/thorchain/features/helpers.go @@ -47,7 +47,7 @@ func GetAndFundTestUsers( return err } if !userBalance.Equal(amount) { - return fmt.Errorf("User (%s) was not properly funded", user[0].KeyName()) + return fmt.Errorf("user (%s) was not properly funded", user[0].KeyName()) } return nil @@ -74,7 +74,7 @@ func PollForPool(ctx context.Context, thorchain *tc.Thorchain, deltaBlocks int64 if pool.BalanceAsset == "0" { time.Sleep(time.Second) // rate limit - return nil, fmt.Errorf("Pool (%s) exists, but not asset balance in %d blocks", asset, deltaBlocks) + return nil, fmt.Errorf("pool (%s) exists, but not asset balance in %d blocks", asset, deltaBlocks) } return nil, nil } @@ -97,7 +97,7 @@ func PollForSaver(ctx context.Context, thorchain *tc.Thorchain, deltaBlocks int6 return tc.Saver{}, err } for _, saver := range savers { - if strings.ToLower(saver.AssetAddress) == strings.ToLower(exoUser.FormattedAddress()) { + if strings.EqualFold(saver.AssetAddress, exoUser.FormattedAddress()) { return saver, nil } @@ -124,7 +124,7 @@ func PollForEjectedSaver(ctx context.Context, thorchain *tc.Thorchain, deltaBloc return tc.Saver{}, err } for _, saver := range savers { - if strings.ToLower(saver.AssetAddress) == strings.ToLower(exoUser.FormattedAddress()) { + if strings.EqualFold(saver.AssetAddress, exoUser.FormattedAddress()) { time.Sleep(time.Second) // rate limit return saver, fmt.Errorf("saver took longer than %d blocks to eject", deltaBlocks) } @@ -228,7 +228,7 @@ func PollForPoolSuspended(ctx context.Context, thorchain *tc.Thorchain, deltaBlo } time.Sleep(time.Second) // rate limit - return nil, fmt.Errorf("Pool (%s) did not suspend in %d blocks", exoAsset, deltaBlocks) + return nil, fmt.Errorf("pool (%s) did not suspend in %d blocks", exoAsset, deltaBlocks) } time.Sleep(time.Second) // Limit how quickly Height() is called back to back per go routine bp := testutil.BlockPoller[any]{CurrentHeight: thorchain.Height, PollFunc: doPoll} diff --git a/examples/thorchain/features/ragnarok.go b/examples/thorchain/features/ragnarok.go index e8e150cd4..5268ca4dc 100644 --- a/examples/thorchain/features/ragnarok.go +++ b/examples/thorchain/features/ragnarok.go @@ -63,7 +63,7 @@ func Ragnarok( return err } if exoUserPostRagBalance.LTE(exoUsersPreRagBalance[i]) { - return fmt.Errorf("User (%s) balance did not increase after %s ragnarok", exoUser.KeyName(), exoChainType) + return fmt.Errorf("user (%s) balance did not increase after %s ragnarok", exoUser.KeyName(), exoChainType) } fmt.Printf("\nUser (%s), pre: %s, post: %s\n", exoUser.KeyName(), exoUsersPreRagBalance[i], exoUserPostRagBalance) } diff --git a/examples/thorchain/features/saver_eject.go b/examples/thorchain/features/saver_eject.go index 7d52972a6..e6c6245c0 100644 --- a/examples/thorchain/features/saver_eject.go +++ b/examples/thorchain/features/saver_eject.go @@ -38,31 +38,36 @@ func SaverEject( mimirLock.Lock() mimirs, err := thorchain.ApiGetMimirs() if err != nil { + mimirLock.Unlock() return exoUser, err } // Set max synth per pool depth to 100% of pool amount if mimir, ok := mimirs[strings.ToUpper("MaxSynthPerPoolDepth")]; ok && mimir != int64(5000) { - if err := thorchain.SetMimir(ctx, "admin", "MaxSynthPerPoolDepth", "5000"); err != nil { + if err = thorchain.SetMimir(ctx, "admin", "MaxSynthPerPoolDepth", "5000"); err != nil { + mimirLock.Unlock() return exoUser, err } } // Disable saver ejection if mimir, ok := mimirs[strings.ToUpper("SaversEjectInterval")]; ok && mimir != int64(0) || !ok { - if err := thorchain.SetMimir(ctx, "admin", "SaversEjectInterval", "0"); err != nil { + if err = thorchain.SetMimir(ctx, "admin", "SaversEjectInterval", "0"); err != nil { + mimirLock.Unlock() return exoUser, err } } exoChainType, err := common.NewChain(exoChain.Config().Name) if err != nil { + mimirLock.Unlock() return exoUser, err } exoAsset := exoChainType.GetGasAsset() pool, err := thorchain.ApiGetPool(exoAsset) if err != nil { + mimirLock.Unlock() return exoUser, err } saveAmount := sdkmath.NewUintFromString(pool.BalanceAsset). @@ -70,6 +75,7 @@ func SaverEject( saverQuote, err := thorchain.ApiGetSaverDepositQuote(exoAsset, saveAmount) if err != nil { + mimirLock.Unlock() return exoUser, err } @@ -91,6 +97,7 @@ func SaverEject( exoInboundAddr, _, err := thorchain.ApiGetInboundAddress(exoChainType.String()) if err != nil { + mimirLock.Unlock() return exoUser, err } @@ -107,25 +114,30 @@ func SaverEject( err = exoChain.SendFunds(ctx, exoUser.KeyName(), wallet) } if err != nil { + mimirLock.Unlock() return exoUser, err } errMsgCommon := fmt.Sprintf("saver (%s - %s) of asset %s", exoUser.KeyName(), exoUser.FormattedAddress(), exoAsset) saver, err := PollForSaver(ctx, thorchain, 30, exoAsset, exoUser) if err != nil { + mimirLock.Unlock() return exoUser, fmt.Errorf("%s not found, %w", errMsgCommon, err) } deposit := sdkmath.NewUintFromString(saver.AssetDepositValue) if deposit.LT(minExpectedSaver) { + mimirLock.Unlock() return exoUser, fmt.Errorf("%s deposit: %s, min expected: %s", errMsgCommon, deposit, minExpectedSaver) } if deposit.GT(maxExpectedSaver) { + mimirLock.Unlock() return exoUser, fmt.Errorf("%s deposit: %s, max expected: %s", errMsgCommon, deposit, maxExpectedSaver) } exoUserPreEjectBalance, err := exoChain.GetBalance(ctx, exoUser.FormattedAddress(), exoChain.Config().Denom) if err != nil { + mimirLock.Unlock() return exoUser, err } @@ -133,35 +145,40 @@ func SaverEject( for i, exoSaver := range exoSavers { exoSaversBalance[i], err = exoChain.GetBalance(ctx, exoSaver.FormattedAddress(), exoChain.Config().Denom) if err != nil { + mimirLock.Unlock() return exoUser, err } } mimirs, err = thorchain.ApiGetMimirs() if err != nil { + mimirLock.Unlock() return exoUser, err } // Set mimirs if mimir, ok := mimirs[strings.ToUpper("MaxSynthPerPoolDepth")]; ok && mimir != int64(500) || !ok { if err := thorchain.SetMimir(ctx, "admin", "MaxSynthPerPoolDepth", "500"); err != nil { + mimirLock.Unlock() return exoUser, err } } if mimir, ok := mimirs[strings.ToUpper("SaversEjectInterval")]; ok && mimir != int64(1) || !ok { if err := thorchain.SetMimir(ctx, "admin", "SaversEjectInterval", "1"); err != nil { + mimirLock.Unlock() return exoUser, err } } _, err = PollForEjectedSaver(ctx, thorchain, 30, exoAsset, exoUser) if err != nil { + mimirLock.Unlock() return exoUser, err } mimirLock.Unlock() - if err := PollForBalanceChange(ctx, exoChain, 15, ibc.WalletAmount{ + if err = PollForBalanceChange(ctx, exoChain, 15, ibc.WalletAmount{ Address: exoUser.FormattedAddress(), Denom: exoChain.Config().Denom, Amount: exoUserPreEjectBalance, @@ -173,7 +190,7 @@ func SaverEject( return exoUser, err } if exoUserPostEjectBalance.LTE(exoUserPreEjectBalance) { - return exoUser, fmt.Errorf("User (%s) balance (%s) must be greater after ejection: %s", exoUser.KeyName(), exoUserPostEjectBalance, exoUserPreEjectBalance) + return exoUser, fmt.Errorf("user (%s) balance (%s) must be greater after ejection: %s", exoUser.KeyName(), exoUserPostEjectBalance, exoUserPreEjectBalance) } for i, exoSaver := range exoSavers { @@ -182,7 +199,7 @@ func SaverEject( return exoUser, err } if !exoSaverPostBalance.Equal(exoSaversBalance[i]) { - return exoUser, fmt.Errorf("Saver's (%s) post balance (%s) should be the same as (%s)", exoSaver.KeyName(), exoSaverPostBalance, exoSaversBalance[i]) + return exoUser, fmt.Errorf("saver's (%s) post balance (%s) should be the same as (%s)", exoSaver.KeyName(), exoSaverPostBalance, exoSaversBalance[i]) } } diff --git a/examples/thorchain/features/swap.go b/examples/thorchain/features/swap.go index 2ede3f9a1..a31393fc7 100644 --- a/examples/thorchain/features/swap.go +++ b/examples/thorchain/features/swap.go @@ -10,7 +10,6 @@ import ( tc "github.com/strangelove-ventures/interchaintest/v8/chain/thorchain" "github.com/strangelove-ventures/interchaintest/v8/chain/thorchain/common" "github.com/strangelove-ventures/interchaintest/v8/ibc" - "golang.org/x/sync/errgroup" ) func SingleSwap( @@ -44,17 +43,12 @@ func DualSwap( } srcUser, destUser := users[0], users[1] - var eg errgroup.Group - - eg.Go(func() error { - return singleSwap(ctx, thorchain, srcChain, srcUser, destChain, destUser) - }) - - eg.Go(func() error { - return singleSwap(ctx, thorchain, destChain, destUser, srcChain, srcUser) - }) + // Perform these swap sequential so the balance checks work properly + if err := singleSwap(ctx, thorchain, srcChain, srcUser, destChain, destUser); err != nil { + return err + } - return eg.Wait() + return singleSwap(ctx, thorchain, destChain, destUser, srcChain, srcUser) } // swap 0.5% of pool depth @@ -140,7 +134,7 @@ func singleSwap( } else { _, err = PollOutboundSigned(ctx, thorchain, 200, txHash) if err != nil { - return err + return fmt.Errorf("outbound chain: %s, err: %w", destChainType, err) } } @@ -179,10 +173,10 @@ func singleSwap( } actualSwapAmount := destUserBalancePostSwap.Sub(destUserBalancePreSwap) if actualSwapAmount.LT(sdkmath.Int(minExpectedSwapAmount)) { - return fmt.Errorf("Actual swap amount: %s %s, min expected: %s", actualSwapAmount, destChain.Config().Denom, minExpectedSwapAmount) + return fmt.Errorf("actual swap amount: %s %s, min expected: %s", actualSwapAmount, destChain.Config().Denom, minExpectedSwapAmount) } if actualSwapAmount.GT(sdkmath.Int(maxExpectedSwapAmount)) { - return fmt.Errorf("Actual swap amount: %s %s, max expected: %s", actualSwapAmount, destChain.Config().Denom, maxExpectedSwapAmount) + return fmt.Errorf("actual swap amount: %s %s, max expected: %s", actualSwapAmount, destChain.Config().Denom, maxExpectedSwapAmount) } // TODO: compare outAmountPlusMaxGas -> actualSwapAmount fmt.Println("outAmountPlusMaxGas:", outAmountPlusMaxGas, "actualSwapAmount:", actualSwapAmount) diff --git a/examples/thorchain/helper.go b/examples/thorchain/helper.go index 1656fe6f3..be897a7b6 100644 --- a/examples/thorchain/helper.go +++ b/examples/thorchain/helper.go @@ -4,9 +4,8 @@ import ( "context" "fmt" "regexp" - "strings" + "time" - "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/ibc" ) @@ -23,8 +22,22 @@ func GetEthAddressFromStdout(stdout string) (string, error) { return matches[1], nil } -func sendFunds(ctx context.Context, keyName string, toAddr string, amount ibc.WalletAmount, val0 *cosmos.ChainNode) { - memo := strings.Repeat("Hello World ", 10) - command := []string{"bank", "send", keyName, toAddr, fmt.Sprintf("%s%s", amount.Amount.String(), amount.Denom), "--note", memo} - _, _, _ = val0.Exec(ctx, val0.TxCommand(keyName, command...), val0.Chain.Config().Env) +// We are running many nodes, using many resources. This function is similar to +// testutils.WaitForBlocks(), but does not hammer calls as fast as possible. +func NiceWaitForBlocks(ctx context.Context, delta int64, chain ibc.Chain) error { + startingHeight, err := chain.Height(ctx) + if err != nil { + return err + } + + currentHeight := startingHeight + for ; currentHeight < startingHeight + delta; { + time.Sleep(time.Millisecond * 200) + currentHeight, err = chain.Height(ctx) + if err != nil { + return err + } + } + + return nil } diff --git a/examples/thorchain/setup.go b/examples/thorchain/setup.go index f49e008c2..2353e4963 100644 --- a/examples/thorchain/setup.go +++ b/examples/thorchain/setup.go @@ -2,9 +2,11 @@ package thorchain_test import ( "context" + _ "embed" "fmt" "strings" "testing" + "time" sdkmath "cosmossdk.io/math" "github.com/cosmos/cosmos-sdk/codec" @@ -19,10 +21,11 @@ import ( "github.com/strangelove-ventures/interchaintest/v8" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/foundry" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/geth" tc "github.com/strangelove-ventures/interchaintest/v8/chain/thorchain" "github.com/strangelove-ventures/interchaintest/v8/chain/utxo" "github.com/strangelove-ventures/interchaintest/v8/ibc" - "github.com/strangelove-ventures/interchaintest/v8/testutil" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" @@ -30,12 +33,14 @@ import ( func StartExoChains(t *testing.T, ctx context.Context, client *client.Client, network string) ExoChains { chainSpecs := []*interchaintest.ChainSpec{ - EthChainSpec(), + EthChainSpec("geth"), // only use this chain spec for eth or the one below + //EthChainSpec("anvil"), GaiaChainSpec(), BtcChainSpec(), BchChainSpec(), LtcChainSpec(), DogeChainSpec(), + BscChainSpec(), } cf0 := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), chainSpecs) @@ -66,16 +71,16 @@ func StartExoChains(t *testing.T, ctx context.Context, client *client.Client, ne for _, wallet := range exoChains[name].genWallets { additionalGenesisWallets = append(additionalGenesisWallets, ibc.WalletAmount{ Address: wallet.FormattedAddress(), - Amount: sdkmath.NewInt(100_000_000), - Denom: chain.Config().Denom, + Amount: sdkmath.NewInt(100_000_000), + Denom: chain.Config().Denom, }) } if name == "GAIA" { // this wallet just stops bifrost complaining about it not existing additionalGenesisWallets = append(additionalGenesisWallets, ibc.WalletAmount{ Address: "cosmos1zf3gsk7edzwl9syyefvfhle37cjtql35427vcp", - Amount: sdkmath.NewInt(1_000_000), - Denom: chain.Config().Denom, + Amount: sdkmath.NewInt(1_000_000), + Denom: chain.Config().Denom, }) } ic.AddChain(chain, additionalGenesisWallets...) @@ -94,7 +99,7 @@ func StartExoChains(t *testing.T, ctx context.Context, client *client.Client, ne return exoChains } -func StartThorchain(t *testing.T, ctx context.Context, client *client.Client, network string, exoChains ExoChains, ethRouterContractAddress string) *tc.Thorchain { +func StartThorchain(t *testing.T, ctx context.Context, client *client.Client, network string, exoChains ExoChains, ethRouterContractAddress string, bscRouterContractAddress string) *tc.Thorchain { numThorchainValidators := 1 numThorchainFullNodes := 0 @@ -111,8 +116,14 @@ func StartThorchain(t *testing.T, ctx context.Context, client *client.Client, ne } disableChainKey := fmt.Sprintf("BIFROST_CHAINS_%s_DISABLED", name) bifrostEnvOverrides[disableChainKey] = "false" + if name == "BSC" { + hostKey = fmt.Sprintf("BIFROST_CHAINS_%s_RPC_HOST", name) + bifrostEnvOverrides[hostKey] = exoChain.chain.GetRPCAddress() + bsRpcHost := fmt.Sprintf("BIFROST_CHAINS_%s_BLOCK_SCANNER_RPC_HOST", name) + bifrostEnvOverrides[bsRpcHost] = exoChain.chain.GetRPCAddress() + } } - thorchainChainSpec := ThorchainDefaultChainSpec(t.Name(), numThorchainValidators, numThorchainFullNodes, ethRouterContractAddress, nil, bifrostEnvOverrides) + thorchainChainSpec := ThorchainDefaultChainSpec(t.Name(), numThorchainValidators, numThorchainFullNodes, ethRouterContractAddress, bscRouterContractAddress, nil, bifrostEnvOverrides) cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ thorchainChainSpec, @@ -140,50 +151,130 @@ func StartThorchain(t *testing.T, ctx context.Context, client *client.Client, ne require.NoError(t, err, "failed starting validator sidecars") // Give some time for bifrost to initialize before any tests start - err = testutil.WaitForBlocks(ctx, 5, thorchain) + time.Sleep(time.Second * 15) require.NoError(t, err) return thorchain } -func SetupEthContracts(t *testing.T, ctx context.Context, exoChain *ExoChain) string { - ethChain := exoChain.chain.(*ethereum.EthereumChain) +func SetupContracts(ctx context.Context, ethExoChain *ExoChain, bscExoChain *ExoChain) (ethContractAddr, bscContractAddr string, err error) { + eg, egCtx := errgroup.WithContext(ctx) + eg.Go(func() error { + var err error + if ethExoChain.chain.Config().Bin == "geth" { + ethContractAddr, err = SetupGethContracts(egCtx, ethExoChain) + } else { + ethContractAddr, err = SetupAnvilContracts(egCtx, ethExoChain) + } + return err + }) + eg.Go(func() error { + var err error + bscContractAddr, err = SetupGethContracts(egCtx, bscExoChain) + return err + }) + + return ethContractAddr, bscContractAddr, eg.Wait() +} + +//go:embed contracts/eth-router-abi.json +var ethRouterAbi []byte + +//go:embed contracts/eth-router-bytecode.txt +var ethRouterByteCode []byte + +//go:embed contracts/router-abi.json +var routerAbi []byte + +//go:embed contracts/router-bytecode.txt +var routerByteCode []byte + +func SetupGethContracts(ctx context.Context, exoChain *ExoChain) (string, error) { + abi := routerAbi + byteCode := routerByteCode + if exoChain.chain.Config().Name == "ETH" { + abi = ethRouterAbi + byteCode = append(ethRouterByteCode, []byte("000000000000000000000000de06987c28d839daaefb6c85816a2cc55277654c")...) // RUNE token (doesn't matter) + } + + ethChain := exoChain.chain.(*geth.GethChain) + + ethUserInitialAmount := ethereum.ETHER.MulRaw(100) + + ethUser, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, "user", strings.Repeat("dog ", 23)+"fossil", ethUserInitialAmount, ethChain) + if err != nil { + return "", err + } + + ethRouterContractAddress, err := ethChain.DeployContract(ctx, ethUser.KeyName(), abi, byteCode) + if err != nil { + return "", err + } + if ethRouterContractAddress == "" { + return "", fmt.Errorf("router contract address for (%s) chain is empty", ethChain.Config().Name) + } + if !ethcommon.IsHexAddress(ethRouterContractAddress) { + return "", fmt.Errorf("router contract address for (%s) chain is not a hex address", ethChain.Config().Name) + } + + return ethRouterContractAddress, nil +} + +func SetupAnvilContracts(ctx context.Context, exoChain *ExoChain) (string, error) { + ethChain := exoChain.chain.(*foundry.AnvilChain) ethUserInitialAmount := ethereum.ETHER.MulRaw(2) ethUser, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, "user", strings.Repeat("dog ", 23)+"fossil", ethUserInitialAmount, ethChain) - require.NoError(t, err) + if err != nil { + return "", err + } - stdout, _, err := ethChain.ForgeScript(ctx, ethUser.KeyName(), ethereum.ForgeScriptOpts{ + stdout, _, err := ethChain.ForgeScript(ctx, ethUser.KeyName(), foundry.ForgeScriptOpts{ ContractRootDir: "contracts", SolidityContract: "script/Token.s.sol", RawOptions: []string{"--sender", ethUser.FormattedAddress(), "--json"}, }) - require.NoError(t, err) + if err != nil { + return "", err + } tokenContractAddress, err := GetEthAddressFromStdout(string(stdout)) - require.NoError(t, err) - require.NotEmpty(t, tokenContractAddress) - require.True(t, ethcommon.IsHexAddress(tokenContractAddress)) + if err != nil { + return "", err + } + if tokenContractAddress == "" { + return "", fmt.Errorf("token contract address for (%s) chain is empty", ethChain.Config().Name) + } + if !ethcommon.IsHexAddress(tokenContractAddress) { + return "", fmt.Errorf("token contract address for (%s) chain is not a hex address", ethChain.Config().Name) + } - stdout, _, err = ethChain.ForgeScript(ctx, ethUser.KeyName(), ethereum.ForgeScriptOpts{ + stdout, _, err = ethChain.ForgeScript(ctx, ethUser.KeyName(), foundry.ForgeScriptOpts{ ContractRootDir: "contracts", SolidityContract: "script/Router.s.sol", RawOptions: []string{"--sender", ethUser.FormattedAddress(), "--json"}, }) - require.NoError(t, err) + if err != nil { + return "", err + } ethRouterContractAddress, err := GetEthAddressFromStdout(string(stdout)) - require.NoError(t, err) - require.NotEmpty(t, ethRouterContractAddress) - require.True(t, ethcommon.IsHexAddress(ethRouterContractAddress)) + if err != nil { + return "", err + } + if ethRouterContractAddress == "" { + return "", fmt.Errorf("router contract address for (%s) chain is empty", ethChain.Config().Name) + } + if !ethcommon.IsHexAddress(ethRouterContractAddress) { + return "", fmt.Errorf("router contract address for (%s) chain is not a hex address", ethChain.Config().Name) + } - return ethRouterContractAddress + return ethRouterContractAddress, nil } func SetupGaia(t *testing.T, ctx context.Context, exoChain *ExoChain) *errgroup.Group { gaia := exoChain.chain.(*cosmos.CosmosChain) - eg, egCtx := errgroup.WithContext(ctx) eg.Go(func() error { for _, genWallet := range exoChain.genWallets { @@ -192,21 +283,27 @@ func SetupGaia(t *testing.T, ctx context.Context, exoChain *ExoChain) *errgroup. return err } } - - amount := ibc.WalletAmount{ - Denom: gaia.Config().Denom, - Amount: sdkmath.NewInt(1_000_000), - } - + // Send 100 txs on gaia so that bifrost can automatically set the network fee // Sim testing can directly use bifrost to do this, right now, we can't, but may in the future val0 := gaia.GetNode() - for i := 0; i < 100/len(exoChain.genWallets) + 1; i++ { + amount := fmt.Sprintf("%s%s", sdkmath.NewInt(1_000_000).String(), gaia.Config().Denom) + for i := 0; i < 100/len(exoChain.genWallets)+1; i++ { + cmd := "" for j, genWallet := range exoChain.genWallets { toUser := exoChain.genWallets[(j+1)%len(exoChain.genWallets)] - go sendFunds(ctx, genWallet.KeyName(), toUser.FormattedAddress(), amount, val0) + bankSend := []string{"bank", "send", genWallet.KeyName(), toUser.FormattedAddress(), amount} + bankSend = val0.TxCommand(genWallet.KeyName(), bankSend...) + if j < len(exoChain.genWallets)-1 { + bankSend = append(bankSend, "&& ") + } + cmd = fmt.Sprintf("%s%s", cmd, strings.Join(bankSend, " ")) } - err := testutil.WaitForBlocks(ctx, 1, gaia) + _, _, err := val0.Exec(ctx, []string{"sh", "-c", cmd}, val0.Chain.Config().Env) + if err != nil { + fmt.Println("Gaia send funds err:", err) + } + err = NiceWaitForBlocks(ctx, 1, gaia) if err != nil { return err } @@ -242,4 +339,4 @@ func BuildGaiaWallets(t *testing.T, numWallets int, cfg ibc.ChainConfig) []ibc.W } return gaiaWallets -} \ No newline at end of file +} diff --git a/examples/thorchain/thorchain_hardfork_test.go b/examples/thorchain/thorchain_hardfork_test.go index 5fc5305a7..6e5d794bd 100644 --- a/examples/thorchain/thorchain_hardfork_test.go +++ b/examples/thorchain/thorchain_hardfork_test.go @@ -15,7 +15,6 @@ import ( "go.uber.org/zap/zaptest" ) -// mainnet-genesis.json will not be included in interchaintest repo // //go:embed mainnet-genesis.json var genesisBz []byte @@ -34,7 +33,7 @@ func TestThorchainHardFork(t *testing.T) { // ---------------------------- // Set up thorchain and others // ---------------------------- - thorchainChainSpec := ThorchainDefaultChainSpec(t.Name(), numThorchainValidators, numThorchainFullNodes, "", nil, nil) + thorchainChainSpec := ThorchainDefaultChainSpec(t.Name(), numThorchainValidators, numThorchainFullNodes, "", "", nil, nil) thorchainChainSpec.Bech32Prefix = "thor" thorchainChainSpec.Images[0].Version = "local-mainnet" diff --git a/examples/thorchain/thorchain_test.go b/examples/thorchain/thorchain_test.go index cef90e957..60b47caa0 100644 --- a/examples/thorchain/thorchain_test.go +++ b/examples/thorchain/thorchain_test.go @@ -3,6 +3,7 @@ package thorchain_test import ( "context" "fmt" + "math/rand" "sync" "testing" @@ -26,12 +27,13 @@ func TestThorchainSim(t *testing.T) { // Start non-thorchain chains exoChains := StartExoChains(t, ctx, client, network) gaiaEg := SetupGaia(t, ctx, exoChains["GAIA"]) - ethRouterContractAddress := SetupEthContracts(t, ctx, exoChains["ETH"]) + ethRouterContractAddress, bscRouterContractAddress, err := SetupContracts(ctx, exoChains["ETH"], exoChains["BSC"]) + require.NoError(t, err) // Start thorchain - thorchain := StartThorchain(t, ctx, client, network, exoChains, ethRouterContractAddress) + thorchain := StartThorchain(t, ctx, client, network, exoChains, ethRouterContractAddress, bscRouterContractAddress) require.NoError(t, gaiaEg.Wait()) // Wait for 100 transactions before starting tests - + // -------------------------------------------------------- // Bootstrap pool // -------------------------------------------------------- @@ -69,7 +71,7 @@ func TestThorchainSim(t *testing.T) { // -------------------------------------------------------- // Arb // -------------------------------------------------------- - _, err := features.Arb(t, ctx, thorchain, exoChains.GetChains()...) // Must add all active chains + _, err = features.Arb(t, ctx, thorchain, exoChains.GetChains()...) require.NoError(t, err) // -------------------------------------------------------- @@ -80,11 +82,15 @@ func TestThorchainSim(t *testing.T) { for i := range exoChainList { i := i fmt.Println("Chain:", i, "Name:", exoChainList[i].Config().Name) - if i%2 == 0 { - eg.Go(func() error { - return features.SingleSwap(t, egCtx, thorchain, exoChainList[i], exoChainList[i+1]) - }) + randomChain := rand.Intn(len(exoChainList)) + if i == randomChain && i == 0 { + randomChain++ + } else if i == randomChain { + randomChain-- } + eg.Go(func() error { + return features.DualSwap(t, ctx, thorchain, exoChainList[i], exoChainList[randomChain]) + }) } require.NoError(t, eg.Wait()) diff --git a/local-interchain/interchain/logs.go b/local-interchain/interchain/logs.go index aa9d85ad5..00c46dee6 100644 --- a/local-interchain/interchain/logs.go +++ b/local-interchain/interchain/logs.go @@ -8,7 +8,7 @@ import ( types "github.com/strangelove-ventures/interchaintest/local-interchain/interchain/types" "github.com/strangelove-ventures/interchaintest/v8/chain/cosmos" - "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum" + "github.com/strangelove-ventures/interchaintest/v8/chain/ethereum/foundry" "github.com/strangelove-ventures/interchaintest/v8/ibc" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -56,8 +56,8 @@ func DumpChainsInfoToLogs(configDir string, config *types.Config, chains []ibc.C } mainLogs.Chains = append(mainLogs.Chains, log) - case *ethereum.EthereumChain: - chainObj := chains[idx].(*ethereum.EthereumChain) + case *foundry.AnvilChain: + chainObj := chains[idx].(*foundry.AnvilChain) log := types.LogOutput{ ChainID: chainObj.Config().ChainID,