From 78aabee98d440bf9c61114a2efeebd01330d7f4f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 14:02:13 -0400 Subject: [PATCH 01/63] add skel --- cmd/root.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/root.go b/cmd/root.go index 7c157fba4..76e1cb466 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,6 +18,7 @@ import ( "github.com/ava-labs/avalanche-cli/cmd/configcmd" "github.com/ava-labs/avalanche-cli/cmd/backendcmd" + "github.com/ava-labs/avalanche-cli/cmd/contractcmd" "github.com/ava-labs/avalanche-cli/cmd/keycmd" "github.com/ava-labs/avalanche-cli/cmd/networkcmd" "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" @@ -98,6 +99,9 @@ in with avalanche subnet create myNewSubnet.`, // add teleporter command rootCmd.AddCommand(teleportercmd.NewCmd(app)) + // add contract command + rootCmd.AddCommand(contractcmd.NewCmd(app)) + cobrautils.ConfigureRootCmd(rootCmd) return rootCmd From a2d0d6e48994da00896be51c2fb424312471a441 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 14:06:10 -0400 Subject: [PATCH 02/63] add missing files --- cmd/contractcmd/contract.go | 26 +++ cmd/contractcmd/deploy.go | 21 ++ cmd/contractcmd/deployBridge.go | 326 ++++++++++++++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 cmd/contractcmd/contract.go create mode 100644 cmd/contractcmd/deploy.go create mode 100644 cmd/contractcmd/deployBridge.go diff --git a/cmd/contractcmd/contract.go b/cmd/contractcmd/contract.go new file mode 100644 index 000000000..52f866299 --- /dev/null +++ b/cmd/contractcmd/contract.go @@ -0,0 +1,26 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package contractcmd + +import ( + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/spf13/cobra" +) + +var app *application.Avalanche + +// avalanche contract +func NewCmd(injectedApp *application.Avalanche) *cobra.Command { + cmd := &cobra.Command{ + Use: "contract", + Short: "Interact with smart contracts", + Long: `The contract command suite provides a collection of tools for interacting +with Smart Contracts.`, + RunE: cobrautils.CommandSuiteUsage, + } + app = injectedApp + // contract deploy + cmd.AddCommand(newDeployCmd()) + return cmd +} diff --git a/cmd/contractcmd/deploy.go b/cmd/contractcmd/deploy.go new file mode 100644 index 000000000..2b5c0376a --- /dev/null +++ b/cmd/contractcmd/deploy.go @@ -0,0 +1,21 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package contractcmd + +import ( + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/spf13/cobra" +) + +// avalanche contract deploy +func newDeployCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "deploy", + Short: "Deploy smart contracts", + Long: `The deploy command suite provides deploy flows for different Smart Contracts.`, + RunE: cobrautils.CommandSuiteUsage, + } + // contract deploy + cmd.AddCommand(newDeployBridgeCmd()) + return cmd +} diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go new file mode 100644 index 000000000..0ee467506 --- /dev/null +++ b/cmd/contractcmd/deployBridge.go @@ -0,0 +1,326 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package contractcmd + +import ( + "fmt" + + cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" + "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" + "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/networkoptions" + "github.com/ava-labs/avalanche-cli/pkg/prompts" + "github.com/ava-labs/avalanche-cli/pkg/subnet" + "github.com/ava-labs/avalanche-cli/pkg/teleporter" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanchego/ids" + + "github.com/spf13/cobra" +) + +type DeployFlags struct { + Network networkoptions.NetworkFlags + SubnetName string + BlockchainID string + CChain bool + PrivateKey string + KeyName string + GenesisKey bool + DeployMessenger bool + DeployRegistry bool + TeleporterVersion string + RPCURL string +} + +const ( + cChainAlias = "C" + cChainName = "c-chain" +) + +var ( + deploySupportedNetworkOptions = []networkoptions.NetworkOption{ + networkoptions.Local, + networkoptions.Devnet, + networkoptions.Fuji, + } + deployFlags DeployFlags +) + +// avalanche contract deploy bridge +func newDeployBridgeCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "bridge", + Short: "Deploys Tokeb Bridge into a given Network and Subnets", + Long: "Deploys Tokeb Bridge into a given Network and Subnets", + RunE: deployBridge, + Args: cobrautils.ExactArgs(0), + } + networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deploySupportedNetworkOptions) + cmd.Flags().StringVar(&deployFlags.SubnetName, "subnet", "", "deploy teleporter into the given CLI subnet") + cmd.Flags().StringVar(&deployFlags.BlockchainID, "blockchain-id", "", "deploy teleporter into the given blockchain ID/Alias") + cmd.Flags().BoolVar(&deployFlags.CChain, "c-chain", false, "deploy teleporter into C-Chain") + cmd.Flags().StringVar(&deployFlags.PrivateKey, "private-key", "", "private key to use to fund teleporter deploy)") + cmd.Flags().StringVar(&deployFlags.KeyName, "key", "", "CLI stored key to use to fund teleporter deploy)") + cmd.Flags().BoolVar(&deployFlags.GenesisKey, "genesis-key", false, "use genesis aidrop key to fund teleporter deploy") + cmd.Flags().BoolVar(&deployFlags.DeployMessenger, "deploy-messenger", true, "deploy Teleporter Messenger") + cmd.Flags().BoolVar(&deployFlags.DeployRegistry, "deploy-registry", true, "deploy Teleporter Registry") + cmd.Flags().StringVar(&deployFlags.TeleporterVersion, "version", "latest", "version to deploy") + cmd.Flags().StringVar(&deployFlags.RPCURL, "rpc-url", "", "use the given RPC URL to connect to the subnet") + return cmd +} + +func deployBridge(_ *cobra.Command, args []string) error { + return CallDeployBridge(args, deployFlags) +} + +func CallDeployBridge(_ []string, flags DeployFlags) error { + network, err := networkoptions.GetNetworkFromCmdLineFlags( + app, + "On what Network do you want to deploy the Teleporter Messenger?", + flags.Network, + true, + false, + deploySupportedNetworkOptions, + "", + ) + if err != nil { + return err + } + if !cmdflags.EnsureMutuallyExclusive([]bool{flags.SubnetName != "", flags.BlockchainID != "", flags.CChain}) { + return fmt.Errorf("--subnet, --blockchain-id and --cchain are mutually exclusive flags") + } + if !cmdflags.EnsureMutuallyExclusive([]bool{flags.PrivateKey != "", flags.KeyName != "", flags.GenesisKey}) { + return fmt.Errorf("--private-key, --key and --genesis-key are mutually exclusive flags") + } + if !flags.DeployMessenger && !flags.DeployRegistry { + return fmt.Errorf("you should set at least one of --deploy-messenger/--deploy-registry to true") + } + if flags.SubnetName == "" && flags.BlockchainID == "" && !flags.CChain { + // fill flags based on user prompts + blockchainIDOptions := []string{ + "Get Blockchain ID from an existing subnet (deployed with avalanche subnet deploy)", + "Use C-Chain Blockchain ID", + "Custom", + } + blockchainIDOption, err := app.Prompt.CaptureList("Which Blockchain ID would you like to deploy Teleporter to?", blockchainIDOptions) + if err != nil { + return err + } + switch blockchainIDOption { + case blockchainIDOptions[0]: + subnetNames, err := app.GetSubnetNames() + if err != nil { + return err + } + flags.SubnetName, err = app.Prompt.CaptureList( + "Choose a Subnet", + subnetNames, + ) + if err != nil { + return err + } + case blockchainIDOptions[1]: + flags.CChain = true + default: + flags.BlockchainID, err = app.Prompt.CaptureString("Blockchain ID/Alias") + if err != nil { + return err + } + } + } + + var ( + blockchainID string + teleporterSubnetDesc string + privateKey = flags.PrivateKey + teleporterVersion string + ) + switch { + case flags.SubnetName != "": + teleporterSubnetDesc = flags.SubnetName + sc, err := app.LoadSidecar(flags.SubnetName) + if err != nil { + return fmt.Errorf("failed to load sidecar: %w", err) + } + if b, _, err := subnetcmd.HasSubnetEVMGenesis(flags.SubnetName); err != nil { + return err + } else if !b { + return fmt.Errorf("only Subnet-EVM based vms can be used for teleporter") + } + if sc.Networks[network.Name()].BlockchainID == ids.Empty { + return fmt.Errorf("subnet has not been deployed to %s", network.Name()) + } + blockchainID = sc.Networks[network.Name()].BlockchainID.String() + if sc.TeleporterVersion != "" { + teleporterVersion = sc.TeleporterVersion + } + if sc.TeleporterKey != "" { + k, err := app.GetKey(sc.TeleporterKey, network, true) + if err != nil { + return err + } + privateKey = k.PrivKeyHex() + } + case flags.BlockchainID != "": + teleporterSubnetDesc = flags.BlockchainID + blockchainID = flags.BlockchainID + case flags.CChain: + teleporterSubnetDesc = cChainName + blockchainID = cChainAlias + } + var chainID ids.ID + if flags.CChain || !network.StandardPublicEndpoint() { + chainID, err = utils.GetChainID(network.Endpoint, blockchainID) + if err != nil { + return err + } + } else { + chainID, err = ids.FromString(blockchainID) + if err != nil { + return err + } + } + createChainTx, err := utils.GetBlockchainTx(network.Endpoint, chainID) + if err != nil { + return err + } + if !utils.ByteSliceIsSubnetEvmGenesis(createChainTx.GenesisData) { + return fmt.Errorf("teleporter can only be deployed to Subnet-EVM based vms") + } + if flags.KeyName != "" { + k, err := app.GetKey(flags.KeyName, network, false) + if err != nil { + return err + } + privateKey = k.PrivKeyHex() + } + _, genesisAddress, genesisPrivateKey, err := subnet.GetSubnetAirdropKeyInfo(app, network, flags.SubnetName, createChainTx.GenesisData) + if err != nil { + return err + } + if flags.GenesisKey { + privateKey = genesisPrivateKey + } + if privateKey == "" { + cliKeyOpt := "Get private key from an existing stored key (created from avalanche key create or avalanche key import)" + customKeyOpt := "Custom" + genesisKeyOpt := fmt.Sprintf("Use the private key of the Genesis Aidrop address %s", genesisAddress) + keyOptions := []string{cliKeyOpt, customKeyOpt} + if genesisPrivateKey != "" { + keyOptions = []string{genesisKeyOpt, cliKeyOpt, customKeyOpt} + } + keyOption, err := app.Prompt.CaptureList("Which private key do you want to use to pay fees?", keyOptions) + if err != nil { + return err + } + switch keyOption { + case cliKeyOpt: + keyName, err := prompts.CaptureKeyName(app.Prompt, "pay fees", app.GetKeyDir(), true) + if err != nil { + return err + } + k, err := app.GetKey(keyName, network, false) + if err != nil { + return err + } + privateKey = k.PrivKeyHex() + case customKeyOpt: + privateKey, err = app.Prompt.CaptureString("Private Key") + if err != nil { + return err + } + case genesisKeyOpt: + privateKey = genesisPrivateKey + } + } + if flags.TeleporterVersion != "" && flags.TeleporterVersion != "latest" { + teleporterVersion = flags.TeleporterVersion + } else if teleporterVersion == "" { + teleporterInfo, err := teleporter.GetInfo(app) + if err != nil { + return err + } + teleporterVersion = teleporterInfo.Version + } + // deploy to subnet + rpcURL := network.BlockchainEndpoint(blockchainID) + if flags.RPCURL != "" { + rpcURL = flags.RPCURL + } + td := teleporter.Deployer{} + alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddress, err := td.Deploy( + app.GetTeleporterBinDir(), + teleporterVersion, + teleporterSubnetDesc, + rpcURL, + privateKey, + flags.DeployMessenger, + flags.DeployRegistry, + ) + if err != nil { + return err + } + if flags.SubnetName != "" && !alreadyDeployed { + // update sidecar + sc, err := app.LoadSidecar(flags.SubnetName) + if err != nil { + return fmt.Errorf("failed to load sidecar: %w", err) + } + sc.TeleporterReady = true + sc.TeleporterVersion = teleporterVersion + networkInfo := sc.Networks[network.Name()] + if teleporterMessengerAddress != "" { + networkInfo.TeleporterMessengerAddress = teleporterMessengerAddress + } + if teleporterRegistryAddress != "" { + networkInfo.TeleporterRegistryAddress = teleporterRegistryAddress + } + sc.Networks[network.Name()] = networkInfo + if err := app.UpdateSidecar(&sc); err != nil { + return err + } + } + // automatic deploy to cchain for local/devnet + if !flags.CChain && (network.Kind == models.Local || network.Kind == models.Devnet) { + ewoq, err := app.GetKey("ewoq", network, false) + if err != nil { + return err + } + alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddress, err := td.Deploy( + app.GetTeleporterBinDir(), + teleporterVersion, + cChainName, + network.BlockchainEndpoint(cChainAlias), + ewoq.PrivKeyHex(), + flags.DeployMessenger, + flags.DeployRegistry, + ) + if err != nil { + return err + } + if !alreadyDeployed { + if network.Kind == models.Local { + if err := subnet.WriteExtraLocalNetworkData(app, teleporterMessengerAddress, teleporterRegistryAddress); err != nil { + return err + } + } + if network.ClusterName != "" { + clusterConfig, err := app.GetClusterConfig(network.ClusterName) + if err != nil { + return err + } + if teleporterMessengerAddress != "" { + clusterConfig.ExtraNetworkData.CChainTeleporterMessengerAddress = teleporterMessengerAddress + } + if teleporterRegistryAddress != "" { + clusterConfig.ExtraNetworkData.CChainTeleporterRegistryAddress = teleporterRegistryAddress + } + if err := app.SetClusterConfig(network.ClusterName, clusterConfig); err != nil { + return err + } + } + } + } + return nil +} From 7781e6263086a191d22c4d8a7893acddb0fde7cf Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 14:40:28 -0400 Subject: [PATCH 03/63] check for forge to have been installed --- cmd/contractcmd/deployBridge.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 0ee467506..3781714fb 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -3,7 +3,9 @@ package contractcmd import ( + "errors" "fmt" + "os/exec" cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" @@ -14,6 +16,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/subnet" "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" "github.com/spf13/cobra" @@ -47,6 +50,21 @@ var ( deployFlags DeployFlags ) +// ~/.foundry/bin/forge +// make a first install just using this two commands and assuming bash +// curl -L https://foundry.paradigm.xyz | bash +// foundryup (~/.foundry/bin/foundryup) +func checkForgeIsInstalled() error { + if err := exec.Command("~/.foundry/bin/forge").Run(); errors.Is(err, exec.ErrNotFound) { + ux.Logger.PrintToUser("Forge tool (from foundry toolset) is not available. It is a necessary dependency for CLI to compile smart contracts.") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Please follow install instructions at https://book.getfoundry.sh/getting-started/installation and try again") + ux.Logger.PrintToUser("") + return err + } + return nil +} + // avalanche contract deploy bridge func newDeployBridgeCmd() *cobra.Command { cmd := &cobra.Command{ @@ -75,6 +93,9 @@ func deployBridge(_ *cobra.Command, args []string) error { } func CallDeployBridge(_ []string, flags DeployFlags) error { + if err := checkForgeIsInstalled(); err != nil { + return err + } network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "On what Network do you want to deploy the Teleporter Messenger?", From b09875713c31a9b56e8f65ad3bbefad6655dfc93 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 17:11:20 -0400 Subject: [PATCH 04/63] add comments --- cmd/contractcmd/deployBridge.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 3781714fb..1288d7604 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -54,6 +54,11 @@ var ( // make a first install just using this two commands and assuming bash // curl -L https://foundry.paradigm.xyz | bash // foundryup (~/.foundry/bin/foundryup) +// don't forget this +// inside avalanche-starter-kit repo +// git submodule update --init --recursive +// forge create --rpc-url http://127.0.0.1:9650/ext/bc/2tvKVYuMmKg2NwGWKtaHUgnS8Wc35RaAXyTNm9riDP622DEYgy/rpc --private-key 6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb src/5-native-token-bridge/ExampleWNATV.sol:WNATV +// deployer to 0x3058749395527bF64e687A05d23d38cfeC9e7682 func checkForgeIsInstalled() error { if err := exec.Command("~/.foundry/bin/forge").Run(); errors.Is(err, exec.ErrNotFound) { ux.Logger.PrintToUser("Forge tool (from foundry toolset) is not available. It is a necessary dependency for CLI to compile smart contracts.") From 9c2cb9cf155333fb108f5428912f5a5334ed94b8 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 17:37:43 -0400 Subject: [PATCH 05/63] start automating foundry install --- cmd/contractcmd/deployBridge.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 1288d7604..0b05558d2 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -3,7 +3,6 @@ package contractcmd import ( - "errors" "fmt" "os/exec" @@ -59,15 +58,23 @@ var ( // git submodule update --init --recursive // forge create --rpc-url http://127.0.0.1:9650/ext/bc/2tvKVYuMmKg2NwGWKtaHUgnS8Wc35RaAXyTNm9riDP622DEYgy/rpc --private-key 6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb src/5-native-token-bridge/ExampleWNATV.sol:WNATV // deployer to 0x3058749395527bF64e687A05d23d38cfeC9e7682 -func checkForgeIsInstalled() error { - if err := exec.Command("~/.foundry/bin/forge").Run(); errors.Is(err, exec.ErrNotFound) { - ux.Logger.PrintToUser("Forge tool (from foundry toolset) is not available. It is a necessary dependency for CLI to compile smart contracts.") +func foundryIsInstalled() bool { + return utils.IsExecutable(utils.ExpandHome("~/.foundry/bin/forge")) +} + +func installFoundry() error { + ux.Logger.PrintToUser("Installing Foundry") + ux.Logger.PrintToUser("") + out, err := exec.Command(utils.ExpandHome("~/.foundry/bin/foundryup")).Output() + ux.Logger.PrintToUser(string(out)) + if err != nil { + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Foundry toolset is not available and couldn't automatically be installed. It is a necessary dependency for CLI to compile smart contracts.") ux.Logger.PrintToUser("") ux.Logger.PrintToUser("Please follow install instructions at https://book.getfoundry.sh/getting-started/installation and try again") ux.Logger.PrintToUser("") - return err } - return nil + return err } // avalanche contract deploy bridge @@ -98,9 +105,11 @@ func deployBridge(_ *cobra.Command, args []string) error { } func CallDeployBridge(_ []string, flags DeployFlags) error { - if err := checkForgeIsInstalled(); err != nil { - return err + if !foundryIsInstalled() { + return installFoundry() } + return installFoundry() + return nil network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "On what Network do you want to deploy the Teleporter Messenger?", From aceff272bbbcc838cd3ef8d38ff382bbbae2d214 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 20 May 2024 18:18:26 -0400 Subject: [PATCH 06/63] added automated froundry install --- cmd/contractcmd/deployBridge.go | 46 +++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 0b05558d2..9a53a0476 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -4,7 +4,9 @@ package contractcmd import ( "fmt" + "io" "os/exec" + "strings" cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" @@ -49,22 +51,50 @@ var ( deployFlags DeployFlags ) -// ~/.foundry/bin/forge -// make a first install just using this two commands and assuming bash -// curl -L https://foundry.paradigm.xyz | bash -// foundryup (~/.foundry/bin/foundryup) -// don't forget this // inside avalanche-starter-kit repo // git submodule update --init --recursive -// forge create --rpc-url http://127.0.0.1:9650/ext/bc/2tvKVYuMmKg2NwGWKtaHUgnS8Wc35RaAXyTNm9riDP622DEYgy/rpc --private-key 6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb src/5-native-token-bridge/ExampleWNATV.sol:WNATV -// deployer to 0x3058749395527bF64e687A05d23d38cfeC9e7682 + func foundryIsInstalled() bool { return utils.IsExecutable(utils.ExpandHome("~/.foundry/bin/forge")) } func installFoundry() error { ux.Logger.PrintToUser("Installing Foundry") - ux.Logger.PrintToUser("") + downloadCmd := exec.Command("curl", "-L", "https://foundry.paradigm.xyz") + installCmd := exec.Command("sh") + var downloadOutbuf, downloadErrbuf strings.Builder + downloadCmdStdoutPipe, err := downloadCmd.StdoutPipe() + if err != nil { + return err + } + downloadCmd.Stderr = &downloadErrbuf + installCmd.Stdin = io.TeeReader(downloadCmdStdoutPipe, &downloadOutbuf) + var installOutbuf, installErrbuf strings.Builder + installCmd.Stdout = &installOutbuf + installCmd.Stderr = &installErrbuf + if err := installCmd.Start(); err != nil { + return err + } + if err := downloadCmd.Run(); err != nil { + if downloadOutbuf.String() != "" { + ux.Logger.PrintToUser(strings.TrimSuffix(downloadOutbuf.String(), "\n")) + } + if downloadErrbuf.String() != "" { + ux.Logger.PrintToUser(strings.TrimSuffix(downloadErrbuf.String(), "\n")) + } + return err + } + if err := installCmd.Wait(); err != nil { + if installOutbuf.String() != "" { + ux.Logger.PrintToUser(strings.TrimSuffix(installOutbuf.String(), "\n")) + } + if installErrbuf.String() != "" { + ux.Logger.PrintToUser(strings.TrimSuffix(installErrbuf.String(), "\n")) + } + ux.Logger.PrintToUser("installation failed: %s", err.Error()) + return err + } + ux.Logger.PrintToUser(strings.TrimSuffix(installOutbuf.String(), "\n")) out, err := exec.Command(utils.ExpandHome("~/.foundry/bin/foundryup")).Output() ux.Logger.PrintToUser(string(out)) if err != nil { From 17e6c81d1d37d11d86b6ae8c06467f90dcc4c26b Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 21 May 2024 11:15:53 -0400 Subject: [PATCH 07/63] start working agains teleporter repo --- cmd/contractcmd/deployBridge.go | 77 +++++++++++++++++++++++++++++---- pkg/vm/createCustom.go | 11 +++-- 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 9a53a0476..5dbbfa59d 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os/exec" + "path/filepath" "strings" cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" @@ -18,6 +19,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ava-labs/avalanchego/ids" "github.com/spf13/cobra" @@ -43,19 +45,21 @@ const ( ) var ( - deploySupportedNetworkOptions = []networkoptions.NetworkOption{ + deployBridgeSupportedNetworkOptions = []networkoptions.NetworkOption{ networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, } - deployFlags DeployFlags + deployFlags DeployFlags + foundryupPath = utils.ExpandHome("~/.foundry/bin/foundryup") + forgePath = utils.ExpandHome("~/.foundry/bin/forge") ) // inside avalanche-starter-kit repo // git submodule update --init --recursive func foundryIsInstalled() bool { - return utils.IsExecutable(utils.ExpandHome("~/.foundry/bin/forge")) + return utils.IsExecutable(forgePath) } func installFoundry() error { @@ -95,7 +99,7 @@ func installFoundry() error { return err } ux.Logger.PrintToUser(strings.TrimSuffix(installOutbuf.String(), "\n")) - out, err := exec.Command(utils.ExpandHome("~/.foundry/bin/foundryup")).Output() + out, err := exec.Command(foundryupPath).CombinedOutput() ux.Logger.PrintToUser(string(out)) if err != nil { ux.Logger.PrintToUser("") @@ -116,7 +120,7 @@ func newDeployBridgeCmd() *cobra.Command { RunE: deployBridge, Args: cobrautils.ExactArgs(0), } - networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deploySupportedNetworkOptions) + networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deployBridgeSupportedNetworkOptions) cmd.Flags().StringVar(&deployFlags.SubnetName, "subnet", "", "deploy teleporter into the given CLI subnet") cmd.Flags().StringVar(&deployFlags.BlockchainID, "blockchain-id", "", "deploy teleporter into the given blockchain ID/Alias") cmd.Flags().BoolVar(&deployFlags.CChain, "c-chain", false, "deploy teleporter into C-Chain") @@ -135,23 +139,78 @@ func deployBridge(_ *cobra.Command, args []string) error { } func CallDeployBridge(_ []string, flags DeployFlags) error { + if err := vm.CheckGitIsInstalled(); err != nil { + return err + } if !foundryIsInstalled() { return installFoundry() } - return installFoundry() - return nil network, err := networkoptions.GetNetworkFromCmdLineFlags( app, - "On what Network do you want to deploy the Teleporter Messenger?", + "On what Network do you want to deploy the Teleporter bridge?", flags.Network, true, false, - deploySupportedNetworkOptions, + deployBridgeSupportedNetworkOptions, "", ) if err != nil { return err } + bridgeSrcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") + // install bridge src dependencies + cmd := exec.Command( + "git", + "submodule", + "update", + "--init", + "--recursive", + ) + cmd.Dir = bridgeSrcDir + out, err := cmd.CombinedOutput() + if err != nil { + ux.Logger.PrintToUser(string(out)) + return err + } + // build teleporter contracts + cmd = exec.Command( + forgePath, + "build", + ) + cmd.Dir = filepath.Join(bridgeSrcDir, "contracts") + out, err = cmd.CombinedOutput() + if err != nil { + ux.Logger.PrintToUser(string(out)) + return err + } + return nil + cmd = exec.Command( + forgePath, + "install", + "openzeppelin/openzeppelin-contracts@v4.8.1", + "--no-commit", + ) + cmd.Dir = utils.ExpandHome("~/Workspace/projects/avalanche-cli/") + out, err = cmd.CombinedOutput() + fmt.Println(string(out)) + fmt.Println(err) + createCmd := exec.Command( + forgePath, + "create", + "--rpc-url", + network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), + "--private-key", + "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + "src/5-native-token-bridge/WrappedNativeToken.sol:WrappedNativeToken", + "--constructor-args", + "Wrapped TOK Name", + "WTOK", + ) + createCmd.Dir = utils.ExpandHome("~/Workspace/projects/avalanche-cli/") + out, err = createCmd.CombinedOutput() + fmt.Println(string(out)) + fmt.Println(err) + return nil if !cmdflags.EnsureMutuallyExclusive([]bool{flags.SubnetName != "", flags.BlockchainID != "", flags.CChain}) { return fmt.Errorf("--subnet, --blockchain-id and --cchain are mutually exclusive flags") } diff --git a/pkg/vm/createCustom.go b/pkg/vm/createCustom.go index 5a9fcd3b4..0c6b527c8 100644 --- a/pkg/vm/createCustom.go +++ b/pkg/vm/createCustom.go @@ -3,7 +3,6 @@ package vm import ( - "errors" "fmt" "os" "os/exec" @@ -144,22 +143,22 @@ func SetCustomVMSourceCodeFields(app *application.Avalanche, sc *models.Sidecar, return nil } -func checkGitIsInstalled() error { - if err := exec.Command("git").Run(); errors.Is(err, exec.ErrNotFound) { +func CheckGitIsInstalled() error { + err := exec.Command("git", "--version").Run() + if err != nil { ux.Logger.PrintToUser("Git tool is not available. It is a necessary dependency for CLI to import a custom VM.") ux.Logger.PrintToUser("") ux.Logger.PrintToUser("Please follow install instructions at https://git-scm.com/book/en/v2/Getting-Started-Installing-Git and try again") ux.Logger.PrintToUser("") - return err } - return nil + return err } func BuildCustomVM( app *application.Avalanche, sc *models.Sidecar, ) error { - if err := checkGitIsInstalled(); err != nil { + if err := CheckGitIsInstalled(); err != nil { return err } From fe993a1ff12e3d9a06a9fd973677d341a48c2a9e Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 21 May 2024 13:06:13 -0400 Subject: [PATCH 08/63] deploying contract given abi and bin files --- cmd/contractcmd/deployBridge.go | 62 ++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 5dbbfa59d..42330644c 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -5,6 +5,7 @@ package contractcmd import ( "fmt" "io" + "os" "os/exec" "path/filepath" "strings" @@ -12,6 +13,7 @@ import ( cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" + "github.com/ava-labs/avalanche-cli/pkg/evm" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/networkoptions" "github.com/ava-labs/avalanche-cli/pkg/prompts" @@ -21,6 +23,8 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/subnet-evm/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" "github.com/spf13/cobra" ) @@ -55,9 +59,6 @@ var ( forgePath = utils.ExpandHome("~/.foundry/bin/forge") ) -// inside avalanche-starter-kit repo -// git submodule update --init --recursive - func foundryIsInstalled() bool { return utils.IsExecutable(forgePath) } @@ -138,6 +139,52 @@ func deployBridge(_ *cobra.Command, args []string) error { return CallDeployBridge(args, deployFlags) } +func test( + rpcURL string, + prefundedPrivateKey string, +) error { + srcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") + abiPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.abi.json") + binPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.bin") + abiBytes, err := os.ReadFile(abiPath) + if err != nil { + return err + } + binBytes, err := os.ReadFile(binPath) + if err != nil { + return err + } + metadata := &bind.MetaData{ + ABI: string(abiBytes), + Bin: string(binBytes), + } + abi, err := metadata.GetAbi() + if err != nil { + return err + } + bin := common.FromHex(metadata.Bin) + client, err := evm.GetClient(rpcURL) + if err != nil { + return err + } + defer client.Close() + txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) + if err != nil { + return err + } + address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, "Al fin", "ALFIN") + if err != nil { + return err + } + if _, success, err := evm.WaitForTransaction(client, tx); err != nil { + return err + } else if !success { + return fmt.Errorf("failed receipt status deploying contract") + } + fmt.Println(address) + return nil +} + func CallDeployBridge(_ []string, flags DeployFlags) error { if err := vm.CheckGitIsInstalled(); err != nil { return err @@ -157,6 +204,10 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if err != nil { return err } + return test( + network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), + "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + ) bridgeSrcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") // install bridge src dependencies cmd := exec.Command( @@ -172,10 +223,13 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { ux.Logger.PrintToUser(string(out)) return err } - // build teleporter contracts + // build teleporter contracts bytecode + abi cmd = exec.Command( forgePath, "build", + "--extra-output-files", + "abi", + "bin", ) cmd.Dir = filepath.Join(bridgeSrcDir, "contracts") out, err = cmd.CombinedOutput() From a158f30c71116836d09aaa8a39665b3eed6e290d Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 21 May 2024 16:11:49 -0400 Subject: [PATCH 09/63] adding destination deploy --- cmd/contractcmd/deployBridge.go | 235 +++++++++++++++++++++++++------- 1 file changed, 185 insertions(+), 50 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 42330644c..b21457a0e 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -139,20 +139,22 @@ func deployBridge(_ *cobra.Command, args []string) error { return CallDeployBridge(args, deployFlags) } -func test( +func deployWrappedNativeToken( + srcDir string, rpcURL string, prefundedPrivateKey string, -) error { - srcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") + tokenName string, +) (common.Address, error) { + srcDir = utils.ExpandHome(srcDir) abiPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.abi.json") binPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.bin") abiBytes, err := os.ReadFile(abiPath) if err != nil { - return err + return common.Address{}, err } binBytes, err := os.ReadFile(binPath) if err != nil { - return err + return common.Address{}, err } metadata := &bind.MetaData{ ABI: string(abiBytes), @@ -160,29 +162,143 @@ func test( } abi, err := metadata.GetAbi() if err != nil { - return err + return common.Address{}, err } bin := common.FromHex(metadata.Bin) client, err := evm.GetClient(rpcURL) if err != nil { - return err + return common.Address{}, err } defer client.Close() txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) if err != nil { - return err + return common.Address{}, err } - address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, "Al fin", "ALFIN") + address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, tokenName) if err != nil { - return err + return common.Address{}, err } if _, success, err := evm.WaitForTransaction(client, tx); err != nil { - return err + return common.Address{}, err } else if !success { - return fmt.Errorf("failed receipt status deploying contract") + return common.Address{}, fmt.Errorf("failed receipt status deploying contract") } - fmt.Println(address) - return nil + return address, nil +} + +func deployNativeTokenSource( + srcDir string, + rpcURL string, + prefundedPrivateKey string, + teleporterRegistryAddress common.Address, + teleporterManagerAddress common.Address, + wrappedNativeTokenAddress common.Address, +) (common.Address, error) { + srcDir = utils.ExpandHome(srcDir) + abiPath := filepath.Join(srcDir, "contracts/out/NativeTokenSource.sol/NativeTokenSource.abi.json") + binPath := filepath.Join(srcDir, "contracts/out/NativeTokenSource.sol/NativeTokenSource.bin") + abiBytes, err := os.ReadFile(abiPath) + if err != nil { + return common.Address{}, err + } + binBytes, err := os.ReadFile(binPath) + if err != nil { + return common.Address{}, err + } + metadata := &bind.MetaData{ + ABI: string(abiBytes), + Bin: string(binBytes), + } + abi, err := metadata.GetAbi() + if err != nil { + return common.Address{}, err + } + bin := common.FromHex(metadata.Bin) + client, err := evm.GetClient(rpcURL) + if err != nil { + return common.Address{}, err + } + defer client.Close() + txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) + if err != nil { + return common.Address{}, err + } + address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, teleporterRegistryAddress, teleporterManagerAddress, wrappedNativeTokenAddress) + if err != nil { + return common.Address{}, err + } + if _, success, err := evm.WaitForTransaction(client, tx); err != nil { + return common.Address{}, err + } else if !success { + return common.Address{}, fmt.Errorf("failed receipt status deploying contract") + } + return address, nil +} + +type TeleporterTokenDestinationSettings struct { + TeleporterRegistryAddress common.Address + TeleporterManager common.Address + SourceBlockchainID [32]byte + TokenSourceAddress common.Address +} + +func deployERC20Destination( + srcDir string, + rpcURL string, + prefundedPrivateKey string, + teleporterTokenDestinationSettings TeleporterTokenDestinationSettings, + tokenName string, + tokenSymbol string, + tokenDecimals uint8, +) (common.Address, error) { + srcDir = utils.ExpandHome(srcDir) + abiPath := filepath.Join(srcDir, "contracts/out/ERC20Destination.sol/ERC20Destination.abi.json") + binPath := filepath.Join(srcDir, "contracts/out/ERC20Destination.sol/ERC20Destination.bin") + abiBytes, err := os.ReadFile(abiPath) + if err != nil { + return common.Address{}, err + } + binBytes, err := os.ReadFile(binPath) + if err != nil { + return common.Address{}, err + } + metadata := &bind.MetaData{ + ABI: string(abiBytes), + Bin: string(binBytes), + } + abi, err := metadata.GetAbi() + if err != nil { + return common.Address{}, err + } + bin := common.FromHex(metadata.Bin) + client, err := evm.GetClient(rpcURL) + if err != nil { + return common.Address{}, err + } + defer client.Close() + txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) + if err != nil { + return common.Address{}, err + } + address, tx, _, err := bind.DeployContract( + txOpts, + *abi, + bin, + client, + teleporterTokenDestinationSettings, + tokenName, + tokenSymbol, + tokenDecimals, + ) + if err != nil { + return common.Address{}, err + } + if _, success, err := evm.WaitForTransaction(client, tx); err != nil { + return common.Address{}, err + } else if !success { + return common.Address{}, fmt.Errorf("failed receipt status deploying contract") + } + return address, nil } func CallDeployBridge(_ []string, flags DeployFlags) error { @@ -204,11 +320,57 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if err != nil { return err } - return test( + bridgeSrcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") + wrappedNativeTokenAddress, err := deployWrappedNativeToken( + bridgeSrcDir, network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + "TOK", ) - bridgeSrcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") + if err != nil { + return err + } + teleporterRegistryAddress := common.HexToAddress("0xbD9e8eC38E43d34CAB4194881B9BF39d639D7Bd3") + teleporterManagerAddress := common.HexToAddress("0x13D42261c6970023fBD486A24AB57c7c8e5DfcB9") + nativeTokenSourceAddress, err := deployNativeTokenSource( + bridgeSrcDir, + network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), + "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + teleporterRegistryAddress, + teleporterManagerAddress, + wrappedNativeTokenAddress, + ) + if err != nil { + return err + } + teleporterRegistryAddress = common.HexToAddress("0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25") + teleporterManagerAddress = common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC") + sourceBlockchainID, err := ids.FromString("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL") + if err != nil { + return err + } + teleporterTokenDestinationSettings := TeleporterTokenDestinationSettings{ + TeleporterRegistryAddress: teleporterRegistryAddress, + TeleporterManager: teleporterManagerAddress, + SourceBlockchainID: sourceBlockchainID, + TokenSourceAddress: wrappedNativeTokenAddress, + } + erc20DestinationAddress, err := deployERC20Destination( + bridgeSrcDir, + network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), + "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + teleporterTokenDestinationSettings, + "Wrapped Token", + "WTOK", + 18, + ) + if err != nil { + return err + } + fmt.Println(wrappedNativeTokenAddress) + fmt.Println(nativeTokenSourceAddress) + fmt.Println(erc20DestinationAddress) + return nil // install bridge src dependencies cmd := exec.Command( "git", @@ -238,33 +400,6 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { return err } return nil - cmd = exec.Command( - forgePath, - "install", - "openzeppelin/openzeppelin-contracts@v4.8.1", - "--no-commit", - ) - cmd.Dir = utils.ExpandHome("~/Workspace/projects/avalanche-cli/") - out, err = cmd.CombinedOutput() - fmt.Println(string(out)) - fmt.Println(err) - createCmd := exec.Command( - forgePath, - "create", - "--rpc-url", - network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), - "--private-key", - "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", - "src/5-native-token-bridge/WrappedNativeToken.sol:WrappedNativeToken", - "--constructor-args", - "Wrapped TOK Name", - "WTOK", - ) - createCmd.Dir = utils.ExpandHome("~/Workspace/projects/avalanche-cli/") - out, err = createCmd.CombinedOutput() - fmt.Println(string(out)) - fmt.Println(err) - return nil if !cmdflags.EnsureMutuallyExclusive([]bool{flags.SubnetName != "", flags.BlockchainID != "", flags.CChain}) { return fmt.Errorf("--subnet, --blockchain-id and --cchain are mutually exclusive flags") } @@ -427,7 +562,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { rpcURL = flags.RPCURL } td := teleporter.Deployer{} - alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddress, err := td.Deploy( + alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddressStr, err := td.Deploy( app.GetTeleporterBinDir(), teleporterVersion, teleporterSubnetDesc, @@ -451,8 +586,8 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if teleporterMessengerAddress != "" { networkInfo.TeleporterMessengerAddress = teleporterMessengerAddress } - if teleporterRegistryAddress != "" { - networkInfo.TeleporterRegistryAddress = teleporterRegistryAddress + if teleporterRegistryAddressStr != "" { + networkInfo.TeleporterRegistryAddress = teleporterRegistryAddressStr } sc.Networks[network.Name()] = networkInfo if err := app.UpdateSidecar(&sc); err != nil { @@ -465,7 +600,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if err != nil { return err } - alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddress, err := td.Deploy( + alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddressStr, err := td.Deploy( app.GetTeleporterBinDir(), teleporterVersion, cChainName, @@ -479,7 +614,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { } if !alreadyDeployed { if network.Kind == models.Local { - if err := subnet.WriteExtraLocalNetworkData(app, teleporterMessengerAddress, teleporterRegistryAddress); err != nil { + if err := subnet.WriteExtraLocalNetworkData(app, teleporterMessengerAddress, teleporterRegistryAddressStr); err != nil { return err } } @@ -491,8 +626,8 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if teleporterMessengerAddress != "" { clusterConfig.ExtraNetworkData.CChainTeleporterMessengerAddress = teleporterMessengerAddress } - if teleporterRegistryAddress != "" { - clusterConfig.ExtraNetworkData.CChainTeleporterRegistryAddress = teleporterRegistryAddress + if teleporterRegistryAddressStr != "" { + clusterConfig.ExtraNetworkData.CChainTeleporterRegistryAddress = teleporterRegistryAddressStr } if err := app.SetClusterConfig(network.ClusterName, clusterConfig); err != nil { return err From 7adfec98e7e67aba95b9e7a35ffa4fb4aee9a294 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 21 May 2024 16:25:28 -0400 Subject: [PATCH 10/63] deploying the destination --- cmd/contractcmd/deployBridge.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index b21457a0e..e911e2f7f 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -343,6 +343,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if err != nil { return err } + fmt.Println("HASTA ACA LLEGO") teleporterRegistryAddress = common.HexToAddress("0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25") teleporterManagerAddress = common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC") sourceBlockchainID, err := ids.FromString("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL") @@ -353,12 +354,12 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { TeleporterRegistryAddress: teleporterRegistryAddress, TeleporterManager: teleporterManagerAddress, SourceBlockchainID: sourceBlockchainID, - TokenSourceAddress: wrappedNativeTokenAddress, + TokenSourceAddress: nativeTokenSourceAddress, } erc20DestinationAddress, err := deployERC20Destination( bridgeSrcDir, - network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), - "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", + network.BlockchainEndpoint("C"), + "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", teleporterTokenDestinationSettings, "Wrapped Token", "WTOK", From 92e573c865cf2494c4a723ae564f3588d0a2ff99 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 22 May 2024 07:43:22 -0400 Subject: [PATCH 11/63] ux design 2 for bridge deploy of fuji --- cmd/contractcmd/deployBridge.go | 635 +++++--------------------------- 1 file changed, 88 insertions(+), 547 deletions(-) diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index e911e2f7f..c2a108b50 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -3,28 +3,13 @@ package contractcmd import ( - "fmt" - "io" - "os" - "os/exec" - "path/filepath" "strings" - cmdflags "github.com/ava-labs/avalanche-cli/cmd/flags" - "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" - "github.com/ava-labs/avalanche-cli/pkg/evm" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/networkoptions" - "github.com/ava-labs/avalanche-cli/pkg/prompts" - "github.com/ava-labs/avalanche-cli/pkg/subnet" - "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" - "github.com/ava-labs/avalanche-cli/pkg/vm" - "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/subnet-evm/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" "github.com/spf13/cobra" ) @@ -43,75 +28,15 @@ type DeployFlags struct { RPCURL string } -const ( - cChainAlias = "C" - cChainName = "c-chain" -) - var ( deployBridgeSupportedNetworkOptions = []networkoptions.NetworkOption{ networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, } - deployFlags DeployFlags - foundryupPath = utils.ExpandHome("~/.foundry/bin/foundryup") - forgePath = utils.ExpandHome("~/.foundry/bin/forge") + deployFlags DeployFlags ) -func foundryIsInstalled() bool { - return utils.IsExecutable(forgePath) -} - -func installFoundry() error { - ux.Logger.PrintToUser("Installing Foundry") - downloadCmd := exec.Command("curl", "-L", "https://foundry.paradigm.xyz") - installCmd := exec.Command("sh") - var downloadOutbuf, downloadErrbuf strings.Builder - downloadCmdStdoutPipe, err := downloadCmd.StdoutPipe() - if err != nil { - return err - } - downloadCmd.Stderr = &downloadErrbuf - installCmd.Stdin = io.TeeReader(downloadCmdStdoutPipe, &downloadOutbuf) - var installOutbuf, installErrbuf strings.Builder - installCmd.Stdout = &installOutbuf - installCmd.Stderr = &installErrbuf - if err := installCmd.Start(); err != nil { - return err - } - if err := downloadCmd.Run(); err != nil { - if downloadOutbuf.String() != "" { - ux.Logger.PrintToUser(strings.TrimSuffix(downloadOutbuf.String(), "\n")) - } - if downloadErrbuf.String() != "" { - ux.Logger.PrintToUser(strings.TrimSuffix(downloadErrbuf.String(), "\n")) - } - return err - } - if err := installCmd.Wait(); err != nil { - if installOutbuf.String() != "" { - ux.Logger.PrintToUser(strings.TrimSuffix(installOutbuf.String(), "\n")) - } - if installErrbuf.String() != "" { - ux.Logger.PrintToUser(strings.TrimSuffix(installErrbuf.String(), "\n")) - } - ux.Logger.PrintToUser("installation failed: %s", err.Error()) - return err - } - ux.Logger.PrintToUser(strings.TrimSuffix(installOutbuf.String(), "\n")) - out, err := exec.Command(foundryupPath).CombinedOutput() - ux.Logger.PrintToUser(string(out)) - if err != nil { - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Foundry toolset is not available and couldn't automatically be installed. It is a necessary dependency for CLI to compile smart contracts.") - ux.Logger.PrintToUser("") - ux.Logger.PrintToUser("Please follow install instructions at https://book.getfoundry.sh/getting-started/installation and try again") - ux.Logger.PrintToUser("") - } - return err -} - // avalanche contract deploy bridge func newDeployBridgeCmd() *cobra.Command { cmd := &cobra.Command{ @@ -122,16 +47,6 @@ func newDeployBridgeCmd() *cobra.Command { Args: cobrautils.ExactArgs(0), } networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deployBridgeSupportedNetworkOptions) - cmd.Flags().StringVar(&deployFlags.SubnetName, "subnet", "", "deploy teleporter into the given CLI subnet") - cmd.Flags().StringVar(&deployFlags.BlockchainID, "blockchain-id", "", "deploy teleporter into the given blockchain ID/Alias") - cmd.Flags().BoolVar(&deployFlags.CChain, "c-chain", false, "deploy teleporter into C-Chain") - cmd.Flags().StringVar(&deployFlags.PrivateKey, "private-key", "", "private key to use to fund teleporter deploy)") - cmd.Flags().StringVar(&deployFlags.KeyName, "key", "", "CLI stored key to use to fund teleporter deploy)") - cmd.Flags().BoolVar(&deployFlags.GenesisKey, "genesis-key", false, "use genesis aidrop key to fund teleporter deploy") - cmd.Flags().BoolVar(&deployFlags.DeployMessenger, "deploy-messenger", true, "deploy Teleporter Messenger") - cmd.Flags().BoolVar(&deployFlags.DeployRegistry, "deploy-registry", true, "deploy Teleporter Registry") - cmd.Flags().StringVar(&deployFlags.TeleporterVersion, "version", "latest", "version to deploy") - cmd.Flags().StringVar(&deployFlags.RPCURL, "rpc-url", "", "use the given RPC URL to connect to the subnet") return cmd } @@ -139,175 +54,7 @@ func deployBridge(_ *cobra.Command, args []string) error { return CallDeployBridge(args, deployFlags) } -func deployWrappedNativeToken( - srcDir string, - rpcURL string, - prefundedPrivateKey string, - tokenName string, -) (common.Address, error) { - srcDir = utils.ExpandHome(srcDir) - abiPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.abi.json") - binPath := filepath.Join(srcDir, "contracts/out/WrappedNativeToken.sol/WrappedNativeToken.bin") - abiBytes, err := os.ReadFile(abiPath) - if err != nil { - return common.Address{}, err - } - binBytes, err := os.ReadFile(binPath) - if err != nil { - return common.Address{}, err - } - metadata := &bind.MetaData{ - ABI: string(abiBytes), - Bin: string(binBytes), - } - abi, err := metadata.GetAbi() - if err != nil { - return common.Address{}, err - } - bin := common.FromHex(metadata.Bin) - client, err := evm.GetClient(rpcURL) - if err != nil { - return common.Address{}, err - } - defer client.Close() - txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) - if err != nil { - return common.Address{}, err - } - address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, tokenName) - if err != nil { - return common.Address{}, err - } - if _, success, err := evm.WaitForTransaction(client, tx); err != nil { - return common.Address{}, err - } else if !success { - return common.Address{}, fmt.Errorf("failed receipt status deploying contract") - } - return address, nil -} - -func deployNativeTokenSource( - srcDir string, - rpcURL string, - prefundedPrivateKey string, - teleporterRegistryAddress common.Address, - teleporterManagerAddress common.Address, - wrappedNativeTokenAddress common.Address, -) (common.Address, error) { - srcDir = utils.ExpandHome(srcDir) - abiPath := filepath.Join(srcDir, "contracts/out/NativeTokenSource.sol/NativeTokenSource.abi.json") - binPath := filepath.Join(srcDir, "contracts/out/NativeTokenSource.sol/NativeTokenSource.bin") - abiBytes, err := os.ReadFile(abiPath) - if err != nil { - return common.Address{}, err - } - binBytes, err := os.ReadFile(binPath) - if err != nil { - return common.Address{}, err - } - metadata := &bind.MetaData{ - ABI: string(abiBytes), - Bin: string(binBytes), - } - abi, err := metadata.GetAbi() - if err != nil { - return common.Address{}, err - } - bin := common.FromHex(metadata.Bin) - client, err := evm.GetClient(rpcURL) - if err != nil { - return common.Address{}, err - } - defer client.Close() - txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) - if err != nil { - return common.Address{}, err - } - address, tx, _, err := bind.DeployContract(txOpts, *abi, bin, client, teleporterRegistryAddress, teleporterManagerAddress, wrappedNativeTokenAddress) - if err != nil { - return common.Address{}, err - } - if _, success, err := evm.WaitForTransaction(client, tx); err != nil { - return common.Address{}, err - } else if !success { - return common.Address{}, fmt.Errorf("failed receipt status deploying contract") - } - return address, nil -} - -type TeleporterTokenDestinationSettings struct { - TeleporterRegistryAddress common.Address - TeleporterManager common.Address - SourceBlockchainID [32]byte - TokenSourceAddress common.Address -} - -func deployERC20Destination( - srcDir string, - rpcURL string, - prefundedPrivateKey string, - teleporterTokenDestinationSettings TeleporterTokenDestinationSettings, - tokenName string, - tokenSymbol string, - tokenDecimals uint8, -) (common.Address, error) { - srcDir = utils.ExpandHome(srcDir) - abiPath := filepath.Join(srcDir, "contracts/out/ERC20Destination.sol/ERC20Destination.abi.json") - binPath := filepath.Join(srcDir, "contracts/out/ERC20Destination.sol/ERC20Destination.bin") - abiBytes, err := os.ReadFile(abiPath) - if err != nil { - return common.Address{}, err - } - binBytes, err := os.ReadFile(binPath) - if err != nil { - return common.Address{}, err - } - metadata := &bind.MetaData{ - ABI: string(abiBytes), - Bin: string(binBytes), - } - abi, err := metadata.GetAbi() - if err != nil { - return common.Address{}, err - } - bin := common.FromHex(metadata.Bin) - client, err := evm.GetClient(rpcURL) - if err != nil { - return common.Address{}, err - } - defer client.Close() - txOpts, err := evm.GetTxOptsWithSigner(client, prefundedPrivateKey) - if err != nil { - return common.Address{}, err - } - address, tx, _, err := bind.DeployContract( - txOpts, - *abi, - bin, - client, - teleporterTokenDestinationSettings, - tokenName, - tokenSymbol, - tokenDecimals, - ) - if err != nil { - return common.Address{}, err - } - if _, success, err := evm.WaitForTransaction(client, tx); err != nil { - return common.Address{}, err - } else if !success { - return common.Address{}, fmt.Errorf("failed receipt status deploying contract") - } - return address, nil -} - func CallDeployBridge(_ []string, flags DeployFlags) error { - if err := vm.CheckGitIsInstalled(); err != nil { - return err - } - if !foundryIsInstalled() { - return installFoundry() - } network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "On what Network do you want to deploy the Teleporter bridge?", @@ -320,321 +67,115 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { if err != nil { return err } - bridgeSrcDir := utils.ExpandHome("~/Workspace/projects/teleporter-token-bridge/") - wrappedNativeTokenAddress, err := deployWrappedNativeToken( - bridgeSrcDir, - network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), - "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", - "TOK", - ) - if err != nil { - return err - } - teleporterRegistryAddress := common.HexToAddress("0xbD9e8eC38E43d34CAB4194881B9BF39d639D7Bd3") - teleporterManagerAddress := common.HexToAddress("0x13D42261c6970023fBD486A24AB57c7c8e5DfcB9") - nativeTokenSourceAddress, err := deployNativeTokenSource( - bridgeSrcDir, - network.BlockchainEndpoint("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL"), - "6e6cb03f2f64e298b28e56bc53a051257bff62be978b6df010fce46a8fdde2cb", - teleporterRegistryAddress, - teleporterManagerAddress, - wrappedNativeTokenAddress, - ) - if err != nil { - return err - } - fmt.Println("HASTA ACA LLEGO") - teleporterRegistryAddress = common.HexToAddress("0x17aB05351fC94a1a67Bf3f56DdbB941aE6c63E25") - teleporterManagerAddress = common.HexToAddress("0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC") - sourceBlockchainID, err := ids.FromString("2FZA3PDpQvYy6uevt34xr7Sv4RczKe3827PWPqAymfqXhJkkGL") - if err != nil { - return err - } - teleporterTokenDestinationSettings := TeleporterTokenDestinationSettings{ - TeleporterRegistryAddress: teleporterRegistryAddress, - TeleporterManager: teleporterManagerAddress, - SourceBlockchainID: sourceBlockchainID, - TokenSourceAddress: nativeTokenSourceAddress, - } - erc20DestinationAddress, err := deployERC20Destination( - bridgeSrcDir, - network.BlockchainEndpoint("C"), - "56289e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8027", - teleporterTokenDestinationSettings, - "Wrapped Token", - "WTOK", - 18, - ) - if err != nil { - return err - } - fmt.Println(wrappedNativeTokenAddress) - fmt.Println(nativeTokenSourceAddress) - fmt.Println(erc20DestinationAddress) - return nil - // install bridge src dependencies - cmd := exec.Command( - "git", - "submodule", - "update", - "--init", - "--recursive", - ) - cmd.Dir = bridgeSrcDir - out, err := cmd.CombinedOutput() - if err != nil { - ux.Logger.PrintToUser(string(out)) - return err - } - // build teleporter contracts bytecode + abi - cmd = exec.Command( - forgePath, - "build", - "--extra-output-files", - "abi", - "bin", - ) - cmd.Dir = filepath.Join(bridgeSrcDir, "contracts") - out, err = cmd.CombinedOutput() - if err != nil { - ux.Logger.PrintToUser(string(out)) - return err - } - return nil - if !cmdflags.EnsureMutuallyExclusive([]bool{flags.SubnetName != "", flags.BlockchainID != "", flags.CChain}) { - return fmt.Errorf("--subnet, --blockchain-id and --cchain are mutually exclusive flags") - } - if !cmdflags.EnsureMutuallyExclusive([]bool{flags.PrivateKey != "", flags.KeyName != "", flags.GenesisKey}) { - return fmt.Errorf("--private-key, --key and --genesis-key are mutually exclusive flags") - } - if !flags.DeployMessenger && !flags.DeployRegistry { - return fmt.Errorf("you should set at least one of --deploy-messenger/--deploy-registry to true") - } - if flags.SubnetName == "" && flags.BlockchainID == "" && !flags.CChain { - // fill flags based on user prompts - blockchainIDOptions := []string{ - "Get Blockchain ID from an existing subnet (deployed with avalanche subnet deploy)", - "Use C-Chain Blockchain ID", - "Custom", - } - blockchainIDOption, err := app.Prompt.CaptureList("Which Blockchain ID would you like to deploy Teleporter to?", blockchainIDOptions) - if err != nil { - return err - } - switch blockchainIDOption { - case blockchainIDOptions[0]: - subnetNames, err := app.GetSubnetNames() - if err != nil { - return err - } - flags.SubnetName, err = app.Prompt.CaptureList( - "Choose a Subnet", - subnetNames, - ) - if err != nil { - return err - } - case blockchainIDOptions[1]: - flags.CChain = true - default: - flags.BlockchainID, err = app.Prompt.CaptureString("Blockchain ID/Alias") - if err != nil { - return err - } - } - } - - var ( - blockchainID string - teleporterSubnetDesc string - privateKey = flags.PrivateKey - teleporterVersion string - ) - switch { - case flags.SubnetName != "": - teleporterSubnetDesc = flags.SubnetName - sc, err := app.LoadSidecar(flags.SubnetName) - if err != nil { - return fmt.Errorf("failed to load sidecar: %w", err) - } - if b, _, err := subnetcmd.HasSubnetEVMGenesis(flags.SubnetName); err != nil { - return err - } else if !b { - return fmt.Errorf("only Subnet-EVM based vms can be used for teleporter") - } - if sc.Networks[network.Name()].BlockchainID == ids.Empty { - return fmt.Errorf("subnet has not been deployed to %s", network.Name()) - } - blockchainID = sc.Networks[network.Name()].BlockchainID.String() - if sc.TeleporterVersion != "" { - teleporterVersion = sc.TeleporterVersion - } - if sc.TeleporterKey != "" { - k, err := app.GetKey(sc.TeleporterKey, network, true) - if err != nil { - return err - } - privateKey = k.PrivKeyHex() - } - case flags.BlockchainID != "": - teleporterSubnetDesc = flags.BlockchainID - blockchainID = flags.BlockchainID - case flags.CChain: - teleporterSubnetDesc = cChainName - blockchainID = cChainAlias - } - var chainID ids.ID - if flags.CChain || !network.StandardPublicEndpoint() { - chainID, err = utils.GetChainID(network.Endpoint, blockchainID) + switch network.Kind { + case models.Local: + ux.Logger.PrintToUser("To be defined") + return nil + case models.Devnet: + ux.Logger.PrintToUser("To be defined") + return nil + case models.Fuji: + subnetNames, err := app.GetSubnetNames() if err != nil { return err } - } else { - chainID, err = ids.FromString(blockchainID) - if err != nil { - return err - } - } - createChainTx, err := utils.GetBlockchainTx(network.Endpoint, chainID) - if err != nil { - return err - } - if !utils.ByteSliceIsSubnetEvmGenesis(createChainTx.GenesisData) { - return fmt.Errorf("teleporter can only be deployed to Subnet-EVM based vms") - } - if flags.KeyName != "" { - k, err := app.GetKey(flags.KeyName, network, false) + subnetOptions := utils.Map(subnetNames, func(s string) string { return "Subnet " + s }) + prompt := "Where is the Token origin?" + cChainOption := "C-Chain" + notListedOption := "My blockchain isn't listed" + subnetOptions = append(append([]string{cChainOption}, subnetOptions...), notListedOption) + subnetOption, err := app.Prompt.CaptureListWithSize( + prompt, + subnetOptions, + 11, + ) if err != nil { return err } - privateKey = k.PrivKeyHex() - } - _, genesisAddress, genesisPrivateKey, err := subnet.GetSubnetAirdropKeyInfo(app, network, flags.SubnetName, createChainTx.GenesisData) - if err != nil { - return err - } - if flags.GenesisKey { - privateKey = genesisPrivateKey - } - if privateKey == "" { - cliKeyOpt := "Get private key from an existing stored key (created from avalanche key create or avalanche key import)" - customKeyOpt := "Custom" - genesisKeyOpt := fmt.Sprintf("Use the private key of the Genesis Aidrop address %s", genesisAddress) - keyOptions := []string{cliKeyOpt, customKeyOpt} - if genesisPrivateKey != "" { - keyOptions = []string{genesisKeyOpt, cliKeyOpt, customKeyOpt} - } - keyOption, err := app.Prompt.CaptureList("Which private key do you want to use to pay fees?", keyOptions) - if err != nil { - return err + if subnetOption == notListedOption { + ux.Logger.PrintToUser("Please import the subnet first, using the `avalanche subnet import` command suite") + return nil } - switch keyOption { - case cliKeyOpt: - keyName, err := prompts.CaptureKeyName(app.Prompt, "pay fees", app.GetKeyDir(), true) + tokenSymbol := "AVAX" + if subnetOption != cChainOption { + subnetName := strings.TrimPrefix(subnetOption, "Subnet ") + sc, err := app.LoadSidecar(subnetName) if err != nil { return err } - k, err := app.GetKey(keyName, network, false) - if err != nil { - return err - } - privateKey = k.PrivKeyHex() - case customKeyOpt: - privateKey, err = app.Prompt.CaptureString("Private Key") + tokenSymbol = sc.TokenSymbol + } + prompt = "What kind of token do you want to bridge?" + popularOption := "A popular token (e.g. AVAX, USDC, WAVAX, ...)" + existingOriginOption := "A token with an existing Origin Bridge" + nativeOption := "The native token " + tokenSymbol + erc20Option := "An ERC-20 token" + explainOption := "Explain the difference" + popularTokens := getPopularTokens(network, subnetOption) + options := []string{popularOption, existingOriginOption, nativeOption, erc20Option, explainOption} + if len(popularTokens) == 0 { + options = []string{existingOriginOption, nativeOption, erc20Option, explainOption} + } + for { + option, err := app.Prompt.CaptureList( + prompt, + options, + ) if err != nil { return err } - case genesisKeyOpt: - privateKey = genesisPrivateKey - } - } - if flags.TeleporterVersion != "" && flags.TeleporterVersion != "latest" { - teleporterVersion = flags.TeleporterVersion - } else if teleporterVersion == "" { - teleporterInfo, err := teleporter.GetInfo(app) - if err != nil { - return err - } - teleporterVersion = teleporterInfo.Version - } - // deploy to subnet - rpcURL := network.BlockchainEndpoint(blockchainID) - if flags.RPCURL != "" { - rpcURL = flags.RPCURL - } - td := teleporter.Deployer{} - alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddressStr, err := td.Deploy( - app.GetTeleporterBinDir(), - teleporterVersion, - teleporterSubnetDesc, - rpcURL, - privateKey, - flags.DeployMessenger, - flags.DeployRegistry, - ) - if err != nil { - return err - } - if flags.SubnetName != "" && !alreadyDeployed { - // update sidecar - sc, err := app.LoadSidecar(flags.SubnetName) - if err != nil { - return fmt.Errorf("failed to load sidecar: %w", err) - } - sc.TeleporterReady = true - sc.TeleporterVersion = teleporterVersion - networkInfo := sc.Networks[network.Name()] - if teleporterMessengerAddress != "" { - networkInfo.TeleporterMessengerAddress = teleporterMessengerAddress - } - if teleporterRegistryAddressStr != "" { - networkInfo.TeleporterRegistryAddress = teleporterRegistryAddressStr - } - sc.Networks[network.Name()] = networkInfo - if err := app.UpdateSidecar(&sc); err != nil { - return err - } - } - // automatic deploy to cchain for local/devnet - if !flags.CChain && (network.Kind == models.Local || network.Kind == models.Devnet) { - ewoq, err := app.GetKey("ewoq", network, false) - if err != nil { - return err - } - alreadyDeployed, teleporterMessengerAddress, teleporterRegistryAddressStr, err := td.Deploy( - app.GetTeleporterBinDir(), - teleporterVersion, - cChainName, - network.BlockchainEndpoint(cChainAlias), - ewoq.PrivKeyHex(), - flags.DeployMessenger, - flags.DeployRegistry, - ) - if err != nil { - return err - } - if !alreadyDeployed { - if network.Kind == models.Local { - if err := subnet.WriteExtraLocalNetworkData(app, teleporterMessengerAddress, teleporterRegistryAddressStr); err != nil { + switch option { + case popularOption: + _, err = app.Prompt.CaptureList( + "Choose Token", + popularTokens, + ) + if err != nil { return err } - } - if network.ClusterName != "" { - clusterConfig, err := app.GetClusterConfig(network.ClusterName) + case existingOriginOption: + _, err = app.Prompt.CaptureAddress( + "Enter the address of the Origin Bridge", + ) if err != nil { return err } - if teleporterMessengerAddress != "" { - clusterConfig.ExtraNetworkData.CChainTeleporterMessengerAddress = teleporterMessengerAddress - } - if teleporterRegistryAddressStr != "" { - clusterConfig.ExtraNetworkData.CChainTeleporterRegistryAddress = teleporterRegistryAddressStr - } - if err := app.SetClusterConfig(network.ClusterName, clusterConfig); err != nil { + case erc20Option: + _, err = app.Prompt.CaptureAddress( + "Enter the address of the ERC-20 Token", + ) + if err != nil { return err } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue } + break + } + prompt = "Where should the token be bridged as an ERC-20?" + subnetOptions = utils.Filter(subnetOptions, func(s string) bool { return s != subnetOption }) + subnetOption, err = app.Prompt.CaptureListWithSize( + prompt, + subnetOptions, + 11, + ) + if err != nil { + return err + } + if subnetOption == notListedOption { + ux.Logger.PrintToUser("Please import the subnet first, using the `avalanche subnet import` command suite") + return nil } } return nil } + +func getPopularTokens(network models.Network, subnetOption string) []string { + if network.Kind == models.Fuji && subnetOption == "C-Chain" { + return []string{"AVAX", "USDC", "WAVAX"} + } else { + return []string{} + } +} From d37e5b94f38c157b18b8ce4ada1b90d52661e656 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 23 May 2024 12:40:29 -0400 Subject: [PATCH 12/63] add popular token info list --- cmd/contractcmd/deployBridge.go | 46 ++++++++++++++++++++++---- cmd/contractcmd/popularTokensInfo.json | 19 +++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 cmd/contractcmd/popularTokensInfo.json diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index c2a108b50..3d5bad0bd 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -3,6 +3,9 @@ package contractcmd import ( + _ "embed" + "encoding/json" + "fmt" "strings" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" @@ -111,9 +114,18 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { nativeOption := "The native token " + tokenSymbol erc20Option := "An ERC-20 token" explainOption := "Explain the difference" - popularTokens := getPopularTokens(network, subnetOption) + popularTokensInfo, err := getPopularTokensInfo(network, subnetOption) + if err != nil { + return err + } + popularTokensDesc := utils.Map( + popularTokensInfo, + func(i PopularTokenInfo) string { + return i.Desc() + }, + ) options := []string{popularOption, existingOriginOption, nativeOption, erc20Option, explainOption} - if len(popularTokens) == 0 { + if len(popularTokensDesc) == 0 { options = []string{existingOriginOption, nativeOption, erc20Option, explainOption} } for { @@ -128,7 +140,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { case popularOption: _, err = app.Prompt.CaptureList( "Choose Token", - popularTokens, + popularTokensDesc, ) if err != nil { return err @@ -172,10 +184,32 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { return nil } -func getPopularTokens(network models.Network, subnetOption string) []string { +type PopularTokenInfo struct { + TokenName string + TokenContractAddress string + BridgeHubAddress string +} + +//go:embed popularTokensInfo.json +var popularTokensInfoByteSlice []byte + +var popularTokensInfo map[string][]PopularTokenInfo + +func (i PopularTokenInfo) Desc() string { + if i.TokenContractAddress == "" { + return i.TokenName + } else { + return fmt.Sprintf("%s | Token address %s | Hub bridge address %s", i.TokenName, i.TokenContractAddress, i.BridgeHubAddress) + } +} + +func getPopularTokensInfo(network models.Network, subnetOption string) ([]PopularTokenInfo, error) { + if err := json.Unmarshal(popularTokensInfoByteSlice, &popularTokensInfo); err != nil { + return nil, fmt.Errorf("unabled to get popular tokens info from file: %w", err) + } if network.Kind == models.Fuji && subnetOption == "C-Chain" { - return []string{"AVAX", "USDC", "WAVAX"} + return popularTokensInfo[models.Fuji.String()], nil } else { - return []string{} + return nil, nil } } diff --git a/cmd/contractcmd/popularTokensInfo.json b/cmd/contractcmd/popularTokensInfo.json new file mode 100644 index 000000000..b8dcbb82b --- /dev/null +++ b/cmd/contractcmd/popularTokensInfo.json @@ -0,0 +1,19 @@ +{ + "Fuji": [ + { + "TokenName": "AVAX", + "TokenContractAddress": "", + "BridgeHubAddress": "" + }, + { + "TokenName": "USDC", + "TokenContractAddress": "0x5425890298aed601595a70AB815c96711a31Bc65", + "BridgeHubAddress": "0x5425890298aed601595a70AB815c96711a31Bc65" + }, + { + "TokenName": "WAVAX", + "TokenContractAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "BridgeHubAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c" + } + ] +} \ No newline at end of file From 04bd4ebb44f0d95832fb03f8915bb375a930cb59 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 23 May 2024 12:43:03 -0400 Subject: [PATCH 13/63] move to new file --- cmd/contractcmd/deployBridge.go | 34 +---------------------- cmd/contractcmd/popularTokensInfo.go | 41 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 33 deletions(-) create mode 100644 cmd/contractcmd/popularTokensInfo.go diff --git a/cmd/contractcmd/deployBridge.go b/cmd/contractcmd/deployBridge.go index 3d5bad0bd..79c38801e 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/contractcmd/deployBridge.go @@ -4,8 +4,6 @@ package contractcmd import ( _ "embed" - "encoding/json" - "fmt" "strings" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" @@ -114,7 +112,7 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { nativeOption := "The native token " + tokenSymbol erc20Option := "An ERC-20 token" explainOption := "Explain the difference" - popularTokensInfo, err := getPopularTokensInfo(network, subnetOption) + popularTokensInfo, err := GetPopularTokensInfo(network, subnetOption) if err != nil { return err } @@ -183,33 +181,3 @@ func CallDeployBridge(_ []string, flags DeployFlags) error { } return nil } - -type PopularTokenInfo struct { - TokenName string - TokenContractAddress string - BridgeHubAddress string -} - -//go:embed popularTokensInfo.json -var popularTokensInfoByteSlice []byte - -var popularTokensInfo map[string][]PopularTokenInfo - -func (i PopularTokenInfo) Desc() string { - if i.TokenContractAddress == "" { - return i.TokenName - } else { - return fmt.Sprintf("%s | Token address %s | Hub bridge address %s", i.TokenName, i.TokenContractAddress, i.BridgeHubAddress) - } -} - -func getPopularTokensInfo(network models.Network, subnetOption string) ([]PopularTokenInfo, error) { - if err := json.Unmarshal(popularTokensInfoByteSlice, &popularTokensInfo); err != nil { - return nil, fmt.Errorf("unabled to get popular tokens info from file: %w", err) - } - if network.Kind == models.Fuji && subnetOption == "C-Chain" { - return popularTokensInfo[models.Fuji.String()], nil - } else { - return nil, nil - } -} diff --git a/cmd/contractcmd/popularTokensInfo.go b/cmd/contractcmd/popularTokensInfo.go new file mode 100644 index 000000000..886e31a34 --- /dev/null +++ b/cmd/contractcmd/popularTokensInfo.go @@ -0,0 +1,41 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package contractcmd + +import ( + _ "embed" + "encoding/json" + "fmt" + + "github.com/ava-labs/avalanche-cli/pkg/models" +) + +type PopularTokenInfo struct { + TokenName string + TokenContractAddress string + BridgeHubAddress string +} + +//go:embed popularTokensInfo.json +var popularTokensInfoByteSlice []byte + +var popularTokensInfo map[string][]PopularTokenInfo + +func (i PopularTokenInfo) Desc() string { + if i.TokenContractAddress == "" { + return i.TokenName + } else { + return fmt.Sprintf("%s | Token address %s | Hub bridge address %s", i.TokenName, i.TokenContractAddress, i.BridgeHubAddress) + } +} + +func GetPopularTokensInfo(network models.Network, subnetOption string) ([]PopularTokenInfo, error) { + if err := json.Unmarshal(popularTokensInfoByteSlice, &popularTokensInfo); err != nil { + return nil, fmt.Errorf("unabled to get popular tokens info from file: %w", err) + } + if network.Kind == models.Fuji && subnetOption == "C-Chain" { + return popularTokensInfo[models.Fuji.String()], nil + } else { + return nil, nil + } +} From c836def618070e8a983a29321a3c42bb22f6b9fc Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 24 May 2024 07:16:54 -0400 Subject: [PATCH 14/63] add blockchain alias to popular tokens json --- cmd/contractcmd/popularTokensInfo.go | 8 +++--- cmd/contractcmd/popularTokensInfo.json | 37 ++++++++++++++------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/cmd/contractcmd/popularTokensInfo.go b/cmd/contractcmd/popularTokensInfo.go index 886e31a34..b5e179c66 100644 --- a/cmd/contractcmd/popularTokensInfo.go +++ b/cmd/contractcmd/popularTokensInfo.go @@ -19,7 +19,7 @@ type PopularTokenInfo struct { //go:embed popularTokensInfo.json var popularTokensInfoByteSlice []byte -var popularTokensInfo map[string][]PopularTokenInfo +var popularTokensInfo map[string]map[string][]PopularTokenInfo func (i PopularTokenInfo) Desc() string { if i.TokenContractAddress == "" { @@ -29,12 +29,12 @@ func (i PopularTokenInfo) Desc() string { } } -func GetPopularTokensInfo(network models.Network, subnetOption string) ([]PopularTokenInfo, error) { +func GetPopularTokensInfo(network models.Network, blockchainAlias string) ([]PopularTokenInfo, error) { if err := json.Unmarshal(popularTokensInfoByteSlice, &popularTokensInfo); err != nil { return nil, fmt.Errorf("unabled to get popular tokens info from file: %w", err) } - if network.Kind == models.Fuji && subnetOption == "C-Chain" { - return popularTokensInfo[models.Fuji.String()], nil + if network.Kind == models.Fuji { + return popularTokensInfo[models.Fuji.String()][blockchainAlias], nil } else { return nil, nil } diff --git a/cmd/contractcmd/popularTokensInfo.json b/cmd/contractcmd/popularTokensInfo.json index b8dcbb82b..44241e99d 100644 --- a/cmd/contractcmd/popularTokensInfo.json +++ b/cmd/contractcmd/popularTokensInfo.json @@ -1,19 +1,22 @@ { - "Fuji": [ - { - "TokenName": "AVAX", - "TokenContractAddress": "", - "BridgeHubAddress": "" - }, - { - "TokenName": "USDC", - "TokenContractAddress": "0x5425890298aed601595a70AB815c96711a31Bc65", - "BridgeHubAddress": "0x5425890298aed601595a70AB815c96711a31Bc65" - }, - { - "TokenName": "WAVAX", - "TokenContractAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", - "BridgeHubAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c" - } + "Fuji": { + "C-Chain": [ + { + "TokenName": "AVAX", + "TokenContractAddress": "", + "BridgeHubAddress": "" + }, + { + "TokenName": "USDC", + "TokenContractAddress": "0x5425890298aed601595a70AB815c96711a31Bc65", + "BridgeHubAddress": "0x5425890298aed601595a70AB815c96711a31Bc65" + }, + { + "TokenName": "WAVAX", + "TokenContractAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + "BridgeHubAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c" + } ] -} \ No newline at end of file + } +} + From 64c1eac99f2839a46846ca4bb189924f163798d3 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 24 May 2024 07:24:02 -0400 Subject: [PATCH 15/63] start using teleporter bridge deploy --- cmd/contractcmd/contract.go | 26 ---------------- cmd/root.go | 4 --- .../deploy.go => teleportercmd/bridge.go} | 14 ++++----- .../bridgeDeploy.go} | 30 +++++++++---------- .../popularTokensInfo.go | 2 +- .../popularTokensInfo.json | 0 cmd/teleportercmd/teleporter.go | 2 ++ 7 files changed, 25 insertions(+), 53 deletions(-) delete mode 100644 cmd/contractcmd/contract.go rename cmd/{contractcmd/deploy.go => teleportercmd/bridge.go} (51%) rename cmd/{contractcmd/deployBridge.go => teleportercmd/bridgeDeploy.go} (86%) rename cmd/{contractcmd => teleportercmd}/popularTokensInfo.go (98%) rename cmd/{contractcmd => teleportercmd}/popularTokensInfo.json (100%) diff --git a/cmd/contractcmd/contract.go b/cmd/contractcmd/contract.go deleted file mode 100644 index 52f866299..000000000 --- a/cmd/contractcmd/contract.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -package contractcmd - -import ( - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/cobrautils" - "github.com/spf13/cobra" -) - -var app *application.Avalanche - -// avalanche contract -func NewCmd(injectedApp *application.Avalanche) *cobra.Command { - cmd := &cobra.Command{ - Use: "contract", - Short: "Interact with smart contracts", - Long: `The contract command suite provides a collection of tools for interacting -with Smart Contracts.`, - RunE: cobrautils.CommandSuiteUsage, - } - app = injectedApp - // contract deploy - cmd.AddCommand(newDeployCmd()) - return cmd -} diff --git a/cmd/root.go b/cmd/root.go index 76e1cb466..7c157fba4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -18,7 +18,6 @@ import ( "github.com/ava-labs/avalanche-cli/cmd/configcmd" "github.com/ava-labs/avalanche-cli/cmd/backendcmd" - "github.com/ava-labs/avalanche-cli/cmd/contractcmd" "github.com/ava-labs/avalanche-cli/cmd/keycmd" "github.com/ava-labs/avalanche-cli/cmd/networkcmd" "github.com/ava-labs/avalanche-cli/cmd/subnetcmd" @@ -99,9 +98,6 @@ in with avalanche subnet create myNewSubnet.`, // add teleporter command rootCmd.AddCommand(teleportercmd.NewCmd(app)) - // add contract command - rootCmd.AddCommand(contractcmd.NewCmd(app)) - cobrautils.ConfigureRootCmd(rootCmd) return rootCmd diff --git a/cmd/contractcmd/deploy.go b/cmd/teleportercmd/bridge.go similarity index 51% rename from cmd/contractcmd/deploy.go rename to cmd/teleportercmd/bridge.go index 2b5c0376a..b5b8f1511 100644 --- a/cmd/contractcmd/deploy.go +++ b/cmd/teleportercmd/bridge.go @@ -1,21 +1,21 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package contractcmd +package teleportercmd import ( "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/spf13/cobra" ) -// avalanche contract deploy -func newDeployCmd() *cobra.Command { +// avalanche teleporter bridge +func newBridgeCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "deploy", - Short: "Deploy smart contracts", - Long: `The deploy command suite provides deploy flows for different Smart Contracts.`, + Use: "bridge", + Short: "Manage Teleporter Bridges", + Long: `The bridge command suite provides tools to deploy and manage Teleporter Bridges.`, RunE: cobrautils.CommandSuiteUsage, } // contract deploy - cmd.AddCommand(newDeployBridgeCmd()) + cmd.AddCommand(newBridgeDeployCmd()) return cmd } diff --git a/cmd/contractcmd/deployBridge.go b/cmd/teleportercmd/bridgeDeploy.go similarity index 86% rename from cmd/contractcmd/deployBridge.go rename to cmd/teleportercmd/bridgeDeploy.go index 79c38801e..066d50ff0 100644 --- a/cmd/contractcmd/deployBridge.go +++ b/cmd/teleportercmd/bridgeDeploy.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package contractcmd +package teleportercmd import ( _ "embed" @@ -15,7 +15,7 @@ import ( "github.com/spf13/cobra" ) -type DeployFlags struct { +type BridgeDeployFlags struct { Network networkoptions.NetworkFlags SubnetName string BlockchainID string @@ -30,39 +30,39 @@ type DeployFlags struct { } var ( - deployBridgeSupportedNetworkOptions = []networkoptions.NetworkOption{ + bridgeDeploySupportedNetworkOptions = []networkoptions.NetworkOption{ networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, } - deployFlags DeployFlags + bridgeDeployFlags BridgeDeployFlags ) -// avalanche contract deploy bridge -func newDeployBridgeCmd() *cobra.Command { +// avalanche teleporter bridge deploy +func newBridgeDeployCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "bridge", - Short: "Deploys Tokeb Bridge into a given Network and Subnets", - Long: "Deploys Tokeb Bridge into a given Network and Subnets", - RunE: deployBridge, + Use: "deploy", + Short: "Deploys Token Bridge into a given Network and Subnets", + Long: "Deploys Token Bridge into a given Network and Subnets", + RunE: bridgeDeploy, Args: cobrautils.ExactArgs(0), } - networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deployBridgeSupportedNetworkOptions) + networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, bridgeDeploySupportedNetworkOptions) return cmd } -func deployBridge(_ *cobra.Command, args []string) error { - return CallDeployBridge(args, deployFlags) +func bridgeDeploy(_ *cobra.Command, args []string) error { + return CallBridgeDeploy(args, deployFlags) } -func CallDeployBridge(_ []string, flags DeployFlags) error { +func CallBridgeDeploy(_ []string, flags DeployFlags) error { network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "On what Network do you want to deploy the Teleporter bridge?", flags.Network, true, false, - deployBridgeSupportedNetworkOptions, + bridgeDeploySupportedNetworkOptions, "", ) if err != nil { diff --git a/cmd/contractcmd/popularTokensInfo.go b/cmd/teleportercmd/popularTokensInfo.go similarity index 98% rename from cmd/contractcmd/popularTokensInfo.go rename to cmd/teleportercmd/popularTokensInfo.go index b5e179c66..90fb0c0e2 100644 --- a/cmd/contractcmd/popularTokensInfo.go +++ b/cmd/teleportercmd/popularTokensInfo.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package contractcmd +package teleportercmd import ( _ "embed" diff --git a/cmd/contractcmd/popularTokensInfo.json b/cmd/teleportercmd/popularTokensInfo.json similarity index 100% rename from cmd/contractcmd/popularTokensInfo.json rename to cmd/teleportercmd/popularTokensInfo.json diff --git a/cmd/teleportercmd/teleporter.go b/cmd/teleportercmd/teleporter.go index 2724f0767..b17f49fcd 100644 --- a/cmd/teleportercmd/teleporter.go +++ b/cmd/teleportercmd/teleporter.go @@ -26,5 +26,7 @@ with Teleporter-Enabled Subnets.`, cmd.AddCommand(newDeployCmd()) // teleporter relayer cmd.AddCommand(newRelayerCmd()) + // teleporter bridge + cmd.AddCommand(newBridgeCmd()) return cmd } From bfdc9ffc87ea099ceff1405c9353fdd552883062 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 24 May 2024 10:22:55 -0400 Subject: [PATCH 16/63] adddress suggestions --- cmd/teleportercmd/{ => bridgecmd}/bridge.go | 10 ++-- .../{bridgeDeploy.go => bridgecmd/deploy.go} | 49 +++++++++++++------ .../{ => bridgecmd}/popularTokensInfo.go | 2 +- .../{ => bridgecmd}/popularTokensInfo.json | 0 cmd/teleportercmd/teleporter.go | 3 +- 5 files changed, 44 insertions(+), 20 deletions(-) rename cmd/teleportercmd/{ => bridgecmd}/bridge.go (68%) rename cmd/teleportercmd/{bridgeDeploy.go => bridgecmd/deploy.go} (75%) rename cmd/teleportercmd/{ => bridgecmd}/popularTokensInfo.go (98%) rename cmd/teleportercmd/{ => bridgecmd}/popularTokensInfo.json (100%) diff --git a/cmd/teleportercmd/bridge.go b/cmd/teleportercmd/bridgecmd/bridge.go similarity index 68% rename from cmd/teleportercmd/bridge.go rename to cmd/teleportercmd/bridgecmd/bridge.go index b5b8f1511..40d3de0a5 100644 --- a/cmd/teleportercmd/bridge.go +++ b/cmd/teleportercmd/bridgecmd/bridge.go @@ -1,21 +1,25 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package teleportercmd +package bridgecmd import ( + "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/spf13/cobra" ) +var app *application.Avalanche + // avalanche teleporter bridge -func newBridgeCmd() *cobra.Command { +func NewCmd(injectedApp *application.Avalanche) *cobra.Command { cmd := &cobra.Command{ Use: "bridge", Short: "Manage Teleporter Bridges", Long: `The bridge command suite provides tools to deploy and manage Teleporter Bridges.`, RunE: cobrautils.CommandSuiteUsage, } + app = injectedApp // contract deploy - cmd.AddCommand(newBridgeDeployCmd()) + cmd.AddCommand(newDeployCmd()) return cmd } diff --git a/cmd/teleportercmd/bridgeDeploy.go b/cmd/teleportercmd/bridgecmd/deploy.go similarity index 75% rename from cmd/teleportercmd/bridgeDeploy.go rename to cmd/teleportercmd/bridgecmd/deploy.go index 066d50ff0..03c8727c8 100644 --- a/cmd/teleportercmd/bridgeDeploy.go +++ b/cmd/teleportercmd/bridgecmd/deploy.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package teleportercmd +package bridgecmd import ( _ "embed" @@ -15,7 +15,7 @@ import ( "github.com/spf13/cobra" ) -type BridgeDeployFlags struct { +type DeployFlags struct { Network networkoptions.NetworkFlags SubnetName string BlockchainID string @@ -30,39 +30,39 @@ type BridgeDeployFlags struct { } var ( - bridgeDeploySupportedNetworkOptions = []networkoptions.NetworkOption{ + deploySupportedNetworkOptions = []networkoptions.NetworkOption{ networkoptions.Local, networkoptions.Devnet, networkoptions.Fuji, } - bridgeDeployFlags BridgeDeployFlags + deployFlags DeployFlags ) // avalanche teleporter bridge deploy -func newBridgeDeployCmd() *cobra.Command { +func newDeployCmd() *cobra.Command { cmd := &cobra.Command{ Use: "deploy", Short: "Deploys Token Bridge into a given Network and Subnets", Long: "Deploys Token Bridge into a given Network and Subnets", - RunE: bridgeDeploy, + RunE: deploy, Args: cobrautils.ExactArgs(0), } - networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, bridgeDeploySupportedNetworkOptions) + networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, deploySupportedNetworkOptions) return cmd } -func bridgeDeploy(_ *cobra.Command, args []string) error { - return CallBridgeDeploy(args, deployFlags) +func deploy(_ *cobra.Command, args []string) error { + return CallDeploy(args, deployFlags) } -func CallBridgeDeploy(_ []string, flags DeployFlags) error { +func CallDeploy(_ []string, flags DeployFlags) error { network, err := networkoptions.GetNetworkFromCmdLineFlags( app, "On what Network do you want to deploy the Teleporter bridge?", flags.Network, true, false, - bridgeDeploySupportedNetworkOptions, + deploySupportedNetworkOptions, "", ) if err != nil { @@ -112,6 +112,7 @@ func CallBridgeDeploy(_ []string, flags DeployFlags) error { nativeOption := "The native token " + tokenSymbol erc20Option := "An ERC-20 token" explainOption := "Explain the difference" + goBackOption := "Go Back" popularTokensInfo, err := GetPopularTokensInfo(network, subnetOption) if err != nil { return err @@ -119,7 +120,7 @@ func CallBridgeDeploy(_ []string, flags DeployFlags) error { popularTokensDesc := utils.Map( popularTokensInfo, func(i PopularTokenInfo) string { - return i.Desc() + return i.Desc() + " (recommended)" }, ) options := []string{popularOption, existingOriginOption, nativeOption, erc20Option, explainOption} @@ -136,13 +137,18 @@ func CallBridgeDeploy(_ []string, flags DeployFlags) error { } switch option { case popularOption: - _, err = app.Prompt.CaptureList( + options := popularTokensDesc + options = append(options, goBackOption) + option, err := app.Prompt.CaptureList( "Choose Token", - popularTokensDesc, + options, ) if err != nil { return err } + if option == goBackOption { + continue + } case existingOriginOption: _, err = app.Prompt.CaptureAddress( "Enter the address of the Origin Bridge", @@ -151,12 +157,25 @@ func CallBridgeDeploy(_ []string, flags DeployFlags) error { return err } case erc20Option: - _, err = app.Prompt.CaptureAddress( + erc20TokenAddr, err := app.Prompt.CaptureAddress( "Enter the address of the ERC-20 Token", ) if err != nil { return err } + if p := utils.Find(popularTokensInfo, func(p PopularTokenInfo) bool { return p.TokenContractAddress == erc20TokenAddr.Hex() }); p != nil { + ux.Logger.PrintToUser("You have entered the address of %s, a popular token in the subnet.", p.TokenName) + deployANewHupOption := "Yes, I want to deploy a new Bridge Hub" + useTheExistingHubOption := "No, I want to use the existing official Bridge Hub" + options := []string{deployANewHupOption, useTheExistingHubOption} + _, err = app.Prompt.CaptureList( + "Are you sure you want to deploy a new Bridge Hub for it?", + options, + ) + if err != nil { + return err + } + } case explainOption: ux.Logger.PrintToUser("The difference is...") ux.Logger.PrintToUser("") diff --git a/cmd/teleportercmd/popularTokensInfo.go b/cmd/teleportercmd/bridgecmd/popularTokensInfo.go similarity index 98% rename from cmd/teleportercmd/popularTokensInfo.go rename to cmd/teleportercmd/bridgecmd/popularTokensInfo.go index 90fb0c0e2..a6a813f21 100644 --- a/cmd/teleportercmd/popularTokensInfo.go +++ b/cmd/teleportercmd/bridgecmd/popularTokensInfo.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package teleportercmd +package bridgecmd import ( _ "embed" diff --git a/cmd/teleportercmd/popularTokensInfo.json b/cmd/teleportercmd/bridgecmd/popularTokensInfo.json similarity index 100% rename from cmd/teleportercmd/popularTokensInfo.json rename to cmd/teleportercmd/bridgecmd/popularTokensInfo.json diff --git a/cmd/teleportercmd/teleporter.go b/cmd/teleportercmd/teleporter.go index b17f49fcd..bb52a1756 100644 --- a/cmd/teleportercmd/teleporter.go +++ b/cmd/teleportercmd/teleporter.go @@ -3,6 +3,7 @@ package teleportercmd import ( + "github.com/ava-labs/avalanche-cli/cmd/teleportercmd/bridgecmd" "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/cobrautils" "github.com/spf13/cobra" @@ -27,6 +28,6 @@ with Teleporter-Enabled Subnets.`, // teleporter relayer cmd.AddCommand(newRelayerCmd()) // teleporter bridge - cmd.AddCommand(newBridgeCmd()) + cmd.AddCommand(bridgecmd.NewCmd(app)) return cmd } From 1e0651f64cff893d021b222d6215e4960e721ca0 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 24 May 2024 10:25:52 -0400 Subject: [PATCH 17/63] fix recommended --- cmd/teleportercmd/bridgecmd/deploy.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/teleportercmd/bridgecmd/deploy.go b/cmd/teleportercmd/bridgecmd/deploy.go index 03c8727c8..5a6cafaa5 100644 --- a/cmd/teleportercmd/bridgecmd/deploy.go +++ b/cmd/teleportercmd/bridgecmd/deploy.go @@ -120,7 +120,11 @@ func CallDeploy(_ []string, flags DeployFlags) error { popularTokensDesc := utils.Map( popularTokensInfo, func(i PopularTokenInfo) string { - return i.Desc() + " (recommended)" + if i.TokenContractAddress == "" { + return i.Desc() + } else { + return i.Desc() + " (recommended)" + } }, ) options := []string{popularOption, existingOriginOption, nativeOption, erc20Option, explainOption} From 33dc80e7b5301f29878b7219ec3dcb45de1f2871 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 16:42:35 -0400 Subject: [PATCH 18/63] update to latest esp --- cmd/teleportercmd/bridgecmd/deploy.go | 50 ++++++++++++------- .../bridgecmd/popularTokensInfo.go | 9 ++-- .../bridgecmd/popularTokensInfo.json | 2 +- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/cmd/teleportercmd/bridgecmd/deploy.go b/cmd/teleportercmd/bridgecmd/deploy.go index 5a6cafaa5..0501e51c6 100644 --- a/cmd/teleportercmd/bridgecmd/deploy.go +++ b/cmd/teleportercmd/bridgecmd/deploy.go @@ -108,9 +108,8 @@ func CallDeploy(_ []string, flags DeployFlags) error { } prompt = "What kind of token do you want to bridge?" popularOption := "A popular token (e.g. AVAX, USDC, WAVAX, ...)" - existingOriginOption := "A token with an existing Origin Bridge" - nativeOption := "The native token " + tokenSymbol - erc20Option := "An ERC-20 token" + hubDeployedOption := "A token that already has a Hub deployed" + deployNewHubOption := "Deploy a new Hub for the token" explainOption := "Explain the difference" goBackOption := "Go Back" popularTokensInfo, err := GetPopularTokensInfo(network, subnetOption) @@ -120,16 +119,16 @@ func CallDeploy(_ []string, flags DeployFlags) error { popularTokensDesc := utils.Map( popularTokensInfo, func(i PopularTokenInfo) string { - if i.TokenContractAddress == "" { + if i.BridgeHubAddress == "" { return i.Desc() } else { return i.Desc() + " (recommended)" } }, ) - options := []string{popularOption, existingOriginOption, nativeOption, erc20Option, explainOption} + options := []string{popularOption, hubDeployedOption, deployNewHubOption, explainOption} if len(popularTokensDesc) == 0 { - options = []string{existingOriginOption, nativeOption, erc20Option, explainOption} + options = []string{hubDeployedOption, deployNewHubOption, explainOption} } for { option, err := app.Prompt.CaptureList( @@ -153,32 +152,45 @@ func CallDeploy(_ []string, flags DeployFlags) error { if option == goBackOption { continue } - case existingOriginOption: + case hubDeployedOption: _, err = app.Prompt.CaptureAddress( - "Enter the address of the Origin Bridge", + "Enter the address of the Hub", ) if err != nil { return err } - case erc20Option: - erc20TokenAddr, err := app.Prompt.CaptureAddress( - "Enter the address of the ERC-20 Token", + case deployNewHubOption: + nativeOption := "The native token " + tokenSymbol + erc20Option := "An ERC-20 token" + options := []string{nativeOption, erc20Option} + option, err := app.Prompt.CaptureList( + "What kind of token do you want to deploy the Hub for?", + options, ) if err != nil { return err } - if p := utils.Find(popularTokensInfo, func(p PopularTokenInfo) bool { return p.TokenContractAddress == erc20TokenAddr.Hex() }); p != nil { - ux.Logger.PrintToUser("You have entered the address of %s, a popular token in the subnet.", p.TokenName) - deployANewHupOption := "Yes, I want to deploy a new Bridge Hub" - useTheExistingHubOption := "No, I want to use the existing official Bridge Hub" - options := []string{deployANewHupOption, useTheExistingHubOption} - _, err = app.Prompt.CaptureList( - "Are you sure you want to deploy a new Bridge Hub for it?", - options, + switch option { + case erc20Option: + erc20TokenAddr, err := app.Prompt.CaptureAddress( + "Enter the address of the ERC-20 Token", ) if err != nil { return err } + if p := utils.Find(popularTokensInfo, func(p PopularTokenInfo) bool { return p.TokenContractAddress == erc20TokenAddr.Hex() }); p != nil { + ux.Logger.PrintToUser("You have entered the address of %s, a popular token in the subnet.", p.TokenName) + deployANewHupOption := "Yes, I want to deploy a new Bridge Hub" + useTheExistingHubOption := "No, I want to use the existing official Bridge Hub" + options := []string{deployANewHupOption, useTheExistingHubOption} + _, err = app.Prompt.CaptureList( + "Are you sure you want to deploy a new Bridge Hub for it?", + options, + ) + if err != nil { + return err + } + } } case explainOption: ux.Logger.PrintToUser("The difference is...") diff --git a/cmd/teleportercmd/bridgecmd/popularTokensInfo.go b/cmd/teleportercmd/bridgecmd/popularTokensInfo.go index a6a813f21..0e12d76ce 100644 --- a/cmd/teleportercmd/bridgecmd/popularTokensInfo.go +++ b/cmd/teleportercmd/bridgecmd/popularTokensInfo.go @@ -22,10 +22,13 @@ var popularTokensInfoByteSlice []byte var popularTokensInfo map[string]map[string][]PopularTokenInfo func (i PopularTokenInfo) Desc() string { - if i.TokenContractAddress == "" { + switch { + case i.TokenContractAddress != "" && i.BridgeHubAddress != "": + return fmt.Sprintf("%s | Token address %s | Hub address %s", i.TokenName, i.TokenContractAddress, i.BridgeHubAddress) + case i.BridgeHubAddress != "": + return fmt.Sprintf("%s | Hub address %s", i.TokenName, i.BridgeHubAddress) + default: return i.TokenName - } else { - return fmt.Sprintf("%s | Token address %s | Hub bridge address %s", i.TokenName, i.TokenContractAddress, i.BridgeHubAddress) } } diff --git a/cmd/teleportercmd/bridgecmd/popularTokensInfo.json b/cmd/teleportercmd/bridgecmd/popularTokensInfo.json index 44241e99d..dd6e7f818 100644 --- a/cmd/teleportercmd/bridgecmd/popularTokensInfo.json +++ b/cmd/teleportercmd/bridgecmd/popularTokensInfo.json @@ -4,7 +4,7 @@ { "TokenName": "AVAX", "TokenContractAddress": "", - "BridgeHubAddress": "" + "BridgeHubAddress": "0xd00ae08403B9bbb9124bB305C09058E32C39A48c" }, { "TokenName": "USDC", From eb6ace3a6a2d0d9a9acced4ecaa50aece9c89624 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 23:24:49 -0400 Subject: [PATCH 19/63] integrate allow list stuff --- internal/mocks/prompter.go | 28 +++++ pkg/prompts/prompts.go | 26 +++++ pkg/vm/allowlist.go | 225 +++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 pkg/vm/allowlist.go diff --git a/internal/mocks/prompter.go b/internal/mocks/prompter.go index 32855ebe9..0d38e7b05 100644 --- a/internal/mocks/prompter.go +++ b/internal/mocks/prompter.go @@ -54,6 +54,34 @@ func (_m *Prompter) CaptureAddress(promptStr string) (common.Address, error) { return r0, r1 } +// CaptureAddresses provides a mock function with given fields: promptStr +func (_m *Prompter) CaptureAddresses(promptStr string) ([]common.Address, error) { + ret := _m.Called(promptStr) + + if len(ret) == 0 { + panic("no return value specified for CaptureList") + } + + var r0 []common.Address + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]common.Address, error)); ok { + return rf(promptStr) + } + if rf, ok := ret.Get(0).(func(string) []common.Address); ok { + r0 = rf(promptStr) + } else { + r0 = ret.Get(0).([]common.Address) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(promptStr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // CaptureDate provides a mock function with given fields: promptStr func (_m *Prompter) CaptureDate(promptStr string) (time.Time, error) { ret := _m.Called(promptStr) diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index e206d0757..add64086b 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -8,6 +8,7 @@ import ( "math/big" "net/url" "strconv" + "strings" "time" "github.com/ava-labs/avalanche-cli/pkg/constants" @@ -71,6 +72,7 @@ func (comparator *Comparator) Validate(val uint64) error { type Prompter interface { CapturePositiveBigInt(promptStr string) (*big.Int, error) CaptureAddress(promptStr string) (common.Address, error) + CaptureAddresses(promptStr string) ([]common.Address, error) CaptureNewFilepath(promptStr string) (string, error) CaptureExistingFilepath(promptStr string) (string, error) CaptureYesNo(promptStr string) (bool, error) @@ -448,6 +450,30 @@ func (*realPrompter) CaptureAddress(promptStr string) (common.Address, error) { return addressHex, nil } +func (*realPrompter) CaptureAddresses(promptStr string) ([]common.Address, error) { + addressesStr := "" + validated := false + for !validated { + var err error + addressesStr, err = utils.ReadLongString(promptui.IconGood + " " + promptStr + " ") + if err != nil { + return nil, err + } + if err := validateAddresses(addressesStr); err != nil { + fmt.Println(err) + } else { + validated = true + } + } + addresses := utils.Map( + strings.Split(addressesStr, ","), + func(s string) common.Address { + return common.HexToAddress(strings.TrimSpace(s)) + }, + ) + return addresses, nil +} + func (*realPrompter) CaptureExistingFilepath(promptStr string) (string, error) { prompt := promptui.Prompt{ Label: promptStr, diff --git a/pkg/vm/allowlist.go b/pkg/vm/allowlist.go new file mode 100644 index 000000000..cceeb6a02 --- /dev/null +++ b/pkg/vm/allowlist.go @@ -0,0 +1,225 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vm + +import ( + "fmt" + "os" + "strings" + + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/utils" + + "github.com/ethereum/go-ethereum/common" + "github.com/olekukonko/tablewriter" + "golang.org/x/mod/semver" +) + +func preview( + adminAddresses []common.Address, + managerAddresses []common.Address, + enabledAddresses []common.Address, +) { + table := tablewriter.NewWriter(os.Stdout) + table.SetRowLine(true) + table.SetAutoMergeCellsByColumnIndex([]int{0}) + addRoleToPreviewTable(table, "Admins", adminAddresses) + addRoleToPreviewTable(table, "Manager", managerAddresses) + addRoleToPreviewTable(table, "Enabled", enabledAddresses) + table.Render() + fmt.Println() +} + +func addRoleToPreviewTable(table *tablewriter.Table, name string, addresses []common.Address) { + if len(addresses) == 0 { + table.Append([]string{name, strings.Repeat(" ", 11)}) + } else { + addressesStr := strings.Join(utils.Map(addresses, func(a common.Address) string { return a.Hex() }), "\n") + table.Append([]string{name, addressesStr}) + } +} + +func getNewAddresses( + app *application.Avalanche, + adminAddresses []common.Address, + managerAddresses []common.Address, + enabledAddresses []common.Address, +) ([]common.Address, error) { + newAddresses := []common.Address{} + addresses, err := app.Prompt.CaptureAddresses("Enter the address of the account (or multiple comma separated):") + if err != nil { + return nil, err + } + for _, address := range addresses { + switch { + case utils.Belongs(adminAddresses, address): + fmt.Println(address.Hex() + " is already allowed as admin role") + case utils.Belongs(managerAddresses, address): + fmt.Println(address.Hex() + " is already allowed as manager role") + case utils.Belongs(enabledAddresses, address): + fmt.Println(address.Hex() + " is already allowed as enabled role") + default: + newAddresses = append(newAddresses, address) + } + } + return newAddresses, nil +} + +func removeAddress( + app *application.Avalanche, + addresses []common.Address, + kind string, +) ([]common.Address, bool, error) { + if len(addresses) == 0 { + fmt.Printf("There are no %s addresses to remove from\n", kind) + fmt.Println() + return addresses, true, nil + } + cancelOption := "Cancel" + prompt := "Select the address you want to remove" + options := utils.Map(addresses, func(a common.Address) string { return a.Hex() }) + options = append(options, cancelOption) + opt, err := app.Prompt.CaptureList(prompt, options) + if err != nil { + return addresses, false, err + } + if opt != cancelOption { + addresses = utils.RemoveFromSlice(addresses, common.HexToAddress(opt)) + return addresses, false, nil + } + return addresses, true, nil +} + +func GenerateAllowList( + app *application.Avalanche, + action string, + evmVersion string, +) ([]common.Address, []common.Address, []common.Address, bool, error) { + if !semver.IsValid(evmVersion) { + return nil, nil, nil, false, fmt.Errorf("invalid semantic version %q", evmVersion) + } + managerRoleEnabled := semver.Compare(evmVersion, "v0.6.4") >= 0 + + adminAddresses := []common.Address{} + managerAddresses := []common.Address{} + enabledAddresses := []common.Address{} + + promptTemplate := "Configure the addresses that are allowed to %s" + prompt := fmt.Sprintf(promptTemplate, action) + + addOption := "Add an address for a role to the allow list" + removeOption := "Remove address from the allow list" + previewOption := "Preview Allow List" + confirmOption := "Confirm Allow List" + cancelOption := "Cancel" + + adminOption := "Admin" + managerOption := "Manager" + enabledOption := "Enabled" + explainOption := "Explain the difference" + + for { + option, err := app.Prompt.CaptureList( + prompt, []string{addOption, removeOption, previewOption, confirmOption, cancelOption}, + ) + if err != nil { + return nil, nil, nil, false, err + } + switch option { + case addOption: + addPrompt := "What role should the address have?" + for { + options := []string{adminOption, managerOption, enabledOption, explainOption, cancelOption} + if !managerRoleEnabled { + options = []string{adminOption, enabledOption, explainOption, cancelOption} + } + roleOption, err := app.Prompt.CaptureList(addPrompt, options) + if err != nil { + return nil, nil, nil, false, err + } + switch roleOption { + case adminOption: + addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + if err != nil { + return nil, nil, nil, false, err + } + adminAddresses = append(adminAddresses, addresses...) + case managerOption: + addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + if err != nil { + return nil, nil, nil, false, err + } + managerAddresses = append(managerAddresses, addresses...) + case enabledOption: + addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + if err != nil { + return nil, nil, nil, false, err + } + enabledAddresses = append(enabledAddresses, addresses...) + case explainOption: + fmt.Println("The difference to be given by devrel people") + fmt.Println() + continue + case cancelOption: + } + break + } + case removeOption: + keepAsking := true + for keepAsking { + removePrompt := "What role does the address that should be removed have?" + options := []string{adminOption, managerOption, enabledOption, cancelOption} + if !managerRoleEnabled { + options = []string{adminOption, enabledOption, cancelOption} + } + roleOption, err := app.Prompt.CaptureList(removePrompt, options) + if err != nil { + return nil, nil, nil, false, err + } + switch roleOption { + case adminOption: + adminAddresses, keepAsking, err = removeAddress(app, adminAddresses, "admin") + if err != nil { + return nil, nil, nil, false, err + } + case managerOption: + managerAddresses, keepAsking, err = removeAddress(app, managerAddresses, "manager") + if err != nil { + return nil, nil, nil, false, err + } + case enabledOption: + enabledAddresses, keepAsking, err = removeAddress(app, enabledAddresses, "enabled") + if err != nil { + return nil, nil, nil, false, err + } + case cancelOption: + keepAsking = false + } + } + case previewOption: + preview(adminAddresses, managerAddresses, enabledAddresses) + case confirmOption: + if len(adminAddresses) == 0 && len(managerAddresses) == 0 && len(enabledAddresses) == 0 { + fmt.Println("We need at least one address to have been added to the allow list. Otherwise cancel.") + fmt.Println() + continue + } + preview(adminAddresses, managerAddresses, enabledAddresses) + confirmPrompt := "Confirm?" + yesOption := "Yes" + noOption := "No, keep editing" + confirmOption, err := app.Prompt.CaptureList( + confirmPrompt, []string{yesOption, noOption}, + ) + if err != nil { + return nil, nil, nil, false, err + } + if confirmOption == yesOption { + return adminAddresses, managerAddresses, enabledAddresses, false, nil + } + case cancelOption: + return nil, nil, nil, true, err + } + } +} From a5832b9c11c50eaf0cbb560b39950c383b308d9f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 23:27:26 -0400 Subject: [PATCH 20/63] nit --- pkg/prompts/validations.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index 140eb0c7c..c3bcc4d5b 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -94,6 +94,21 @@ func validateAddress(input string) error { return nil } +func validateAddresses(input string) error { + addresses := strings.Split(input, ",") + for _, address := range addresses { + address = strings.TrimSpace(address) + if !common.IsHexAddress(address) { + if address == "" { + return fmt.Errorf("invalid empty address") + } else { + return fmt.Errorf("address %q is invalid", address) + } + } + } + return nil +} + func validateExistingFilepath(input string) error { if fileInfo, err := os.Stat(input); err == nil && !fileInfo.IsDir() { return nil From 6622839a3976548bef983d572f65c625926a2e00 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 23:30:02 -0400 Subject: [PATCH 21/63] niy --- pkg/utils/common.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/utils/common.go b/pkg/utils/common.go index c8fdf4df8..a28dc88ff 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -122,6 +122,16 @@ func Belongs[T comparable](input []T, elem T) bool { return false } +func RemoveFromSlice[T comparable](input []T, toRemove T) []T { + output := make([]T, 0, len(input)) + for _, e := range input { + if e != toRemove { + output = append(output, e) + } + } + return output +} + func Filter[T any](input []T, f func(T) bool) []T { output := make([]T, 0, len(input)) for _, e := range input { @@ -368,7 +378,7 @@ func GetGitCommit(gitRepoURL string) string { // ReadLongString reads a long string from the user input. func ReadLongString(msg string, args ...interface{}) (string, error) { - fmt.Println(fmt.Sprintf(msg, args...)) + fmt.Printf(msg, args...) reader := bufio.NewReader(os.Stdin) longString, err := reader.ReadString('\n') if err != nil { From 0f02278a47cf6ba8c7a4cb7b23a90d27195e68d4 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 23:46:01 -0400 Subject: [PATCH 22/63] VM option --- cmd/subnetcmd/create.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 2abc2d4ea..b6841ee76 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -189,12 +189,30 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { subnetType := getVMFromFlag() if subnetType == "" { - subnetTypeStr, err := app.Prompt.CaptureList( - "Choose your VM", - []string{models.SubnetEvm, models.CustomVM}, - ) - if err != nil { - return err + subnetEvmOption := "Subnet-EVM" + customVMOption := "Custom VM" + explainOption := "Explain the difference" + options := []string{subnetEvmOption, customVMOption, explainOption} + var subnetTypeStr string + for { + option, err := app.Prompt.CaptureList( + "VM", + options, + ) + if err != nil { + return err + } + switch option { + case subnetEvmOption: + subnetTypeStr = models.SubnetEvm + case customVMOption: + subnetTypeStr = models.CustomVM + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break } subnetType = models.VMTypeFromString(subnetTypeStr) } From 0ac2ab29d9a8a60d6663668948379c5bbd05451a Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 30 May 2024 23:59:58 -0400 Subject: [PATCH 23/63] add version option --- cmd/subnetcmd/create.go | 2 +- pkg/vm/createEvm.go | 43 +++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index b6841ee76..627bed6e5 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -236,7 +236,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } if subnetType == models.SubnetEvm { - evmVersion, err = vm.GetVMVersion(app, "Subnet-EVM", constants.SubnetEVMRepoName, evmVersion) + evmVersion, err = vm.GetVMVersion(app, constants.SubnetEVMRepoName, evmVersion) if err != nil { return err } diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index e2ffb72f3..a56ea0c70 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -20,6 +20,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/params" + "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" @@ -250,7 +251,6 @@ func ensureAdminsHaveBalance(admins []common.Address, alloc core.GenesisAlloc) e func GetVMVersion( app *application.Avalanche, - vmName string, repoName string, vmVersion string, ) (string, error) { @@ -273,7 +273,7 @@ func GetVMVersion( return "", err } case "": - vmVersion, err = askForVMVersion(app, vmName, repoName) + vmVersion, err = askForVMVersion(app, repoName) if err != nil { return "", err } @@ -283,31 +283,40 @@ func GetVMVersion( func askForVMVersion( app *application.Avalanche, - vmName string, repoName string, ) (string, error) { - latestReleaseVersion, err := app.Downloader.GetLatestReleaseVersion( - binutils.GetGithubLatestReleaseURL( + var ( + latestReleaseVersion string + latestPreReleaseVersion string + err error + ) + if os.Getenv("OFFLINECLI") == "" { + latestReleaseVersion, err = app.Downloader.GetLatestReleaseVersion( + binutils.GetGithubLatestReleaseURL( + constants.AvaLabsOrg, + repoName, + ), + ) + if err != nil { + return "", err + } + latestPreReleaseVersion, err = app.Downloader.GetLatestPreReleaseVersion( constants.AvaLabsOrg, repoName, - ), - ) - if err != nil { - return "", err - } - latestPreReleaseVersion, err := app.Downloader.GetLatestPreReleaseVersion( - constants.AvaLabsOrg, - repoName, - ) - if err != nil { - return "", err + ) + if err != nil { + return "", err + } + } else { + latestReleaseVersion = evm.Version + latestPreReleaseVersion = evm.Version } useCustom := "Specify custom version" useLatestRelease := "Use latest release version" + versionComments[latestReleaseVersion] useLatestPreRelease := "Use latest pre-release version" + versionComments[latestPreReleaseVersion] - defaultPrompt := fmt.Sprintf("What version of %s would you like?", vmName) + defaultPrompt := "Version" versionOptions := []string{useLatestRelease, useCustom} if latestPreReleaseVersion != latestReleaseVersion { From b5f98fda90a0dfa9c26b8287e36469a27d337f90 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 31 May 2024 04:22:55 -0500 Subject: [PATCH 24/63] add gas fees stub --- cmd/subnetcmd/create.go | 243 ++++++++++++++++++++++++++++++------- pkg/constants/constants.go | 2 + pkg/vm/createEvm.go | 2 +- pkg/vm/fees.go | 26 ++-- 4 files changed, 222 insertions(+), 51 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 627bed6e5..944606d63 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -16,11 +16,11 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/metrics" "github.com/ava-labs/avalanche-cli/pkg/models" - "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" + "github.com/ava-labs/subnet-evm/params" "github.com/spf13/cobra" "golang.org/x/mod/semver" ) @@ -44,8 +44,7 @@ var ( useLatestReleasedEvmVersion bool useLatestPreReleasedEvmVersion bool useRepo bool - teleporterReady bool - runRelayer bool + useTeleporter bool useWarp bool errIllegalNameCharacter = errors.New( @@ -78,7 +77,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") cmd.Flags().StringVar(&evmVersion, "vm-version", "", "version of Subnet-EVM template to use") cmd.Flags().Uint64Var(&evmChainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") - cmd.Flags().StringVar(&evmToken, "evm-token", "", "token name to use with Subnet-EVM") + cmd.Flags().StringVar(&evmToken, "evm-token", "", "token symbol to use with Subnet-EVM") cmd.Flags().BoolVar(&evmDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") cmd.Flags().BoolVar(&useCustom, "custom", false, "use a custom VM template") cmd.Flags().BoolVar(&useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") @@ -91,8 +90,7 @@ configuration, pass the -f flag.`, cmd.Flags().StringVar(&customVMBuildScript, "custom-vm-build-script", "", "custom vm build-script") cmd.Flags().BoolVar(&useRepo, "from-github-repo", false, "generate custom VM binary from github repository") cmd.Flags().BoolVar(&useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") - cmd.Flags().BoolVar(&teleporterReady, "teleporter", false, "generate a teleporter-ready vm") - cmd.Flags().BoolVar(&runRelayer, "relayer", false, "run AWM relayer when deploying the vm") + cmd.Flags().BoolVar(&useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") return cmd } @@ -235,13 +233,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", evmVersion) } - if subnetType == models.SubnetEvm { - evmVersion, err = vm.GetVMVersion(app, constants.SubnetEVMRepoName, evmVersion) - if err != nil { - return err - } - } - genesisFileIsEVM := false if genesisFile != "" { genesisFileIsEVM, err = utils.PathIsSubnetEVMGenesis(genesisFile) @@ -254,43 +245,214 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return fmt.Errorf("provided genesis file has no proper Subnet-EVM format") } - if subnetType == models.SubnetEvm || genesisFileIsEVM { - if evmDefaults { - teleporterReady = true - runRelayer = true - } - teleporterReady, err = prompts.CaptureBoolFlag( - app.Prompt, - cmd, - "teleporter", - teleporterReady, - "Would you like to enable Teleporter on your VM?", - ) + if subnetType == models.SubnetEvm { + evmVersion, err = vm.GetVMVersion(app, constants.SubnetEVMRepoName, evmVersion) if err != nil { return err } - if teleporterReady && !useWarp { - return fmt.Errorf("warp should be enabled for teleporter to work") + } + + if subnetType == models.SubnetEvm && genesisFile == "" { + if evmChainID == 0 { + evmChainID, err = app.Prompt.CaptureUint64("Chain ID") + if err != nil { + return err + } + } + } + + // Gas token + if subnetType == models.SubnetEvm && genesisFile == "" { + nativeTokenOption := "It's own Native Token" + externalTokenOption := "A token from another blockchain" + explainOption := "Explain the difference" + options := []string{nativeTokenOption, externalTokenOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "What kind of gas token should your blockchain use?", + options, + ) + if err != nil { + return err + } + switch option { + case nativeTokenOption: + evmToken, err = app.Prompt.CaptureString("Token Symbol") + if err != nil { + return err + } + allocateToNewKeyOption := "1m to new key (+x amount to relayer)" + allocateToEwoqOption := "1m to new ewoq (not recommended for production, +x amount to relayer)" + customAllocationOption := "Custom allocation (configure exact amount to relayer)" + options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} + option, err := app.Prompt.CaptureList( + "Initial Token Allocation", + options, + ) + if err != nil { + return err + } + if option == customAllocationOption { + _, err := app.Prompt.CaptureAddress("Address to allocate to") + if err != nil { + return err + } + _, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to airdrop (in %s units)", evmToken)) + if err != nil { + return err + } + } + fixedSupplyOption := "I want to have a fixed supply of tokens on my blockchain. (Native Minter Precompile OFF)" + dynamicSupplyOption := "Yes, I want to be able to mint additional tokens on my blockchain. (Native Minter Precompile ON)" + options = []string{fixedSupplyOption, dynamicSupplyOption} + option, err = app.Prompt.CaptureList( + "Allow minting new native Tokens? (Native Minter Precompile)", + options, + ) + if err != nil { + return err + } + if option == dynamicSupplyOption { + _, _, _, _, err := vm.GenerateAllowList(app, "mint native tokens", evmVersion) + if err != nil { + return err + } + } + case externalTokenOption: + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + } + + // Transaction / Gas Fees + if subnetType == models.SubnetEvm && genesisFile == "" { + customizeOption := "Customize fee config" + explainOption := "Explain the difference" + lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" + mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" + highOption := "High disk use / High Throughput 5 mil gas/s" + options := []string{lowOption, mediumOption, highOption, customizeOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "How should the gas fees be configured on your Blockchain?", + options, + ) + if err != nil { + return err + } + switch option { + case customizeOption: + config := params.ChainConfig{} + _, _, err = vm.CustomizeFeeConfig(config, app) + if err != nil { + return err + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + dontChangeFeeSettingsOption := "I am fine with the gas fee configuration set in the genesis (Fee Manager Precompile OFF)" + changeFeeSettingsOption := "I want to be able to adjust gas pricing if necessary - recommended for production (Fee Manager Precompile ON)" + explainOption = "Explain the difference" + options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Should these fees be changeable on the fly? (Fee Manager Precompile)", + options, + ) + if err != nil { + return err + } + switch option { + case changeFeeSettingsOption: + _, _, _, _, err := vm.GenerateAllowList(app, "adjust the gas fees", evmVersion) + if err != nil { + return err + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break } - if teleporterReady { - runRelayer, err = prompts.CaptureBoolFlag( - app.Prompt, - cmd, - "relayer", - runRelayer, - "Would you like to run AMW Relayer when deploying your VM?", + burnFees := "I am fine with gas fees being burned (Reward Manager Precompile OFF)" + distributeFees := "I want to customize accumulated gas fees distribution (Reward Manager Precompile ON)" + explainOption = "Explain the difference" + options = []string{burnFees, distributeFees, explainOption} + for { + option, err := app.Prompt.CaptureList( + "By default, all fees on Avalanche are burned (sent to a blackhole address). (Reward Manager Precompile)", + options, ) if err != nil { return err } + switch option { + case distributeFees: + _, _, _, _, err := vm.GenerateAllowList(app, "customize gas fees distribution", evmVersion) + if err != nil { + return err + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break } } + // Interoperability var teleporterInfo *teleporter.Info - if teleporterReady { - teleporterInfo, err = teleporter.GetInfo(app) - if err != nil { - return err + if subnetType == models.SubnetEvm || genesisFileIsEVM { + if evmDefaults { + useTeleporter = true + } + flagName := "teleporter" + if flag := cmd.Flags().Lookup(flagName); flag == nil { + return fmt.Errorf("flag configuration %q not found for cmd %q", flagName, cmd.Use) + } else if !flag.Changed { + interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" + isolatedBlockchainOption := "No, I want to run my blockchain isolated" + explainOption := "Explain the difference" + options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to connect your blockchain with other blockchains or C-Chain? (Deploy Teleporter and Registry)", + options, + ) + if err != nil { + return err + } + switch option { + case interoperatingBlockchainOption: + useTeleporter = true + case isolatedBlockchainOption: + useTeleporter = false + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + } + if useTeleporter && !useWarp { + return fmt.Errorf("warp should be enabled for teleporter to work") + } + if useTeleporter { + teleporterInfo, err = teleporter.GetInfo(app) + if err != nil { + return err + } } } @@ -329,11 +491,10 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errors.New("not implemented") } - if teleporterReady { - sc.TeleporterReady = teleporterReady + if useTeleporter { + sc.TeleporterReady = useTeleporter sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version - sc.RunRelayer = runRelayer if genesisFile != "" && genesisFileIsEVM { // evm genesis file was given. make appropriate checks and customizations for teleporter genesisBytes, err = addSubnetEVMGenesisPrefundedAddress(genesisBytes, teleporterInfo.FundedAddress, teleporterInfo.FundedBalance.String()) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 585862547..2c7262126 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -68,6 +68,8 @@ const ( AWSGP3DefaultThroughput = 125 SimulatePublicNetwork = "SIMULATE_PUBLIC_NETWORK" + OperateOfflineEnvVarName = "CLIOFFLINE" + FujiAPIEndpoint = "https://api.avax-test.network" MainnetAPIEndpoint = "https://api.avax.network" diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index a56ea0c70..0408f29ce 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -290,7 +290,7 @@ func askForVMVersion( latestPreReleaseVersion string err error ) - if os.Getenv("OFFLINECLI") == "" { + if os.Getenv(constants.OperateOfflineEnvVarName) == "" { latestReleaseVersion, err = app.Downloader.GetLatestReleaseVersion( binutils.GetGithubLatestReleaseURL( constants.AvaLabsOrg, diff --git a/pkg/vm/fees.go b/pkg/vm/fees.go index d96a39acc..b1b54ba1c 100644 --- a/pkg/vm/fees.go +++ b/pkg/vm/fees.go @@ -21,15 +21,6 @@ func GetFeeConfig(config params.ChainConfig, app *application.Avalanche, useDefa useMedium = "Medium disk use / Medium Throughput 2 mil gas/s" useSlow = "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" customFee = "Customize fee config" - - setGasLimit = "Set gas limit" - setBlockRate = "Set target block rate" - setMinBaseFee = "Set min base fee" - setTargetGas = "Set target gas" - setBaseFeeChangeDenominator = "Set base fee change denominator" - setMinBlockGas = "Set min block gas cost" - setMaxBlockGas = "Set max block gas cost" - setGasStep = "Set block gas cost step" ) config.FeeConfig = StarterFeeConfig @@ -64,7 +55,24 @@ func GetFeeConfig(config params.ChainConfig, app *application.Avalanche, useDefa default: ux.Logger.PrintToUser("Customizing fee config") } + return CustomizeFeeConfig(config, app) +} +func CustomizeFeeConfig(config params.ChainConfig, app *application.Avalanche) ( + params.ChainConfig, + statemachine.StateDirection, + error, +) { + const ( + setGasLimit = "Set gas limit" + setBlockRate = "Set target block rate" + setMinBaseFee = "Set min base fee" + setTargetGas = "Set target gas" + setBaseFeeChangeDenominator = "Set base fee change denominator" + setMinBlockGas = "Set min block gas cost" + setMaxBlockGas = "Set max block gas cost" + setGasStep = "Set block gas cost step" + ) gasLimit, err := app.Prompt.CapturePositiveBigInt(setGasLimit) if err != nil { return config, statemachine.Stop, err From 1fe4ec6ae3abb2548c760e0df4adef5e5fe70bee Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 31 May 2024 05:38:42 -0500 Subject: [PATCH 25/63] add remaining prompts --- cmd/subnetcmd/create.go | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 944606d63..e8bc6af26 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -456,6 +456,81 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } } + // Permissioning + if subnetType == models.SubnetEvm && genesisFile == "" { + noOption := "No" + yesOption := "Yes" + explainOption := "Explain the difference" + options := []string{noOption, yesOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "You can optionally add permissioning on different levels to your blockchain. Do you want to make your blockchain permissioned?", + options, + ) + if err != nil { + return err + } + switch option { + case yesOption: + anyoneCanSubmitTransactionsOption := "I want anyone to be able to submit transactions on my blockchain. (Transaction Allow List OFF)" + approvedCanSubmitTransactionsOption := "I want only approved addresses to submit transactions on my blockchain. (Transaction Allow List ON)" + options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to interact with your blockchain? (Transaction Allowlist Precompile)", + options, + ) + if err != nil { + return err + } + switch option { + case approvedCanSubmitTransactionsOption: + _, _, _, _, err := vm.GenerateAllowList(app, "issue transactions", evmVersion) + if err != nil { + return err + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + anyoneCanDeployContractsOption := "I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" + approvedCanDeployContractsOption := "I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" + options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Contract Deployer Allowlist)", + options, + ) + if err != nil { + return err + } + switch option { + case approvedCanDeployContractsOption: + _, _, _, _, err := vm.GenerateAllowList(app, "deploy smart contracts", evmVersion) + if err != nil { + return err + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + case explainOption: + ux.Logger.PrintToUser("The difference is...") + ux.Logger.PrintToUser("") + continue + } + break + } + } + + return nil + switch subnetType { case models.SubnetEvm: genesisBytes, sc, err = vm.CreateEvmSubnetConfig( From 41776a0e514995d661cd85674477290946d99c16 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 12 Jun 2024 06:30:51 -0300 Subject: [PATCH 26/63] address https://github.com/ava-labs/avalanche-cli/issues/1927 --- cmd/subnetcmd/create.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index e8bc6af26..9c3fc7d51 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -262,6 +262,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } // Gas token + externalGasToken := false if subnetType == models.SubnetEvm && genesisFile == "" { nativeTokenOption := "It's own Native Token" externalTokenOption := "A token from another blockchain" @@ -319,6 +320,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } } case externalTokenOption: + externalGasToken = true case explainOption: ux.Logger.PrintToUser("The difference is...") ux.Logger.PrintToUser("") @@ -413,13 +415,16 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // Interoperability var teleporterInfo *teleporter.Info if subnetType == models.SubnetEvm || genesisFileIsEVM { + if externalGasToken { + useTeleporter = true + } if evmDefaults { useTeleporter = true } flagName := "teleporter" if flag := cmd.Flags().Lookup(flagName); flag == nil { return fmt.Errorf("flag configuration %q not found for cmd %q", flagName, cmd.Use) - } else if !flag.Changed { + } else if !flag.Changed && !externalGasToken { interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" isolatedBlockchainOption := "No, I want to run my blockchain isolated" explainOption := "Explain the difference" From cc495c5b5b14089d2640761849b41a4c69f4724d Mon Sep 17 00:00:00 2001 From: Meaghan FitzGerald Date: Wed, 26 Jun 2024 10:38:52 -0400 Subject: [PATCH 27/63] Meag rewordings (#1978) * ETD subnet-evm vs CustomVM * ETD: native vs erc20 * ETD: to burn or not to burn * finish explaining * Update cmd/subnetcmd/create.go Signed-off-by: Meaghan FitzGerald --------- Signed-off-by: Meaghan FitzGerald --- cmd/subnetcmd/create.go | 43 +++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 9c3fc7d51..163f50bd9 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -67,7 +67,7 @@ can create a custom, user-generated genesis with a custom VM by providing the path to your genesis and VM binaries with the --genesis and --vm flags. By default, running the command with a subnetName that already exists -causes the command to fail. If you’d like to overwrite an existing +causes the command to fail. If you'd like to overwrite an existing configuration, pass the -f flag.`, Args: cobrautils.ExactArgs(1), RunE: createSubnetConfig, @@ -206,8 +206,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { case customVMOption: subnetTypeStr = models.CustomVM case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Virtual machines are the blueprint the defines the application-level logic of a blockchain. It determines the language and rules for writing and executing smart contracts, as well as other blockchain logic.") + ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. For more information, please visit: https://github.com/ava-labs/subnet-evm") + ux.Logger.PrintToUser("Custom VMs created with the HyperSDK or writen from scratch in golang or rust can be deployed on Avalanche using the second option. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") continue } break @@ -322,8 +323,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { case externalTokenOption: externalGasToken = true case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Gas tokens exist because blockchains have limited resources. Native tokens the default gas token, and are non-programmable unless wrapped by an ERC-20 token contract.") + ux.Logger.PrintToUser("If desired, ERC-20 tokens can be deployed on other blockchains and used as the gas token enabled by a bridge. When a transaction is initiated, the ERC-20 amount will be locked on the source chain, a message will be relayed to the Subnet, and then the token will be minted to the sender's address using the Native Minter precompile. This means users with a balance of that ERC-20 on a separate chain can use it to pay for gas on the Subnet.") continue } break @@ -354,15 +355,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") + ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") + ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") continue } break } dontChangeFeeSettingsOption := "I am fine with the gas fee configuration set in the genesis (Fee Manager Precompile OFF)" changeFeeSettingsOption := "I want to be able to adjust gas pricing if necessary - recommended for production (Fee Manager Precompile ON)" - explainOption = "Explain the difference" options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} for { option, err := app.Prompt.CaptureList( @@ -378,10 +379,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") - continue + //missing case for dontChangeFeeSettingsOption } break } @@ -404,8 +402,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") continue } break @@ -431,7 +428,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to connect your blockchain with other blockchains or C-Chain? (Deploy Teleporter and Registry)", + "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", options, ) if err != nil { @@ -443,8 +440,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { case isolatedBlockchainOption: useTeleporter = false case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Avalanche already enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains.") continue } break @@ -482,7 +478,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to interact with your blockchain? (Transaction Allowlist Precompile)", + "Do you want to allow only certain user addresses to interact with your blockchain? (Transaction Allow List Precompile)", options, ) if err != nil { @@ -495,8 +491,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This is useful for permissioning your blockchain, similar to a whitelist, and can be used to prevent spam and unwanted transactions on your chain. This prevents any unauthorized users from sending transactions or deploying smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") continue } break @@ -506,7 +501,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Contract Deployer Allowlist)", + "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Contract Deployer Allow List)", options, ) if err != nil { @@ -519,15 +514,13 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("While you may wish to allow anyone to submit transactions to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain. The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") continue } break } case explainOption: - ux.Logger.PrintToUser("The difference is...") - ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as whitelists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") continue } break From 1efdd0aa4d33242226c71e63983862bbe036d0de Mon Sep 17 00:00:00 2001 From: Martin Eckardt Date: Tue, 2 Jul 2024 18:53:42 +0200 Subject: [PATCH 28/63] [Subnet Create Explore] Rewording Martin (#2002) * Select VM, native token, token allocation, token supply, transaction fees, dynamic fees * transaction and smart contract deployer allowlist * fix typo * introduce const for explainOption * Add missing explain option * Fix nits and incorporated user feedback --- cmd/subnetcmd/create.go | 93 ++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 163f50bd9..1de62ae00 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -20,6 +20,9 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" + + "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/subnet-evm/params" "github.com/spf13/cobra" "golang.org/x/mod/semver" @@ -53,6 +56,8 @@ var ( errMutuallyVMConfigOptions = errors.New("specifying --genesis flag disables SubnetEVM config flags --evm-chain-id,--evm-token,--evm-defaults") ) +const explainOption = "Explain the difference" + // avalanche subnet create func newCreateCmd() *cobra.Command { cmd := &cobra.Command{ @@ -189,7 +194,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if subnetType == "" { subnetEvmOption := "Subnet-EVM" customVMOption := "Custom VM" - explainOption := "Explain the difference" options := []string{subnetEvmOption, customVMOption, explainOption} var subnetTypeStr string for { @@ -207,8 +211,10 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { subnetTypeStr = models.CustomVM case explainOption: ux.Logger.PrintToUser("Virtual machines are the blueprint the defines the application-level logic of a blockchain. It determines the language and rules for writing and executing smart contracts, as well as other blockchain logic.") - ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. For more information, please visit: https://github.com/ava-labs/subnet-evm") - ux.Logger.PrintToUser("Custom VMs created with the HyperSDK or writen from scratch in golang or rust can be deployed on Avalanche using the second option. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. Subnet-EVM can be configured with this CLI to meet the developers requirements without writing code. For more information, please visit: https://github.com/ava-labs/subnet-evm") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") continue } break @@ -243,7 +249,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } if subnetType == models.SubnetEvm && genesisFile != "" && !genesisFileIsEVM { - return fmt.Errorf("provided genesis file has no proper Subnet-EVM format") + return fmt.Errorf("The provided genesis file has no proper Subnet-EVM format") } if subnetType == models.SubnetEvm { @@ -265,13 +271,12 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // Gas token externalGasToken := false if subnetType == models.SubnetEvm && genesisFile == "" { - nativeTokenOption := "It's own Native Token" + nativeTokenOption := "The blockchain's native token" externalTokenOption := "A token from another blockchain" - explainOption := "Explain the difference" options := []string{nativeTokenOption, externalTokenOption, explainOption} for { option, err := app.Prompt.CaptureList( - "What kind of gas token should your blockchain use?", + "Which token will be used for transaction fee payments?", options, ) if err != nil { @@ -283,12 +288,12 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - allocateToNewKeyOption := "1m to new key (+x amount to relayer)" - allocateToEwoqOption := "1m to new ewoq (not recommended for production, +x amount to relayer)" - customAllocationOption := "Custom allocation (configure exact amount to relayer)" + allocateToNewKeyOption := "Allocate 1m tokens to new a newly created account" + allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" + customAllocationOption := "Define a custom allocation (Recommended for production)" options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} option, err := app.Prompt.CaptureList( - "Initial Token Allocation", + "How should the initial token allocation be structured?", options, ) if err != nil { @@ -299,13 +304,13 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - _, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to airdrop (in %s units)", evmToken)) + _, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", evmToken)) if err != nil { return err } } - fixedSupplyOption := "I want to have a fixed supply of tokens on my blockchain. (Native Minter Precompile OFF)" - dynamicSupplyOption := "Yes, I want to be able to mint additional tokens on my blockchain. (Native Minter Precompile ON)" + fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" + dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" options = []string{fixedSupplyOption, dynamicSupplyOption} option, err = app.Prompt.CaptureList( "Allow minting new native Tokens? (Native Minter Precompile)", @@ -323,8 +328,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { case externalTokenOption: externalGasToken = true case explainOption: - ux.Logger.PrintToUser("Gas tokens exist because blockchains have limited resources. Native tokens the default gas token, and are non-programmable unless wrapped by an ERC-20 token contract.") - ux.Logger.PrintToUser("If desired, ERC-20 tokens can be deployed on other blockchains and used as the gas token enabled by a bridge. When a transaction is initiated, the ERC-20 amount will be locked on the source chain, a message will be relayed to the Subnet, and then the token will be minted to the sender's address using the Native Minter precompile. This means users with a balance of that ERC-20 on a separate chain can use it to pay for gas on the Subnet.") + ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) + ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and the therefore the transaction fees are completely isolated.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) + ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") continue } break @@ -334,14 +346,13 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // Transaction / Gas Fees if subnetType == models.SubnetEvm && genesisFile == "" { customizeOption := "Customize fee config" - explainOption := "Explain the difference" lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" highOption := "High disk use / High Throughput 5 mil gas/s" options := []string{lowOption, mediumOption, highOption, customizeOption, explainOption} for { option, err := app.Prompt.CaptureList( - "How should the gas fees be configured on your Blockchain?", + "How should the transaction fees be configured on your Blockchain?", options, ) if err != nil { @@ -356,18 +367,20 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } case explainOption: ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") + ux.Logger.PrintToUser(" ") ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") + ux.Logger.PrintToUser(" ") ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") continue } break } - dontChangeFeeSettingsOption := "I am fine with the gas fee configuration set in the genesis (Fee Manager Precompile OFF)" - changeFeeSettingsOption := "I want to be able to adjust gas pricing if necessary - recommended for production (Fee Manager Precompile ON)" + dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" + changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Should these fees be changeable on the fly? (Fee Manager Precompile)", + "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", options, ) if err != nil { @@ -379,17 +392,19 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - //missing case for dontChangeFeeSettingsOption + case explainOption: + ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") + continue + // missing case for dontChangeFeeSettingsOption } break } - burnFees := "I am fine with gas fees being burned (Reward Manager Precompile OFF)" - distributeFees := "I want to customize accumulated gas fees distribution (Reward Manager Precompile ON)" - explainOption = "Explain the difference" + burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" + distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" options = []string{burnFees, distributeFees, explainOption} for { option, err := app.Prompt.CaptureList( - "By default, all fees on Avalanche are burned (sent to a blackhole address). (Reward Manager Precompile)", + "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", options, ) if err != nil { @@ -424,7 +439,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } else if !flag.Changed && !externalGasToken { interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" isolatedBlockchainOption := "No, I want to run my blockchain isolated" - explainOption := "Explain the difference" options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} for { option, err := app.Prompt.CaptureList( @@ -440,7 +454,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { case isolatedBlockchainOption: useTeleporter = false case explainOption: - ux.Logger.PrintToUser("Avalanche already enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains.") + ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") continue } break @@ -461,11 +475,10 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if subnetType == models.SubnetEvm && genesisFile == "" { noOption := "No" yesOption := "Yes" - explainOption := "Explain the difference" options := []string{noOption, yesOption, explainOption} for { option, err := app.Prompt.CaptureList( - "You can optionally add permissioning on different levels to your blockchain. Do you want to make your blockchain permissioned?", + "Do you want to add permissioning to your blockchain?", options, ) if err != nil { @@ -473,12 +486,12 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } switch option { case yesOption: - anyoneCanSubmitTransactionsOption := "I want anyone to be able to submit transactions on my blockchain. (Transaction Allow List OFF)" - approvedCanSubmitTransactionsOption := "I want only approved addresses to submit transactions on my blockchain. (Transaction Allow List ON)" + anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" + approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to allow only certain user addresses to interact with your blockchain? (Transaction Allow List Precompile)", + "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", options, ) if err != nil { @@ -491,13 +504,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This is useful for permissioning your blockchain, similar to a whitelist, and can be used to prevent spam and unwanted transactions on your chain. This prevents any unauthorized users from sending transactions or deploying smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") + ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") continue } break } - anyoneCanDeployContractsOption := "I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" - approvedCanDeployContractsOption := "I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" + anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" + approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} for { option, err := app.Prompt.CaptureList( @@ -514,13 +529,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } case explainOption: - ux.Logger.PrintToUser("While you may wish to allow anyone to submit transactions to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain. The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") + ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") continue } break } case explainOption: - ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as whitelists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") + ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as allowlists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") continue } break From 860cb66a05b4a12362e0fe2015838ec078565c3d Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 3 Jul 2024 15:03:21 -0300 Subject: [PATCH 29/63] genesis params start making more sense --- cmd/subnetcmd/create.go | 744 ++++++++++++++++++++++++---------------- pkg/vm/allowlist.go | 84 +++-- 2 files changed, 481 insertions(+), 347 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 1de62ae00..316cb634e 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "sort" "strconv" "strings" @@ -20,30 +21,35 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" + "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/subnet-evm/params" "github.com/spf13/cobra" "golang.org/x/mod/semver" ) const ( - forceFlag = "force" - latest = "latest" - preRelease = "pre-release" + forceFlag = "force" + latest = "latest" + preRelease = "pre-release" + explainOption = "Explain the difference" ) +type CreateFlags struct { + chainID uint64 + tokenSymbol string + useDefaults bool +} + var ( + createFlags CreateFlags forceCreate bool useSubnetEvm bool genesisFile string vmFile string useCustom bool evmVersion string - evmChainID uint64 - evmToken string - evmDefaults bool useLatestReleasedEvmVersion bool useLatestPreReleasedEvmVersion bool useRepo bool @@ -56,8 +62,6 @@ var ( errMutuallyVMConfigOptions = errors.New("specifying --genesis flag disables SubnetEVM config flags --evm-chain-id,--evm-token,--evm-defaults") ) -const explainOption = "Explain the difference" - // avalanche subnet create func newCreateCmd() *cobra.Command { cmd := &cobra.Command{ @@ -81,9 +85,9 @@ configuration, pass the -f flag.`, cmd.Flags().StringVar(&genesisFile, "genesis", "", "file path of genesis to use") cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") cmd.Flags().StringVar(&evmVersion, "vm-version", "", "version of Subnet-EVM template to use") - cmd.Flags().Uint64Var(&evmChainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") - cmd.Flags().StringVar(&evmToken, "evm-token", "", "token symbol to use with Subnet-EVM") - cmd.Flags().BoolVar(&evmDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") + cmd.Flags().Uint64Var(&createFlags.chainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") + cmd.Flags().StringVar(&createFlags.tokenSymbol, "evm-token", "", "token symbol to use with Subnet-EVM") + cmd.Flags().BoolVar(&createFlags.useDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") cmd.Flags().BoolVar(&useCustom, "custom", false, "use a custom VM template") cmd.Flags().BoolVar(&useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") cmd.Flags().BoolVar(&useLatestReleasedEvmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") @@ -108,8 +112,8 @@ func CallCreate( useCustomParam bool, evmVersionParam string, evmChainIDParam uint64, - evmTokenParam string, - evmDefaultsParam bool, + tokenSymbolParam string, + useDefaultsParam bool, useLatestReleasedEvmVersionParam bool, useLatestPreReleasedEvmVersionParam bool, customVMRepoURLParam string, @@ -120,9 +124,9 @@ func CallCreate( genesisFile = genesisFileParam useSubnetEvm = useSubnetEvmParam evmVersion = evmVersionParam - evmChainID = evmChainIDParam - evmToken = evmTokenParam - evmDefaults = evmDefaultsParam + createFlags.chainID = evmChainIDParam + createFlags.tokenSymbol = tokenSymbolParam + createFlags.useDefaults = useDefaultsParam useLatestReleasedEvmVersion = useLatestReleasedEvmVersionParam useLatestPreReleasedEvmVersion = useLatestPreReleasedEvmVersionParam useCustom = useCustomParam @@ -185,7 +189,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errMutuallyExlusiveVersionOptions } - if genesisFile != "" && (evmChainID != 0 || evmToken != "" || evmDefaults) { + if genesisFile != "" && (createFlags.chainID != 0 || createFlags.tokenSymbol != "" || createFlags.useDefaults) { return errMutuallyVMConfigOptions } @@ -259,293 +263,14 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } } - if subnetType == models.SubnetEvm && genesisFile == "" { - if evmChainID == 0 { - evmChainID, err = app.Prompt.CaptureUint64("Chain ID") - if err != nil { - return err - } - } - } - - // Gas token - externalGasToken := false - if subnetType == models.SubnetEvm && genesisFile == "" { - nativeTokenOption := "The blockchain's native token" - externalTokenOption := "A token from another blockchain" - options := []string{nativeTokenOption, externalTokenOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Which token will be used for transaction fee payments?", - options, - ) - if err != nil { - return err - } - switch option { - case nativeTokenOption: - evmToken, err = app.Prompt.CaptureString("Token Symbol") - if err != nil { - return err - } - allocateToNewKeyOption := "Allocate 1m tokens to new a newly created account" - allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" - customAllocationOption := "Define a custom allocation (Recommended for production)" - options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} - option, err := app.Prompt.CaptureList( - "How should the initial token allocation be structured?", - options, - ) - if err != nil { - return err - } - if option == customAllocationOption { - _, err := app.Prompt.CaptureAddress("Address to allocate to") - if err != nil { - return err - } - _, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", evmToken)) - if err != nil { - return err - } - } - fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" - dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" - options = []string{fixedSupplyOption, dynamicSupplyOption} - option, err = app.Prompt.CaptureList( - "Allow minting new native Tokens? (Native Minter Precompile)", - options, - ) - if err != nil { - return err - } - if option == dynamicSupplyOption { - _, _, _, _, err := vm.GenerateAllowList(app, "mint native tokens", evmVersion) - if err != nil { - return err - } - } - case externalTokenOption: - externalGasToken = true - case explainOption: - ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) - ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and the therefore the transaction fees are completely isolated.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) - ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") - continue - } - break - } - } - - // Transaction / Gas Fees - if subnetType == models.SubnetEvm && genesisFile == "" { - customizeOption := "Customize fee config" - lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" - mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" - highOption := "High disk use / High Throughput 5 mil gas/s" - options := []string{lowOption, mediumOption, highOption, customizeOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "How should the transaction fees be configured on your Blockchain?", - options, - ) - if err != nil { - return err - } - switch option { - case customizeOption: - config := params.ChainConfig{} - _, _, err = vm.CustomizeFeeConfig(config, app) - if err != nil { - return err - } - case explainOption: - ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") - continue - } - break - } - dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" - changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" - options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", - options, - ) - if err != nil { - return err - } - switch option { - case changeFeeSettingsOption: - _, _, _, _, err := vm.GenerateAllowList(app, "adjust the gas fees", evmVersion) - if err != nil { - return err - } - case explainOption: - ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") - continue - // missing case for dontChangeFeeSettingsOption - } - break - } - burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" - distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" - options = []string{burnFees, distributeFees, explainOption} - for { - option, err := app.Prompt.CaptureList( - "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", - options, - ) - if err != nil { - return err - } - switch option { - case distributeFees: - _, _, _, _, err := vm.GenerateAllowList(app, "customize gas fees distribution", evmVersion) - if err != nil { - return err - } - case explainOption: - ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") - continue - } - break - } - } - - // Interoperability var teleporterInfo *teleporter.Info - if subnetType == models.SubnetEvm || genesisFileIsEVM { - if externalGasToken { - useTeleporter = true - } - if evmDefaults { - useTeleporter = true - } - flagName := "teleporter" - if flag := cmd.Flags().Lookup(flagName); flag == nil { - return fmt.Errorf("flag configuration %q not found for cmd %q", flagName, cmd.Use) - } else if !flag.Changed && !externalGasToken { - interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" - isolatedBlockchainOption := "No, I want to run my blockchain isolated" - options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", - options, - ) - if err != nil { - return err - } - switch option { - case interoperatingBlockchainOption: - useTeleporter = true - case isolatedBlockchainOption: - useTeleporter = false - case explainOption: - ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") - continue - } - break - } - } - if useTeleporter && !useWarp { - return fmt.Errorf("warp should be enabled for teleporter to work") - } - if useTeleporter { - teleporterInfo, err = teleporter.GetInfo(app) - if err != nil { - return err - } - } - } - - // Permissioning - if subnetType == models.SubnetEvm && genesisFile == "" { - noOption := "No" - yesOption := "Yes" - options := []string{noOption, yesOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to add permissioning to your blockchain?", - options, - ) - if err != nil { - return err - } - switch option { - case yesOption: - anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" - approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" - options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", - options, - ) - if err != nil { - return err - } - switch option { - case approvedCanSubmitTransactionsOption: - _, _, _, _, err := vm.GenerateAllowList(app, "issue transactions", evmVersion) - if err != nil { - return err - } - case explainOption: - ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") - continue - } - break - } - anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" - approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" - options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Contract Deployer Allow List)", - options, - ) - if err != nil { - return err - } - switch option { - case approvedCanDeployContractsOption: - _, _, _, _, err := vm.GenerateAllowList(app, "deploy smart contracts", evmVersion) - if err != nil { - return err - } - case explainOption: - ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") - continue - } - break - } - case explainOption: - ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as allowlists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") - continue - } - break + if useTeleporter { + teleporterInfo, err = teleporter.GetInfo(app) + if err != nil { + return err } } - return nil - switch subnetType { case models.SubnetEvm: genesisBytes, sc, err = vm.CreateEvmSubnetConfig( @@ -554,9 +279,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { genesisFile, evmVersion, true, - evmChainID, - evmToken, - evmDefaults, + createFlags.chainID, + createFlags.tokenSymbol, + createFlags.useDefaults, useWarp, teleporterInfo, ) @@ -673,3 +398,414 @@ func checkInvalidSubnetNames(name string) error { } return nil } + +type InitialTokenAllocation struct { + allocToNewKey bool + allocToEwoq bool + customAddress common.Address + customBalance uint64 +} + +type FeeConfig struct { + lowThroughput bool + mediumThroughput bool + highThroughput bool + gasLimit *big.Int + blockRate *big.Int + minBaseFee *big.Int + targetGas *big.Int + baseDenominator *big.Int + minBlockGas *big.Int + maxBlockGas *big.Int + gasStep *big.Int +} + +type SubnetEVMGenesisParams struct { + chainID uint64 + useTeleporter bool + useExternalGasToken bool + initialTokenAllocation InitialTokenAllocation + feeConfig FeeConfig + enableNativeMinterPrecompile bool + nativeMinterPrecompileAllowList vm.AllowList + enableFeeManagerPrecompile bool + feeManagerPrecompileAllowList vm.AllowList + enableRewardManagerPrecompile bool + rewardManagerPrecompileAllowList vm.AllowList + enableTransactionPrecompile bool + transactionPrecompileAllowList vm.AllowList + enableContractDeployerPrecompile bool + contractDeployerPrecompileAllowList vm.AllowList + enableWarpPrecompile bool +} + +// ux to get the needed params to build a genesis for a SubnetEVM based VM +// +// if useDefaults is true, it will: +// - use native gas token, allocating 1m to a newly created key +// - customize fee config for low throughput +// - use teleporter +// - enable warp precompile +// - disable the other precompiles +// +// tokenSymbol is not needed to build a genesis but is needed in the ux flow +// as such, is returned separately from the genesis params +// +// prompts the user for chainID and tokenSymbol, unless provided in call args +func getSubnetEVMGenesisParams( + cmd *cobra.Command, + chainID uint64, + tokenSymbol string, + useDefaults bool, +) (SubnetEVMGenesisParams, string, error) { + var ( + err error + cancel bool + params SubnetEVMGenesisParams + ) + // Chain ID + params.chainID = chainID + if params.chainID == 0 { + params.chainID, err = app.Prompt.CaptureUint64("Chain ID") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + // Gas token + nativeTokenOption := "The blockchain's native token" + externalTokenOption := "A token from another blockchain" + options := []string{nativeTokenOption, externalTokenOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Which token will be used for transaction fee payments?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case externalTokenOption: + params.useExternalGasToken = true + case nativeTokenOption: + if tokenSymbol == "" { + tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + if useDefaults { + params.initialTokenAllocation.allocToNewKey = true + break + } + allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" + allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" + customAllocationOption := "Define a custom allocation (Recommended for production)" + options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} + option, err := app.Prompt.CaptureList( + "How should the initial token allocation be structured?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case allocateToNewKeyOption: + params.initialTokenAllocation.allocToNewKey = true + case allocateToEwoqOption: + params.initialTokenAllocation.allocToEwoq = true + case customAllocationOption: + params.initialTokenAllocation.customAddress, err = app.Prompt.CaptureAddress("Address to allocate to") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.initialTokenAllocation.customBalance, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" + dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" + options = []string{fixedSupplyOption, dynamicSupplyOption} + option, err = app.Prompt.CaptureList( + "Allow minting new native Tokens? (Native Minter Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case fixedSupplyOption: + case dynamicSupplyOption: + params.enableNativeMinterPrecompile = true + params.nativeMinterPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "mint native tokens", evmVersion) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + return SubnetEVMGenesisParams{}, "", nil + } + } + case explainOption: + ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) + ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and the therefore the transaction fees are completely isolated.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) + ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") + continue + } + break + } + + // Transaction / Gas Fees + customizeOption := "Customize fee config" + lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" + mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" + highOption := "High disk use / High Throughput 5 mil gas/s" + options = []string{lowOption, mediumOption, highOption, customizeOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "How should the transaction fees be configured on your Blockchain?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + const ( + setGasLimit = "Set gas limit" + setBlockRate = "Set target block rate" + setMinBaseFee = "Set min base fee" + setTargetGas = "Set target gas" + setBaseFeeChangeDenominator = "Set base fee change denominator" + setMinBlockGas = "Set min block gas cost" + setMaxBlockGas = "Set max block gas cost" + setGasStep = "Set block gas cost step" + ) + switch option { + case lowOption: + params.feeConfig.lowThroughput = true + case mediumOption: + params.feeConfig.mediumThroughput = true + case highOption: + params.feeConfig.highThroughput = true + case customizeOption: + params.feeConfig.gasLimit, err = app.Prompt.CapturePositiveBigInt(setGasLimit) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.blockRate, err = app.Prompt.CapturePositiveBigInt(setBlockRate) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.minBaseFee, err = app.Prompt.CapturePositiveBigInt(setMinBaseFee) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.targetGas, err = app.Prompt.CapturePositiveBigInt(setTargetGas) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.baseDenominator, err = app.Prompt.CapturePositiveBigInt(setBaseFeeChangeDenominator) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.minBlockGas, err = app.Prompt.CapturePositiveBigInt(setMinBlockGas) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.maxBlockGas, err = app.Prompt.CapturePositiveBigInt(setMaxBlockGas) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.feeConfig.gasStep, err = app.Prompt.CapturePositiveBigInt(setGasStep) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + case explainOption: + ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") + continue + } + break + } + dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" + changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" + options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case dontChangeFeeSettingsOption: + case changeFeeSettingsOption: + params.enableFeeManagerPrecompile = true + params.feeManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "adjust the gas fees", evmVersion) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + return SubnetEVMGenesisParams{}, "", nil + } + case explainOption: + ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") + continue + } + break + } + burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" + distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" + options = []string{burnFees, distributeFees, explainOption} + for { + option, err := app.Prompt.CaptureList( + "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case burnFees: + case distributeFees: + params.enableRewardManagerPrecompile = true + params.rewardManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "customize gas fees distribution", evmVersion) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + return SubnetEVMGenesisParams{}, "", nil + } + case explainOption: + ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") + continue + } + break + } + + // Interoperability + if params.useExternalGasToken { + useTeleporter = true + } + if useDefaults { + useTeleporter = true + } + flagName := "teleporter" + if flag := cmd.Flags().Lookup(flagName); flag == nil { + return SubnetEVMGenesisParams{}, "", fmt.Errorf("flag configuration %q not found for cmd %q", flagName, cmd.Use) + } else if !flag.Changed && !params.useExternalGasToken { + interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" + isolatedBlockchainOption := "No, I want to run my blockchain isolated" + options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case interoperatingBlockchainOption: + useTeleporter = true + case isolatedBlockchainOption: + useTeleporter = false + case explainOption: + ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") + continue + } + break + } + } + if useTeleporter && !useWarp { + return SubnetEVMGenesisParams{}, "", fmt.Errorf("warp should be enabled for teleporter to work") + } + + // Permissioning + noOption := "No" + yesOption := "Yes" + options = []string{noOption, yesOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to add permissioning to your blockchain?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case yesOption: + anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" + approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" + options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case approvedCanSubmitTransactionsOption: + params.enableTransactionPrecompile = true + params.transactionPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "issue transactions", evmVersion) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + return SubnetEVMGenesisParams{}, "", nil + } + case explainOption: + ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") + continue + } + break + } + anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" + approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" + options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Smart Contract Deployer Allow List Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case approvedCanDeployContractsOption: + params.enableContractDeployerPrecompile = true + params.contractDeployerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "deploy smart contracts", evmVersion) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + return SubnetEVMGenesisParams{}, "", nil + } + case explainOption: + ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") + continue + } + break + } + case explainOption: + ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as allowlists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") + continue + } + break + } + return SubnetEVMGenesisParams{}, "", nil +} diff --git a/pkg/vm/allowlist.go b/pkg/vm/allowlist.go index cceeb6a02..6ccccb924 100644 --- a/pkg/vm/allowlist.go +++ b/pkg/vm/allowlist.go @@ -16,17 +16,19 @@ import ( "golang.org/x/mod/semver" ) -func preview( - adminAddresses []common.Address, - managerAddresses []common.Address, - enabledAddresses []common.Address, -) { +type AllowList struct { + AdminAddresses []common.Address + ManagerAddresses []common.Address + EnabledAddresses []common.Address +} + +func preview(allowList AllowList) { table := tablewriter.NewWriter(os.Stdout) table.SetRowLine(true) table.SetAutoMergeCellsByColumnIndex([]int{0}) - addRoleToPreviewTable(table, "Admins", adminAddresses) - addRoleToPreviewTable(table, "Manager", managerAddresses) - addRoleToPreviewTable(table, "Enabled", enabledAddresses) + addRoleToPreviewTable(table, "Admins", allowList.AdminAddresses) + addRoleToPreviewTable(table, "Manager", allowList.ManagerAddresses) + addRoleToPreviewTable(table, "Enabled", allowList.EnabledAddresses) table.Render() fmt.Println() } @@ -42,9 +44,7 @@ func addRoleToPreviewTable(table *tablewriter.Table, name string, addresses []co func getNewAddresses( app *application.Avalanche, - adminAddresses []common.Address, - managerAddresses []common.Address, - enabledAddresses []common.Address, + allowList AllowList, ) ([]common.Address, error) { newAddresses := []common.Address{} addresses, err := app.Prompt.CaptureAddresses("Enter the address of the account (or multiple comma separated):") @@ -53,11 +53,11 @@ func getNewAddresses( } for _, address := range addresses { switch { - case utils.Belongs(adminAddresses, address): + case utils.Belongs(allowList.AdminAddresses, address): fmt.Println(address.Hex() + " is already allowed as admin role") - case utils.Belongs(managerAddresses, address): + case utils.Belongs(allowList.ManagerAddresses, address): fmt.Println(address.Hex() + " is already allowed as manager role") - case utils.Belongs(enabledAddresses, address): + case utils.Belongs(allowList.EnabledAddresses, address): fmt.Println(address.Hex() + " is already allowed as enabled role") default: newAddresses = append(newAddresses, address) @@ -95,15 +95,13 @@ func GenerateAllowList( app *application.Avalanche, action string, evmVersion string, -) ([]common.Address, []common.Address, []common.Address, bool, error) { +) (AllowList, bool, error) { if !semver.IsValid(evmVersion) { - return nil, nil, nil, false, fmt.Errorf("invalid semantic version %q", evmVersion) + return AllowList{}, false, fmt.Errorf("invalid semantic version %q", evmVersion) } managerRoleEnabled := semver.Compare(evmVersion, "v0.6.4") >= 0 - adminAddresses := []common.Address{} - managerAddresses := []common.Address{} - enabledAddresses := []common.Address{} + allowList := AllowList{} promptTemplate := "Configure the addresses that are allowed to %s" prompt := fmt.Sprintf(promptTemplate, action) @@ -124,7 +122,7 @@ func GenerateAllowList( prompt, []string{addOption, removeOption, previewOption, confirmOption, cancelOption}, ) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } switch option { case addOption: @@ -136,27 +134,27 @@ func GenerateAllowList( } roleOption, err := app.Prompt.CaptureList(addPrompt, options) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } switch roleOption { case adminOption: - addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + addresses, err := getNewAddresses(app, allowList) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } - adminAddresses = append(adminAddresses, addresses...) + allowList.AdminAddresses = append(allowList.AdminAddresses, addresses...) case managerOption: - addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + addresses, err := getNewAddresses(app, allowList) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } - managerAddresses = append(managerAddresses, addresses...) + allowList.ManagerAddresses = append(allowList.ManagerAddresses, addresses...) case enabledOption: - addresses, err := getNewAddresses(app, adminAddresses, managerAddresses, enabledAddresses) + addresses, err := getNewAddresses(app, allowList) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } - enabledAddresses = append(enabledAddresses, addresses...) + allowList.EnabledAddresses = append(allowList.EnabledAddresses, addresses...) case explainOption: fmt.Println("The difference to be given by devrel people") fmt.Println() @@ -175,37 +173,37 @@ func GenerateAllowList( } roleOption, err := app.Prompt.CaptureList(removePrompt, options) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } switch roleOption { case adminOption: - adminAddresses, keepAsking, err = removeAddress(app, adminAddresses, "admin") + allowList.AdminAddresses, keepAsking, err = removeAddress(app, allowList.AdminAddresses, "admin") if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } case managerOption: - managerAddresses, keepAsking, err = removeAddress(app, managerAddresses, "manager") + allowList.ManagerAddresses, keepAsking, err = removeAddress(app, allowList.ManagerAddresses, "manager") if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } case enabledOption: - enabledAddresses, keepAsking, err = removeAddress(app, enabledAddresses, "enabled") + allowList.EnabledAddresses, keepAsking, err = removeAddress(app, allowList.EnabledAddresses, "enabled") if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } case cancelOption: keepAsking = false } } case previewOption: - preview(adminAddresses, managerAddresses, enabledAddresses) + preview(allowList) case confirmOption: - if len(adminAddresses) == 0 && len(managerAddresses) == 0 && len(enabledAddresses) == 0 { + if len(allowList.AdminAddresses) == 0 && len(allowList.ManagerAddresses) == 0 && len(allowList.EnabledAddresses) == 0 { fmt.Println("We need at least one address to have been added to the allow list. Otherwise cancel.") fmt.Println() continue } - preview(adminAddresses, managerAddresses, enabledAddresses) + preview(allowList) confirmPrompt := "Confirm?" yesOption := "Yes" noOption := "No, keep editing" @@ -213,13 +211,13 @@ func GenerateAllowList( confirmPrompt, []string{yesOption, noOption}, ) if err != nil { - return nil, nil, nil, false, err + return AllowList{}, false, err } if confirmOption == yesOption { - return adminAddresses, managerAddresses, enabledAddresses, false, nil + return allowList, false, nil } case cancelOption: - return nil, nil, nil, true, err + return AllowList{}, true, err } } } From 6a87d29f03aa84c643fea275ac46a7f30456edac Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 3 Jul 2024 15:23:06 -0300 Subject: [PATCH 30/63] add useTeleporter --- cmd/subnetcmd/create.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 316cb634e..2a8d72c10 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -447,15 +447,18 @@ type SubnetEVMGenesisParams struct { // - use teleporter // - enable warp precompile // - disable the other precompiles +// in the other case, will prompt for all these settings // // tokenSymbol is not needed to build a genesis but is needed in the ux flow // as such, is returned separately from the genesis params // -// prompts the user for chainID and tokenSymbol, unless provided in call args +// prompts the user for chainID, tokenSymbol, and useTeleporter, unless +// provided in call args func getSubnetEVMGenesisParams( cmd *cobra.Command, chainID uint64, tokenSymbol string, + useTeleporter *bool, useDefaults bool, ) (SubnetEVMGenesisParams, string, error) { var ( From 5ba44c05ae6ac5267042d3aabe24ef6b29b93f5d Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 3 Jul 2024 22:53:55 -0300 Subject: [PATCH 31/63] gas token prompt closed --- cmd/subnetcmd/create.go | 470 +++------------------------------------- 1 file changed, 34 insertions(+), 436 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 2a8d72c10..fd3b3ee54 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "math/big" "sort" "strconv" "strings" @@ -21,25 +20,23 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" - "github.com/ethereum/go-ethereum/common" - - "github.com/ava-labs/avalanchego/utils/logging" "github.com/spf13/cobra" "golang.org/x/mod/semver" ) const ( - forceFlag = "force" - latest = "latest" - preRelease = "pre-release" - explainOption = "Explain the difference" + forceFlag = "force" + latest = "latest" + preRelease = "pre-release" ) type CreateFlags struct { - chainID uint64 - tokenSymbol string - useDefaults bool + chainID uint64 + tokenSymbol string + useDefaults bool + useWarp bool + useTeleporter bool } var ( @@ -53,8 +50,6 @@ var ( useLatestReleasedEvmVersion bool useLatestPreReleasedEvmVersion bool useRepo bool - useTeleporter bool - useWarp bool errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") @@ -98,8 +93,8 @@ configuration, pass the -f flag.`, cmd.Flags().StringVar(&customVMBranch, "custom-vm-branch", "", "custom vm branch or commit") cmd.Flags().StringVar(&customVMBuildScript, "custom-vm-build-script", "", "custom vm build-script") cmd.Flags().BoolVar(&useRepo, "from-github-repo", false, "generate custom VM binary from github repository") - cmd.Flags().BoolVar(&useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") - cmd.Flags().BoolVar(&useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") + cmd.Flags().BoolVar(&createFlags.useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") + cmd.Flags().BoolVar(&createFlags.useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") return cmd } @@ -218,7 +213,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { ux.Logger.PrintToUser(" ") ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. Subnet-EVM can be configured with this CLI to meet the developers requirements without writing code. For more information, please visit: https://github.com/ava-labs/subnet-evm") ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") + ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") continue } break @@ -253,7 +248,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } if subnetType == models.SubnetEvm && genesisFile != "" && !genesisFileIsEVM { - return fmt.Errorf("The provided genesis file has no proper Subnet-EVM format") + return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") } if subnetType == models.SubnetEvm { @@ -264,13 +259,30 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } var teleporterInfo *teleporter.Info - if useTeleporter { + if createFlags.useTeleporter { teleporterInfo, err = teleporter.GetInfo(app) if err != nil { return err } } + var useTeleporter *bool + flagName := "teleporter" + if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { + useTeleporter = &createFlags.useTeleporter + } + + params, tokenSymbol, err := promptSubnetEVMGenesisParams( + createFlags.chainID, + createFlags.tokenSymbol, + useTeleporter, + createFlags.useDefaults, + createFlags.useWarp, + ) + if err != nil { + return err + } + switch subnetType { case models.SubnetEvm: genesisBytes, sc, err = vm.CreateEvmSubnetConfig( @@ -280,9 +292,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { evmVersion, true, createFlags.chainID, - createFlags.tokenSymbol, + tokenSymbol, createFlags.useDefaults, - useWarp, + createFlags.useWarp, teleporterInfo, ) if err != nil { @@ -306,8 +318,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errors.New("not implemented") } - if useTeleporter { - sc.TeleporterReady = useTeleporter + if params.useTeleporter { + sc.TeleporterReady = true sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version if genesisFile != "" && genesisFileIsEVM { @@ -398,417 +410,3 @@ func checkInvalidSubnetNames(name string) error { } return nil } - -type InitialTokenAllocation struct { - allocToNewKey bool - allocToEwoq bool - customAddress common.Address - customBalance uint64 -} - -type FeeConfig struct { - lowThroughput bool - mediumThroughput bool - highThroughput bool - gasLimit *big.Int - blockRate *big.Int - minBaseFee *big.Int - targetGas *big.Int - baseDenominator *big.Int - minBlockGas *big.Int - maxBlockGas *big.Int - gasStep *big.Int -} - -type SubnetEVMGenesisParams struct { - chainID uint64 - useTeleporter bool - useExternalGasToken bool - initialTokenAllocation InitialTokenAllocation - feeConfig FeeConfig - enableNativeMinterPrecompile bool - nativeMinterPrecompileAllowList vm.AllowList - enableFeeManagerPrecompile bool - feeManagerPrecompileAllowList vm.AllowList - enableRewardManagerPrecompile bool - rewardManagerPrecompileAllowList vm.AllowList - enableTransactionPrecompile bool - transactionPrecompileAllowList vm.AllowList - enableContractDeployerPrecompile bool - contractDeployerPrecompileAllowList vm.AllowList - enableWarpPrecompile bool -} - -// ux to get the needed params to build a genesis for a SubnetEVM based VM -// -// if useDefaults is true, it will: -// - use native gas token, allocating 1m to a newly created key -// - customize fee config for low throughput -// - use teleporter -// - enable warp precompile -// - disable the other precompiles -// in the other case, will prompt for all these settings -// -// tokenSymbol is not needed to build a genesis but is needed in the ux flow -// as such, is returned separately from the genesis params -// -// prompts the user for chainID, tokenSymbol, and useTeleporter, unless -// provided in call args -func getSubnetEVMGenesisParams( - cmd *cobra.Command, - chainID uint64, - tokenSymbol string, - useTeleporter *bool, - useDefaults bool, -) (SubnetEVMGenesisParams, string, error) { - var ( - err error - cancel bool - params SubnetEVMGenesisParams - ) - // Chain ID - params.chainID = chainID - if params.chainID == 0 { - params.chainID, err = app.Prompt.CaptureUint64("Chain ID") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - } - // Gas token - nativeTokenOption := "The blockchain's native token" - externalTokenOption := "A token from another blockchain" - options := []string{nativeTokenOption, externalTokenOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Which token will be used for transaction fee payments?", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case externalTokenOption: - params.useExternalGasToken = true - case nativeTokenOption: - if tokenSymbol == "" { - tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - } - if useDefaults { - params.initialTokenAllocation.allocToNewKey = true - break - } - allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" - allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" - customAllocationOption := "Define a custom allocation (Recommended for production)" - options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} - option, err := app.Prompt.CaptureList( - "How should the initial token allocation be structured?", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case allocateToNewKeyOption: - params.initialTokenAllocation.allocToNewKey = true - case allocateToEwoqOption: - params.initialTokenAllocation.allocToEwoq = true - case customAllocationOption: - params.initialTokenAllocation.customAddress, err = app.Prompt.CaptureAddress("Address to allocate to") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.initialTokenAllocation.customBalance, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - } - fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" - dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" - options = []string{fixedSupplyOption, dynamicSupplyOption} - option, err = app.Prompt.CaptureList( - "Allow minting new native Tokens? (Native Minter Precompile)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case fixedSupplyOption: - case dynamicSupplyOption: - params.enableNativeMinterPrecompile = true - params.nativeMinterPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "mint native tokens", evmVersion) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - return SubnetEVMGenesisParams{}, "", nil - } - } - case explainOption: - ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) - ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and the therefore the transaction fees are completely isolated.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) - ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") - continue - } - break - } - - // Transaction / Gas Fees - customizeOption := "Customize fee config" - lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" - mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" - highOption := "High disk use / High Throughput 5 mil gas/s" - options = []string{lowOption, mediumOption, highOption, customizeOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "How should the transaction fees be configured on your Blockchain?", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - const ( - setGasLimit = "Set gas limit" - setBlockRate = "Set target block rate" - setMinBaseFee = "Set min base fee" - setTargetGas = "Set target gas" - setBaseFeeChangeDenominator = "Set base fee change denominator" - setMinBlockGas = "Set min block gas cost" - setMaxBlockGas = "Set max block gas cost" - setGasStep = "Set block gas cost step" - ) - switch option { - case lowOption: - params.feeConfig.lowThroughput = true - case mediumOption: - params.feeConfig.mediumThroughput = true - case highOption: - params.feeConfig.highThroughput = true - case customizeOption: - params.feeConfig.gasLimit, err = app.Prompt.CapturePositiveBigInt(setGasLimit) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.blockRate, err = app.Prompt.CapturePositiveBigInt(setBlockRate) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.minBaseFee, err = app.Prompt.CapturePositiveBigInt(setMinBaseFee) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.targetGas, err = app.Prompt.CapturePositiveBigInt(setTargetGas) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.baseDenominator, err = app.Prompt.CapturePositiveBigInt(setBaseFeeChangeDenominator) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.minBlockGas, err = app.Prompt.CapturePositiveBigInt(setMinBlockGas) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.maxBlockGas, err = app.Prompt.CapturePositiveBigInt(setMaxBlockGas) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.feeConfig.gasStep, err = app.Prompt.CapturePositiveBigInt(setGasStep) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - case explainOption: - ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") - continue - } - break - } - dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" - changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" - options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case dontChangeFeeSettingsOption: - case changeFeeSettingsOption: - params.enableFeeManagerPrecompile = true - params.feeManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "adjust the gas fees", evmVersion) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - return SubnetEVMGenesisParams{}, "", nil - } - case explainOption: - ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") - continue - } - break - } - burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" - distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" - options = []string{burnFees, distributeFees, explainOption} - for { - option, err := app.Prompt.CaptureList( - "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case burnFees: - case distributeFees: - params.enableRewardManagerPrecompile = true - params.rewardManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "customize gas fees distribution", evmVersion) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - return SubnetEVMGenesisParams{}, "", nil - } - case explainOption: - ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") - continue - } - break - } - - // Interoperability - if params.useExternalGasToken { - useTeleporter = true - } - if useDefaults { - useTeleporter = true - } - flagName := "teleporter" - if flag := cmd.Flags().Lookup(flagName); flag == nil { - return SubnetEVMGenesisParams{}, "", fmt.Errorf("flag configuration %q not found for cmd %q", flagName, cmd.Use) - } else if !flag.Changed && !params.useExternalGasToken { - interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" - isolatedBlockchainOption := "No, I want to run my blockchain isolated" - options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case interoperatingBlockchainOption: - useTeleporter = true - case isolatedBlockchainOption: - useTeleporter = false - case explainOption: - ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") - continue - } - break - } - } - if useTeleporter && !useWarp { - return SubnetEVMGenesisParams{}, "", fmt.Errorf("warp should be enabled for teleporter to work") - } - - // Permissioning - noOption := "No" - yesOption := "Yes" - options = []string{noOption, yesOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to add permissioning to your blockchain?", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case yesOption: - anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" - approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" - options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case approvedCanSubmitTransactionsOption: - params.enableTransactionPrecompile = true - params.transactionPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "issue transactions", evmVersion) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - return SubnetEVMGenesisParams{}, "", nil - } - case explainOption: - ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") - continue - } - break - } - anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" - approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" - options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} - for { - option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Smart Contract Deployer Allow List Precompile)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case approvedCanDeployContractsOption: - params.enableContractDeployerPrecompile = true - params.contractDeployerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "deploy smart contracts", evmVersion) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - return SubnetEVMGenesisParams{}, "", nil - } - case explainOption: - ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") - continue - } - break - } - case explainOption: - ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as allowlists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") - continue - } - break - } - return SubnetEVMGenesisParams{}, "", nil -} From f44c98f89b2e98d05ef9669c0d393d3e7125ad54 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 3 Jul 2024 23:26:26 -0300 Subject: [PATCH 32/63] fee config ok --- cmd/subnetcmd/create.go | 55 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index fd3b3ee54..6aa34dabf 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -32,24 +32,24 @@ const ( ) type CreateFlags struct { - chainID uint64 - tokenSymbol string - useDefaults bool - useWarp bool - useTeleporter bool -} - -var ( - createFlags CreateFlags - forceCreate bool - useSubnetEvm bool - genesisFile string - vmFile string - useCustom bool + chainID uint64 + tokenSymbol string + useDefaults bool + useWarp bool + useTeleporter bool evmVersion string useLatestReleasedEvmVersion bool useLatestPreReleasedEvmVersion bool - useRepo bool +} + +var ( + createFlags CreateFlags + forceCreate bool + useSubnetEvm bool + genesisFile string + vmFile string + useCustom bool + useRepo bool errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") @@ -79,13 +79,13 @@ configuration, pass the -f flag.`, } cmd.Flags().StringVar(&genesisFile, "genesis", "", "file path of genesis to use") cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") - cmd.Flags().StringVar(&evmVersion, "vm-version", "", "version of Subnet-EVM template to use") + cmd.Flags().StringVar(&createFlags.evmVersion, "vm-version", "", "version of Subnet-EVM template to use") cmd.Flags().Uint64Var(&createFlags.chainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") cmd.Flags().StringVar(&createFlags.tokenSymbol, "evm-token", "", "token symbol to use with Subnet-EVM") cmd.Flags().BoolVar(&createFlags.useDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") cmd.Flags().BoolVar(&useCustom, "custom", false, "use a custom VM template") - cmd.Flags().BoolVar(&useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") - cmd.Flags().BoolVar(&useLatestReleasedEvmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") + cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") + cmd.Flags().BoolVar(&createFlags.useLatestReleasedEvmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") cmd.Flags().BoolVarP(&forceCreate, forceFlag, "f", false, "overwrite the existing configuration if one exists") cmd.Flags().StringVar(&vmFile, "vm", "", "file path of custom vm to use. alias to custom-vm-path") cmd.Flags().StringVar(&vmFile, "custom-vm-path", "", "file path of custom vm to use") @@ -118,12 +118,12 @@ func CallCreate( forceCreate = forceCreateParam genesisFile = genesisFileParam useSubnetEvm = useSubnetEvmParam - evmVersion = evmVersionParam + createFlags.evmVersion = evmVersionParam createFlags.chainID = evmChainIDParam createFlags.tokenSymbol = tokenSymbolParam createFlags.useDefaults = useDefaultsParam - useLatestReleasedEvmVersion = useLatestReleasedEvmVersionParam - useLatestPreReleasedEvmVersion = useLatestPreReleasedEvmVersionParam + createFlags.useLatestReleasedEvmVersion = useLatestReleasedEvmVersionParam + createFlags.useLatestPreReleasedEvmVersion = useLatestPreReleasedEvmVersionParam useCustom = useCustomParam customVMRepoURL = customVMRepoURLParam customVMBranch = customVMBranchParam @@ -180,7 +180,11 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errors.New("too many VMs selected. Provide at most one VM selection flag") } - if !flags.EnsureMutuallyExclusive([]bool{useLatestReleasedEvmVersion, useLatestPreReleasedEvmVersion, evmVersion != ""}) { + if !flags.EnsureMutuallyExclusive([]bool{ + createFlags.useLatestReleasedEvmVersion, + createFlags.useLatestPreReleasedEvmVersion, + createFlags.evmVersion != "", + }) { return errMutuallyExlusiveVersionOptions } @@ -227,11 +231,11 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { err error ) - if useLatestReleasedEvmVersion { + evmVersion := createFlags.evmVersion + if createFlags.useLatestReleasedEvmVersion { evmVersion = latest } - - if useLatestPreReleasedEvmVersion { + if createFlags.useLatestPreReleasedEvmVersion { evmVersion = preRelease } @@ -273,6 +277,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } params, tokenSymbol, err := promptSubnetEVMGenesisParams( + evmVersion, createFlags.chainID, createFlags.tokenSymbol, useTeleporter, From 1d119fc767ac2b3ffade9c24e6e6042487a2dc0f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 3 Jul 2024 23:56:27 -0300 Subject: [PATCH 33/63] add missing file --- cmd/subnetcmd/createPrompts.go | 517 +++++++++++++++++++++++++++++++++ 1 file changed, 517 insertions(+) create mode 100644 cmd/subnetcmd/createPrompts.go diff --git a/cmd/subnetcmd/createPrompts.go b/cmd/subnetcmd/createPrompts.go new file mode 100644 index 000000000..d69ab7e54 --- /dev/null +++ b/cmd/subnetcmd/createPrompts.go @@ -0,0 +1,517 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package subnetcmd + +import ( + "fmt" + "math/big" + + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/avalanche-cli/pkg/vm" + "github.com/ethereum/go-ethereum/common" + + "github.com/ava-labs/avalanchego/utils/logging" +) + +const explainOption = "Explain the difference" + +type InitialTokenAllocation struct { + allocToNewKey bool + allocToEwoq bool + customAddress common.Address + customBalance uint64 +} + +type FeeConfig struct { + lowThroughput bool + mediumThroughput bool + highThroughput bool + gasLimit *big.Int + blockRate *big.Int + minBaseFee *big.Int + targetGas *big.Int + baseDenominator *big.Int + minBlockGas *big.Int + maxBlockGas *big.Int + gasStep *big.Int +} + +type SubnetEVMGenesisParams struct { + chainID uint64 + useTeleporter bool + useExternalGasToken bool + initialTokenAllocation InitialTokenAllocation + feeConfig FeeConfig + enableNativeMinterPrecompile bool + nativeMinterPrecompileAllowList vm.AllowList + enableFeeManagerPrecompile bool + feeManagerPrecompileAllowList vm.AllowList + enableRewardManagerPrecompile bool + rewardManagerPrecompileAllowList vm.AllowList + enableTransactionPrecompile bool + transactionPrecompileAllowList vm.AllowList + enableContractDeployerPrecompile bool + contractDeployerPrecompileAllowList vm.AllowList + enableWarpPrecompile bool +} + +// ux to get the needed params to build a genesis for a SubnetEVM based VM +// +// if useDefaults is true, it will: +// - use native gas token, allocating 1m to a newly created key +// - customize fee config for low throughput +// - use teleporter +// - enable warp precompile +// - disable the other precompiles +// in the other case, will prompt for all these settings +// +// tokenSymbol is not needed to build a genesis but is needed in the ux flow +// as such, is returned separately from the genesis params +// +// prompts the user for chainID, tokenSymbol, and useTeleporter, unless +// provided in call args +func promptSubnetEVMGenesisParams( + version string, + chainID uint64, + tokenSymbol string, + useTeleporter *bool, + useDefaults bool, + useWarp bool, +) (SubnetEVMGenesisParams, string, error) { + var ( + err error + cancel bool + params SubnetEVMGenesisParams + ) + // Chain ID + params.chainID = chainID + if params.chainID == 0 { + params.chainID, err = app.Prompt.CaptureUint64("Chain ID") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + // Gas Token + params, tokenSymbol, err = promptGasToken(version, tokenSymbol, useDefaults, params) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + // Transaction / Gas Fees + params, err = promptFeeConfig(version, useDefaults, params) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + // Interoperability + params, err = promptInteropt(useTeleporter, useDefaults, params) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + // Warp + params.enableWarpPrecompile = useWarp + if (params.useTeleporter || params.useExternalGasToken) && !params.enableWarpPrecompile { + return SubnetEVMGenesisParams{}, "", fmt.Errorf("warp should be enabled for teleporter to work") + } + // Permissioning + params, err = promptPermissioning(version, useDefaults, params) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + return params, tokenSymbol, nil +} + +// prompts for wether to use a remote or native gas token, +// and in the case of native, also prompts for token symbol, +// initial token allocation, and native minter precompile +// configuration +// +// if tokenSymbol is not defined, will prompt for it +// is useDefaults is true, will: +// - use native gas token, allocating 1m to a newly created key +// - disable native minter precompile +func promptGasToken( + version string, + tokenSymbol string, + useDefaults bool, + params SubnetEVMGenesisParams, +) (SubnetEVMGenesisParams, string, error) { + var ( + err error + cancel bool + ) + if useDefaults { + if tokenSymbol == "" { + tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + params.initialTokenAllocation.allocToNewKey = true + return params, tokenSymbol, nil + } + nativeTokenOption := "The blockchain's native token" + externalTokenOption := "A token from another blockchain" + options := []string{nativeTokenOption, externalTokenOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Which token will be used for transaction fee payments?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case externalTokenOption: + params.useExternalGasToken = true + case nativeTokenOption: + if tokenSymbol == "" { + tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" + allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" + customAllocationOption := "Define a custom allocation (Recommended for production)" + options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} + option, err := app.Prompt.CaptureList( + "How should the initial token allocation be structured?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case allocateToNewKeyOption: + params.initialTokenAllocation.allocToNewKey = true + case allocateToEwoqOption: + params.initialTokenAllocation.allocToEwoq = true + case customAllocationOption: + params.initialTokenAllocation.customAddress, err = app.Prompt.CaptureAddress("Address to allocate to") + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + params.initialTokenAllocation.customBalance, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + for { + fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" + dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" + options = []string{fixedSupplyOption, dynamicSupplyOption} + option, err = app.Prompt.CaptureList( + "Allow minting new native Tokens? (Native Minter Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + switch option { + case fixedSupplyOption: + case dynamicSupplyOption: + params.nativeMinterPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "mint native tokens", version) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + if cancel { + continue + } + params.enableNativeMinterPrecompile = true + } + break + } + case explainOption: + ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) + ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and therefore the transaction fees are completely isolated.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) + ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") + continue + } + break + } + return params, tokenSymbol, nil +} + +// prompts for transaction fees, fee manager precompile +// and reward manager precompile configuration +// +// is useDefaults is true, will: +// - customize fee config for low throughput +// - disable fee manager precompile +// - disable reward manager precompile +func promptFeeConfig( + version string, + useDefaults bool, + params SubnetEVMGenesisParams, +) (SubnetEVMGenesisParams, error) { + if useDefaults { + params.feeConfig.lowThroughput = true + return params, nil + } + var cancel bool + customizeOption := "Customize fee config" + lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" + mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" + highOption := "High disk use / High Throughput 5 mil gas/s" + options := []string{lowOption, mediumOption, highOption, customizeOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "How should the transaction fees be configured on your Blockchain?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + const ( + setGasLimit = "Set gas limit" + setBlockRate = "Set target block rate" + setMinBaseFee = "Set min base fee" + setTargetGas = "Set target gas" + setBaseFeeChangeDenominator = "Set base fee change denominator" + setMinBlockGas = "Set min block gas cost" + setMaxBlockGas = "Set max block gas cost" + setGasStep = "Set block gas cost step" + ) + switch option { + case lowOption: + params.feeConfig.lowThroughput = true + case mediumOption: + params.feeConfig.mediumThroughput = true + case highOption: + params.feeConfig.highThroughput = true + case customizeOption: + params.feeConfig.gasLimit, err = app.Prompt.CapturePositiveBigInt(setGasLimit) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.blockRate, err = app.Prompt.CapturePositiveBigInt(setBlockRate) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.minBaseFee, err = app.Prompt.CapturePositiveBigInt(setMinBaseFee) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.targetGas, err = app.Prompt.CapturePositiveBigInt(setTargetGas) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.baseDenominator, err = app.Prompt.CapturePositiveBigInt(setBaseFeeChangeDenominator) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.minBlockGas, err = app.Prompt.CapturePositiveBigInt(setMinBlockGas) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.maxBlockGas, err = app.Prompt.CapturePositiveBigInt(setMaxBlockGas) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + params.feeConfig.gasStep, err = app.Prompt.CapturePositiveBigInt(setGasStep) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + case explainOption: + ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") + continue + } + break + } + dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" + changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" + options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case dontChangeFeeSettingsOption: + case changeFeeSettingsOption: + params.feeManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "adjust the gas fees", version) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + if cancel { + continue + } + params.enableFeeManagerPrecompile = true + case explainOption: + ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") + continue + } + break + } + burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" + distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" + options = []string{burnFees, distributeFees, explainOption} + for { + option, err := app.Prompt.CaptureList( + "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case burnFees: + case distributeFees: + params.rewardManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "customize gas fees distribution", version) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + if cancel { + continue + } + params.enableRewardManagerPrecompile = true + case explainOption: + ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") + continue + } + break + } + return params, nil +} + +// if useTeleporter is defined, will enable/disable teleporter based on it +// is useDefaults is true, will enable teleporter +// if using external gas token, will assume teleporter to be enabled +// if other cases, prompts the user for wether to enable teleporter +func promptInteropt( + useTeleporter *bool, + useDefaults bool, + params SubnetEVMGenesisParams, +) (SubnetEVMGenesisParams, error) { + switch { + case useTeleporter != nil: + params.useTeleporter = *useTeleporter + case useDefaults: + params.useTeleporter = true + case params.useExternalGasToken: + default: + interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" + isolatedBlockchainOption := "No, I want to run my blockchain isolated" + options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case isolatedBlockchainOption: + case interoperatingBlockchainOption: + params.useTeleporter = true + case explainOption: + ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") + continue + } + break + } + } + return params, nil +} + +func promptPermissioning( + version string, + useDefaults bool, + params SubnetEVMGenesisParams, +) (SubnetEVMGenesisParams, error) { + if useDefaults { + return params, nil + } + var cancel bool + noOption := "No" + yesOption := "Yes" + options := []string{noOption, yesOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to add permissioning to your blockchain?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case yesOption: + anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" + approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" + options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case approvedCanSubmitTransactionsOption: + params.transactionPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "issue transactions", version) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + if cancel { + continue + } + params.enableTransactionPrecompile = true + case explainOption: + ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") + continue + } + break + } + anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" + approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" + options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Smart Contract Deployer Allow List Precompile)", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case approvedCanDeployContractsOption: + params.contractDeployerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "deploy smart contracts", version) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + if cancel { + continue + } + params.enableContractDeployerPrecompile = true + case explainOption: + ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") + continue + } + break + } + case explainOption: + ux.Logger.PrintToUser("You can permission your chain at different levels of interaction with EVM-Precompiles. These precompiles act as allowlists, preventing unapproved users from deploying smart contracts, sending transactions, or interacting with your blockchain. You may choose to apply as many or as little of these rules as you see fit.") + continue + } + break + } + return params, nil +} From 014a1b2e5ca8b74718c512b827627c45b4bc48fe Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 4 Jul 2024 11:09:22 -0300 Subject: [PATCH 34/63] vm type cleanup --- cmd/subnetcmd/create.go | 82 ++++++---------------------------- cmd/subnetcmd/createPrompts.go | 39 +++++++++++++++- cmd/subnetcmd/import_public.go | 13 ++---- 3 files changed, 54 insertions(+), 80 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 6aa34dabf..027c27624 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -131,36 +131,6 @@ func CallCreate( return createSubnetConfig(cmd, []string{subnetName}) } -func detectVMTypeFromFlags() { - // assumes custom - if customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { - useCustom = true - } -} - -func moreThanOneVMSelected() bool { - vmVars := []bool{useSubnetEvm, useCustom} - firstSelect := false - for _, val := range vmVars { - if firstSelect && val { - return true - } else if val { - firstSelect = true - } - } - return false -} - -func getVMFromFlag() models.VMType { - if useSubnetEvm { - return models.SubnetEvm - } - if useCustom { - return models.CustomVM - } - return "" -} - // override postrun function from root.go, so that we don't double send metrics for the same command func handlePostRun(_ *cobra.Command, _ []string) {} @@ -174,9 +144,12 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return fmt.Errorf("subnet name %q is invalid: %w", subnetName, err) } - detectVMTypeFromFlags() + // if given custom repo info, assumes custom VM + if vmFile != "" || customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { + useCustom = true + } - if moreThanOneVMSelected() { + if !flags.EnsureMutuallyExclusive([]bool{useSubnetEvm, useCustom}) { return errors.New("too many VMs selected. Provide at most one VM selection flag") } @@ -192,43 +165,14 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errMutuallyVMConfigOptions } - subnetType := getVMFromFlag() - - if subnetType == "" { - subnetEvmOption := "Subnet-EVM" - customVMOption := "Custom VM" - options := []string{subnetEvmOption, customVMOption, explainOption} - var subnetTypeStr string - for { - option, err := app.Prompt.CaptureList( - "VM", - options, - ) - if err != nil { - return err - } - switch option { - case subnetEvmOption: - subnetTypeStr = models.SubnetEvm - case customVMOption: - subnetTypeStr = models.CustomVM - case explainOption: - ux.Logger.PrintToUser("Virtual machines are the blueprint the defines the application-level logic of a blockchain. It determines the language and rules for writing and executing smart contracts, as well as other blockchain logic.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. Subnet-EVM can be configured with this CLI to meet the developers requirements without writing code. For more information, please visit: https://github.com/ava-labs/subnet-evm") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") - continue - } - break - } - subnetType = models.VMTypeFromString(subnetTypeStr) + vmType, err := promptVMType(useSubnetEvm, useCustom) + if err != nil { + return err } var ( genesisBytes []byte sc *models.Sidecar - err error ) evmVersion := createFlags.evmVersion @@ -251,11 +195,11 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } } - if subnetType == models.SubnetEvm && genesisFile != "" && !genesisFileIsEVM { + if vmType == models.SubnetEvm && genesisFile != "" && !genesisFileIsEVM { return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") } - if subnetType == models.SubnetEvm { + if vmType == models.SubnetEvm { evmVersion, err = vm.GetVMVersion(app, constants.SubnetEVMRepoName, evmVersion) if err != nil { return err @@ -288,7 +232,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } - switch subnetType { + switch vmType { case models.SubnetEvm: genesisBytes, sc, err = vm.CreateEvmSubnetConfig( app, @@ -344,8 +288,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err = app.CreateSidecar(sc); err != nil { return err } - if subnetType == models.SubnetEvm { - err = sendMetrics(cmd, subnetType.RepoName(), subnetName) + if vmType == models.SubnetEvm { + err = sendMetrics(cmd, vmType.RepoName(), subnetName) if err != nil { return err } diff --git a/cmd/subnetcmd/createPrompts.go b/cmd/subnetcmd/createPrompts.go index d69ab7e54..96de31989 100644 --- a/cmd/subnetcmd/createPrompts.go +++ b/cmd/subnetcmd/createPrompts.go @@ -6,6 +6,7 @@ import ( "fmt" "math/big" + "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ethereum/go-ethereum/common" @@ -55,6 +56,43 @@ type SubnetEVMGenesisParams struct { enableWarpPrecompile bool } +func promptVMType(useSubnetEvm bool, useCustom bool) (models.VMType, error) { + if useSubnetEvm { + return models.SubnetEvm, nil + } + if useCustom { + return models.CustomVM, nil + } + subnetEvmOption := "Subnet-EVM" + customVMOption := "Custom VM" + options := []string{subnetEvmOption, customVMOption, explainOption} + var subnetTypeStr string + for { + option, err := app.Prompt.CaptureList( + "VM", + options, + ) + if err != nil { + return "", err + } + switch option { + case subnetEvmOption: + subnetTypeStr = models.SubnetEvm + case customVMOption: + subnetTypeStr = models.CustomVM + case explainOption: + ux.Logger.PrintToUser("Virtual machines are the blueprint the defines the application-level logic of a blockchain. It determines the language and rules for writing and executing smart contracts, as well as other blockchain logic.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. Subnet-EVM can be configured with this CLI to meet the developers requirements without writing code. For more information, please visit: https://github.com/ava-labs/subnet-evm") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") + continue + } + break + } + return models.VMTypeFromString(subnetTypeStr), nil +} + // ux to get the needed params to build a genesis for a SubnetEVM based VM // // if useDefaults is true, it will: @@ -80,7 +118,6 @@ func promptSubnetEVMGenesisParams( ) (SubnetEVMGenesisParams, string, error) { var ( err error - cancel bool params SubnetEVMGenesisParams ) // Chain ID diff --git a/cmd/subnetcmd/import_public.go b/cmd/subnetcmd/import_public.go index 0794b6a59..f6c6f2019 100644 --- a/cmd/subnetcmd/import_public.go +++ b/cmd/subnetcmd/import_public.go @@ -133,16 +133,9 @@ func importPublic(*cobra.Command, []string) error { // TODO: it's probably possible to deploy VMs with the same name on a public network // In this case, an import could clash because the tool supports unique names only - vmType := getVMFromFlag() - if vmType == "" { - subnetTypeStr, err := app.Prompt.CaptureList( - "What's this VM's type?", - []string{models.SubnetEvm, models.CustomVM}, - ) - if err != nil { - return err - } - vmType = models.VMTypeFromString(subnetTypeStr) + vmType, err := promptVMType(useSubnetEvm, useCustom) + if err != nil { + return err } vmIDstr := vmID.String() From 06a4955db543e954258c55d3d0205d9700a250db Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 4 Jul 2024 11:13:22 -0300 Subject: [PATCH 35/63] more cleanup --- cmd/subnetcmd/create.go | 28 ++++++++--------- cmd/subnetcmd/create_test.go | 57 ---------------------------------- cmd/subnetcmd/import_public.go | 6 ++-- 3 files changed, 18 insertions(+), 73 deletions(-) delete mode 100644 cmd/subnetcmd/create_test.go diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 027c27624..7675b0a4f 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -32,6 +32,8 @@ const ( ) type CreateFlags struct { + useSubnetEvm bool + useCustomVm bool chainID uint64 tokenSymbol string useDefaults bool @@ -43,13 +45,11 @@ type CreateFlags struct { } var ( - createFlags CreateFlags - forceCreate bool - useSubnetEvm bool - genesisFile string - vmFile string - useCustom bool - useRepo bool + createFlags CreateFlags + forceCreate bool + genesisFile string + vmFile string + useRepo bool errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") @@ -78,12 +78,12 @@ configuration, pass the -f flag.`, PersistentPostRun: handlePostRun, } cmd.Flags().StringVar(&genesisFile, "genesis", "", "file path of genesis to use") - cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") + cmd.Flags().BoolVar(&createFlags.useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") cmd.Flags().StringVar(&createFlags.evmVersion, "vm-version", "", "version of Subnet-EVM template to use") cmd.Flags().Uint64Var(&createFlags.chainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") cmd.Flags().StringVar(&createFlags.tokenSymbol, "evm-token", "", "token symbol to use with Subnet-EVM") cmd.Flags().BoolVar(&createFlags.useDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") - cmd.Flags().BoolVar(&useCustom, "custom", false, "use a custom VM template") + cmd.Flags().BoolVar(&createFlags.useCustomVm, "custom", false, "use a custom VM template") cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") cmd.Flags().BoolVar(&createFlags.useLatestReleasedEvmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") cmd.Flags().BoolVarP(&forceCreate, forceFlag, "f", false, "overwrite the existing configuration if one exists") @@ -117,14 +117,14 @@ func CallCreate( ) error { forceCreate = forceCreateParam genesisFile = genesisFileParam - useSubnetEvm = useSubnetEvmParam + createFlags.useSubnetEvm = useSubnetEvmParam createFlags.evmVersion = evmVersionParam createFlags.chainID = evmChainIDParam createFlags.tokenSymbol = tokenSymbolParam createFlags.useDefaults = useDefaultsParam createFlags.useLatestReleasedEvmVersion = useLatestReleasedEvmVersionParam createFlags.useLatestPreReleasedEvmVersion = useLatestPreReleasedEvmVersionParam - useCustom = useCustomParam + createFlags.useCustomVm = useCustomParam customVMRepoURL = customVMRepoURLParam customVMBranch = customVMBranchParam customVMBuildScript = customVMBuildScriptParam @@ -146,10 +146,10 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // if given custom repo info, assumes custom VM if vmFile != "" || customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { - useCustom = true + createFlags.useCustomVm = true } - if !flags.EnsureMutuallyExclusive([]bool{useSubnetEvm, useCustom}) { + if !flags.EnsureMutuallyExclusive([]bool{createFlags.useSubnetEvm, createFlags.useCustomVm}) { return errors.New("too many VMs selected. Provide at most one VM selection flag") } @@ -165,7 +165,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return errMutuallyVMConfigOptions } - vmType, err := promptVMType(useSubnetEvm, useCustom) + vmType, err := promptVMType(createFlags.useSubnetEvm, createFlags.useCustomVm) if err != nil { return err } diff --git a/cmd/subnetcmd/create_test.go b/cmd/subnetcmd/create_test.go deleted file mode 100644 index 577594f09..000000000 --- a/cmd/subnetcmd/create_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -package subnetcmd - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_moreThanOneVMSelected(t *testing.T) { - type test struct { - name string - useSubnetVM bool - useCustomVM bool - expectedResult bool - } - tests := []test{ - { - name: "One Selected", - useSubnetVM: true, - useCustomVM: false, - expectedResult: false, - }, - { - name: "One Selected Reverse", - useSubnetVM: true, - useCustomVM: false, - expectedResult: false, - }, - { - name: "None Selected", - useSubnetVM: false, - useCustomVM: false, - expectedResult: false, - }, - { - name: "Multiple Selected", - useSubnetVM: true, - useCustomVM: true, - expectedResult: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - // Set vars - useSubnetEvm = tt.useSubnetVM - useCustom = tt.useCustomVM - - // Check how many selected - result := moreThanOneVMSelected() - require.Equal(tt.expectedResult, result) - }) - } -} diff --git a/cmd/subnetcmd/import_public.go b/cmd/subnetcmd/import_public.go index f6c6f2019..5ac44fe49 100644 --- a/cmd/subnetcmd/import_public.go +++ b/cmd/subnetcmd/import_public.go @@ -24,6 +24,8 @@ var ( importPublicSupportedNetworkOptions = []networkoptions.NetworkOption{networkoptions.Fuji, networkoptions.Mainnet} blockchainIDstr string nodeURL string + useSubnetEvm bool + useCustomVm bool ) // avalanche subnet import public @@ -45,7 +47,7 @@ flag.`, cmd.Flags().StringVar(&nodeURL, "node-url", "", "[optional] URL of an already running subnet validator") cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "import a subnet-evm") - cmd.Flags().BoolVar(&useCustom, "custom", false, "use a custom VM template") + cmd.Flags().BoolVar(&useCustomVm, "custom", false, "use a custom VM template") cmd.Flags().BoolVar( &overwriteImport, "force", @@ -133,7 +135,7 @@ func importPublic(*cobra.Command, []string) error { // TODO: it's probably possible to deploy VMs with the same name on a public network // In this case, an import could clash because the tool supports unique names only - vmType, err := promptVMType(useSubnetEvm, useCustom) + vmType, err := promptVMType(useSubnetEvm, useCustomVm) if err != nil { return err } From 17915941701e40d3b1f7489bea1a2055695b27fb Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 4 Jul 2024 11:38:50 -0300 Subject: [PATCH 36/63] move all subnet evm prompts to the same file --- cmd/subnetcmd/create.go | 20 +++--- cmd/subnetcmd/createPrompts.go | 101 +++++++++++++++++++++++++++++ pkg/vm/createEvm.go | 112 --------------------------------- 3 files changed, 110 insertions(+), 123 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 7675b0a4f..b629ed732 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -182,7 +182,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if createFlags.useLatestPreReleasedEvmVersion { evmVersion = preRelease } - if evmVersion != latest && evmVersion != preRelease && evmVersion != "" && !semver.IsValid(evmVersion) { return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", evmVersion) } @@ -200,15 +199,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } if vmType == models.SubnetEvm { - evmVersion, err = vm.GetVMVersion(app, constants.SubnetEVMRepoName, evmVersion) - if err != nil { - return err - } - } - - var teleporterInfo *teleporter.Info - if createFlags.useTeleporter { - teleporterInfo, err = teleporter.GetInfo(app) + evmVersion, err = promptVMVersion(app, constants.SubnetEVMRepoName, evmVersion) if err != nil { return err } @@ -219,7 +210,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { useTeleporter = &createFlags.useTeleporter } - params, tokenSymbol, err := promptSubnetEVMGenesisParams( evmVersion, createFlags.chainID, @@ -232,6 +222,14 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } + var teleporterInfo *teleporter.Info + if params.useTeleporter || params.useExternalGasToken { + teleporterInfo, err = teleporter.GetInfo(app) + if err != nil { + return err + } + } + switch vmType { case models.SubnetEvm: genesisBytes, sc, err = vm.CreateEvmSubnetConfig( diff --git a/cmd/subnetcmd/createPrompts.go b/cmd/subnetcmd/createPrompts.go index 96de31989..cfc316106 100644 --- a/cmd/subnetcmd/createPrompts.go +++ b/cmd/subnetcmd/createPrompts.go @@ -5,10 +5,15 @@ package subnetcmd import ( "fmt" "math/big" + "os" + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/binutils" + "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" + "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/avalanchego/utils/logging" @@ -552,3 +557,99 @@ func promptPermissioning( } return params, nil } + +func promptVMVersion( + app *application.Avalanche, + repoName string, + vmVersion string, +) (string, error) { + switch vmVersion { + case "latest": + return app.Downloader.GetLatestReleaseVersion(binutils.GetGithubLatestReleaseURL( + constants.AvaLabsOrg, + repoName, + )) + case "pre-release": + return app.Downloader.GetLatestPreReleaseVersion( + constants.AvaLabsOrg, + repoName, + ) + case "": + return promptUserForVMVersion(app, repoName) + } + return vmVersion, nil +} + +func promptUserForVMVersion( + app *application.Avalanche, + repoName string, +) (string, error) { + var ( + latestReleaseVersion string + latestPreReleaseVersion string + err error + ) + if os.Getenv(constants.OperateOfflineEnvVarName) == "" { + latestReleaseVersion, err = app.Downloader.GetLatestReleaseVersion( + binutils.GetGithubLatestReleaseURL( + constants.AvaLabsOrg, + repoName, + ), + ) + if err != nil { + return "", err + } + latestPreReleaseVersion, err = app.Downloader.GetLatestPreReleaseVersion( + constants.AvaLabsOrg, + repoName, + ) + if err != nil { + return "", err + } + } else { + latestReleaseVersion = evm.Version + latestPreReleaseVersion = evm.Version + } + + useCustom := "Specify custom version" + useLatestRelease := "Use latest release version" + useLatestPreRelease := "Use latest pre-release version" + + defaultPrompt := "Version" + + versionOptions := []string{useLatestRelease, useCustom} + if latestPreReleaseVersion != latestReleaseVersion { + versionOptions = []string{useLatestPreRelease, useLatestRelease, useCustom} + } + + versionOption, err := app.Prompt.CaptureList( + defaultPrompt, + versionOptions, + ) + if err != nil { + return "", err + } + + if versionOption == useLatestPreRelease { + return latestPreReleaseVersion, err + } + + if versionOption == useLatestRelease { + return latestReleaseVersion, err + } + + // prompt for version + versions, err := app.Downloader.GetAllReleasesForRepo( + constants.AvaLabsOrg, + constants.SubnetEVMRepoName, + ) + if err != nil { + return "", err + } + version, err := app.Prompt.CaptureList("Pick the version for this VM", versions) + if err != nil { + return "", err + } + + return version, nil +} diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 0408f29ce..5e79c4b15 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -13,23 +13,17 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/binutils" - "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/statemachine" "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" ) -var versionComments = map[string]string{ - "v0.6.0-fuji": " (recommended for fuji durango)", -} - func CreateEvmSubnetConfig( app *application.Avalanche, subnetName string, @@ -248,109 +242,3 @@ func ensureAdminsHaveBalance(admins []common.Address, alloc core.GenesisAlloc) e "none of the addresses in the transaction allow list precompile have any tokens allocated to them. Currently, no address can transact on the network. Airdrop some funds to one of the allow list addresses to continue", ) } - -func GetVMVersion( - app *application.Avalanche, - repoName string, - vmVersion string, -) (string, error) { - var err error - switch vmVersion { - case "latest": - vmVersion, err = app.Downloader.GetLatestReleaseVersion(binutils.GetGithubLatestReleaseURL( - constants.AvaLabsOrg, - repoName, - )) - if err != nil { - return "", err - } - case "pre-release": - vmVersion, err = app.Downloader.GetLatestPreReleaseVersion( - constants.AvaLabsOrg, - repoName, - ) - if err != nil { - return "", err - } - case "": - vmVersion, err = askForVMVersion(app, repoName) - if err != nil { - return "", err - } - } - return vmVersion, nil -} - -func askForVMVersion( - app *application.Avalanche, - repoName string, -) (string, error) { - var ( - latestReleaseVersion string - latestPreReleaseVersion string - err error - ) - if os.Getenv(constants.OperateOfflineEnvVarName) == "" { - latestReleaseVersion, err = app.Downloader.GetLatestReleaseVersion( - binutils.GetGithubLatestReleaseURL( - constants.AvaLabsOrg, - repoName, - ), - ) - if err != nil { - return "", err - } - latestPreReleaseVersion, err = app.Downloader.GetLatestPreReleaseVersion( - constants.AvaLabsOrg, - repoName, - ) - if err != nil { - return "", err - } - } else { - latestReleaseVersion = evm.Version - latestPreReleaseVersion = evm.Version - } - - useCustom := "Specify custom version" - useLatestRelease := "Use latest release version" + versionComments[latestReleaseVersion] - useLatestPreRelease := "Use latest pre-release version" + versionComments[latestPreReleaseVersion] - - defaultPrompt := "Version" - - versionOptions := []string{useLatestRelease, useCustom} - if latestPreReleaseVersion != latestReleaseVersion { - versionOptions = []string{useLatestPreRelease, useLatestRelease, useCustom} - } - - versionOption, err := app.Prompt.CaptureList( - defaultPrompt, - versionOptions, - ) - if err != nil { - return "", err - } - - if versionOption == useLatestPreRelease { - return latestPreReleaseVersion, err - } - - if versionOption == useLatestRelease { - return latestReleaseVersion, err - } - - // prompt for version - versions, err := app.Downloader.GetAllReleasesForRepo( - constants.AvaLabsOrg, - constants.SubnetEVMRepoName, - ) - if err != nil { - return "", err - } - version, err := app.Prompt.CaptureList("Pick the version for this VM", versions) - if err != nil { - return "", err - } - - return version, nil -} From 21ec0c1072bb1a28d935cf7dc57baac3a539b9a5 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 4 Jul 2024 16:26:07 -0300 Subject: [PATCH 37/63] nits --- cmd/subnetcmd/create.go | 172 +++++++++++++++++++++------------------- pkg/utils/common.go | 2 +- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index b629ed732..31629cc4d 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "os" "sort" "strconv" "strings" @@ -32,16 +33,16 @@ const ( ) type CreateFlags struct { - useSubnetEvm bool - useCustomVm bool - chainID uint64 - tokenSymbol string - useDefaults bool - useWarp bool - useTeleporter bool - evmVersion string - useLatestReleasedEvmVersion bool - useLatestPreReleasedEvmVersion bool + useSubnetEvm bool + useCustomVm bool + chainID uint64 + tokenSymbol string + useDefaults bool + useWarp bool + useTeleporter bool + vmVersion string + useLatestReleasedVmVersion bool + useLatestPreReleasedVmVersion bool } var ( @@ -53,8 +54,8 @@ var ( errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") - errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") - errMutuallyVMConfigOptions = errors.New("specifying --genesis flag disables SubnetEVM config flags --evm-chain-id,--evm-token,--evm-defaults") + errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") + errMutuallyExclusiveVMConfigOptions = errors.New("specifying --genesis flag disable flags --evm-chain-id,--evm-token,--evm-defaults") ) // avalanche subnet create @@ -79,13 +80,13 @@ configuration, pass the -f flag.`, } cmd.Flags().StringVar(&genesisFile, "genesis", "", "file path of genesis to use") cmd.Flags().BoolVar(&createFlags.useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") - cmd.Flags().StringVar(&createFlags.evmVersion, "vm-version", "", "version of Subnet-EVM template to use") + cmd.Flags().BoolVar(&createFlags.useCustomVm, "custom", false, "use a custom VM template") + cmd.Flags().StringVar(&createFlags.vmVersion, "vm-version", "", "version of Subnet-EVM template to use") + cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedVmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") + cmd.Flags().BoolVar(&createFlags.useLatestReleasedVmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") cmd.Flags().Uint64Var(&createFlags.chainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") cmd.Flags().StringVar(&createFlags.tokenSymbol, "evm-token", "", "token symbol to use with Subnet-EVM") cmd.Flags().BoolVar(&createFlags.useDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") - cmd.Flags().BoolVar(&createFlags.useCustomVm, "custom", false, "use a custom VM template") - cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedEvmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") - cmd.Flags().BoolVar(&createFlags.useLatestReleasedEvmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") cmd.Flags().BoolVarP(&forceCreate, forceFlag, "f", false, "overwrite the existing configuration if one exists") cmd.Flags().StringVar(&vmFile, "vm", "", "file path of custom vm to use. alias to custom-vm-path") cmd.Flags().StringVar(&vmFile, "custom-vm-path", "", "file path of custom vm to use") @@ -105,12 +106,12 @@ func CallCreate( genesisFileParam string, useSubnetEvmParam bool, useCustomParam bool, - evmVersionParam string, + vmVersionParam string, evmChainIDParam uint64, tokenSymbolParam string, useDefaultsParam bool, - useLatestReleasedEvmVersionParam bool, - useLatestPreReleasedEvmVersionParam bool, + useLatestReleasedVMVersionParam bool, + useLatestPreReleasedVMVersionParam bool, customVMRepoURLParam string, customVMBranchParam string, customVMBuildScriptParam string, @@ -118,12 +119,12 @@ func CallCreate( forceCreate = forceCreateParam genesisFile = genesisFileParam createFlags.useSubnetEvm = useSubnetEvmParam - createFlags.evmVersion = evmVersionParam + createFlags.vmVersion = vmVersionParam createFlags.chainID = evmChainIDParam createFlags.tokenSymbol = tokenSymbolParam createFlags.useDefaults = useDefaultsParam - createFlags.useLatestReleasedEvmVersion = useLatestReleasedEvmVersionParam - createFlags.useLatestPreReleasedEvmVersion = useLatestPreReleasedEvmVersionParam + createFlags.useLatestReleasedVmVersion = useLatestReleasedVMVersionParam + createFlags.useLatestPreReleasedVmVersion = useLatestPreReleasedVMVersionParam createFlags.useCustomVm = useCustomParam customVMRepoURL = customVMRepoURLParam customVMBranch = customVMBranchParam @@ -136,6 +137,7 @@ func handlePostRun(_ *cobra.Command, _ []string) {} func createSubnetConfig(cmd *cobra.Command, args []string) error { subnetName := args[0] + if app.GenesisExists(subnetName) && !forceCreate { return errors.New("configuration already exists. Use --" + forceFlag + " parameter to overwrite") } @@ -144,27 +146,31 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return fmt.Errorf("subnet name %q is invalid: %w", subnetName, err) } - // if given custom repo info, assumes custom VM - if vmFile != "" || customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { - createFlags.useCustomVm = true - } - - if !flags.EnsureMutuallyExclusive([]bool{createFlags.useSubnetEvm, createFlags.useCustomVm}) { - return errors.New("too many VMs selected. Provide at most one VM selection flag") - } - + // version flags exclusiveness if !flags.EnsureMutuallyExclusive([]bool{ - createFlags.useLatestReleasedEvmVersion, - createFlags.useLatestPreReleasedEvmVersion, - createFlags.evmVersion != "", + createFlags.useLatestReleasedVmVersion, + createFlags.useLatestPreReleasedVmVersion, + createFlags.vmVersion != "", }) { return errMutuallyExlusiveVersionOptions } + // genesis flags exclusiveness if genesisFile != "" && (createFlags.chainID != 0 || createFlags.tokenSymbol != "" || createFlags.useDefaults) { - return errMutuallyVMConfigOptions + return errMutuallyExclusiveVMConfigOptions } + // if given custom repo info, assumes custom VM + if vmFile != "" || customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { + createFlags.useCustomVm = true + } + + // vm type exclusiveness + if !flags.EnsureMutuallyExclusive([]bool{createFlags.useSubnetEvm, createFlags.useCustomVm}) { + return errors.New("flags --evm,--custom are mutually exclusive") + } + + // get vm kind vmType, err := promptVMType(createFlags.useSubnetEvm, createFlags.useCustomVm) if err != nil { return err @@ -175,51 +181,53 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { sc *models.Sidecar ) - evmVersion := createFlags.evmVersion - if createFlags.useLatestReleasedEvmVersion { - evmVersion = latest - } - if createFlags.useLatestPreReleasedEvmVersion { - evmVersion = preRelease - } - if evmVersion != latest && evmVersion != preRelease && evmVersion != "" && !semver.IsValid(evmVersion) { - return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", evmVersion) - } - - genesisFileIsEVM := false - if genesisFile != "" { - genesisFileIsEVM, err = utils.PathIsSubnetEVMGenesis(genesisFile) - if err != nil { - return err - } - } - - if vmType == models.SubnetEvm && genesisFile != "" && !genesisFileIsEVM { - return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") - } - if vmType == models.SubnetEvm { - evmVersion, err = promptVMVersion(app, constants.SubnetEVMRepoName, evmVersion) + // get vm version + vmVersion := createFlags.vmVersion + if createFlags.useLatestReleasedVmVersion { + vmVersion = latest + } + if createFlags.useLatestPreReleasedVmVersion { + vmVersion = preRelease + } + if vmVersion != latest && vmVersion != preRelease && vmVersion != "" && !semver.IsValid(vmVersion) { + return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", vmVersion) + } + vmVersion, err = promptVMVersion(app, constants.SubnetEVMRepoName, vmVersion) if err != nil { return err } - } - var useTeleporter *bool - flagName := "teleporter" - if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { - useTeleporter = &createFlags.useTeleporter - } - params, tokenSymbol, err := promptSubnetEVMGenesisParams( - evmVersion, - createFlags.chainID, - createFlags.tokenSymbol, - useTeleporter, - createFlags.useDefaults, - createFlags.useWarp, - ) - if err != nil { - return err + if genesisFile != "" { + // load given genesis + if evmCompatibleGenesis, err := utils.FileIsSubnetEVMGenesis(genesisFile); err != nil { + return err + } else if !evmCompatibleGenesis { + return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") + } + ux.Logger.PrintToUser("importing genesis for subnet %s", subnetName) + genesisBytes, err = os.ReadFile(genesisFile) + if err != nil { + return err + } + } else { + var useTeleporterFlag *bool + flagName := "teleporter" + if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { + useTeleporterFlag = &createFlags.useTeleporter + } + params, tokenSymbol, err := promptSubnetEVMGenesisParams( + vmVersion, + createFlags.chainID, + createFlags.tokenSymbol, + useTeleporterFlag, + createFlags.useDefaults, + createFlags.useWarp, + ) + if err != nil { + return err + } + } } var teleporterInfo *teleporter.Info @@ -236,7 +244,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { app, subnetName, genesisFile, - evmVersion, + vmVersion, true, createFlags.chainID, tokenSymbol, @@ -269,11 +277,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { sc.TeleporterReady = true sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version - if genesisFile != "" && genesisFileIsEVM { - // evm genesis file was given. make appropriate checks and customizations for teleporter - genesisBytes, err = addSubnetEVMGenesisPrefundedAddress(genesisBytes, teleporterInfo.FundedAddress, teleporterInfo.FundedBalance.String()) - if err != nil { + if genesisFile != "" { + if evmCompatibleGenesis, err := utils.FileIsSubnetEVMGenesis(genesisFile); err != nil { return err + } else if !evmCompatibleGenesis { + // evm genesis file was given. make appropriate checks and customizations for teleporter + genesisBytes, err = addSubnetEVMGenesisPrefundedAddress(genesisBytes, teleporterInfo.FundedAddress, teleporterInfo.FundedBalance.String()) + if err != nil { + return err + } } } } diff --git a/pkg/utils/common.go b/pkg/utils/common.go index a28dc88ff..a96f93e94 100644 --- a/pkg/utils/common.go +++ b/pkg/utils/common.go @@ -484,7 +484,7 @@ func ByteSliceIsSubnetEvmGenesis(bs []byte) bool { return err == nil } -func PathIsSubnetEVMGenesis(genesisPath string) (bool, error) { +func FileIsSubnetEVMGenesis(genesisPath string) (bool, error) { genesisBytes, err := os.ReadFile(genesisPath) if err != nil { return false, err From 8c2fe517de65894fefc40df965e0b62fcd7da586 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 4 Jul 2024 16:57:59 -0300 Subject: [PATCH 38/63] lint --- cmd/subnetcmd/create.go | 86 +++++++++++++-------------- cmd/subnetcmd/createPrompts.go | 4 +- cmd/subnetcmd/import_public.go | 6 +- cmd/teleportercmd/bridgecmd/deploy.go | 3 +- 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 31629cc4d..370e9401a 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -34,15 +34,15 @@ const ( type CreateFlags struct { useSubnetEvm bool - useCustomVm bool + useCustomVM bool chainID uint64 tokenSymbol string useDefaults bool useWarp bool useTeleporter bool vmVersion string - useLatestReleasedVmVersion bool - useLatestPreReleasedVmVersion bool + useLatestReleasedVMVersion bool + useLatestPreReleasedVMVersion bool } var ( @@ -80,10 +80,10 @@ configuration, pass the -f flag.`, } cmd.Flags().StringVar(&genesisFile, "genesis", "", "file path of genesis to use") cmd.Flags().BoolVar(&createFlags.useSubnetEvm, "evm", false, "use the Subnet-EVM as the base template") - cmd.Flags().BoolVar(&createFlags.useCustomVm, "custom", false, "use a custom VM template") + cmd.Flags().BoolVar(&createFlags.useCustomVM, "custom", false, "use a custom VM template") cmd.Flags().StringVar(&createFlags.vmVersion, "vm-version", "", "version of Subnet-EVM template to use") - cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedVmVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") - cmd.Flags().BoolVar(&createFlags.useLatestReleasedVmVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") + cmd.Flags().BoolVar(&createFlags.useLatestPreReleasedVMVersion, preRelease, false, "use latest Subnet-EVM pre-released version, takes precedence over --vm-version") + cmd.Flags().BoolVar(&createFlags.useLatestReleasedVMVersion, latest, false, "use latest Subnet-EVM released version, takes precedence over --vm-version") cmd.Flags().Uint64Var(&createFlags.chainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM") cmd.Flags().StringVar(&createFlags.tokenSymbol, "evm-token", "", "token symbol to use with Subnet-EVM") cmd.Flags().BoolVar(&createFlags.useDefaults, "evm-defaults", false, "use default settings for fees/airdrop/precompiles/teleporter with Subnet-EVM") @@ -123,9 +123,9 @@ func CallCreate( createFlags.chainID = evmChainIDParam createFlags.tokenSymbol = tokenSymbolParam createFlags.useDefaults = useDefaultsParam - createFlags.useLatestReleasedVmVersion = useLatestReleasedVMVersionParam - createFlags.useLatestPreReleasedVmVersion = useLatestPreReleasedVMVersionParam - createFlags.useCustomVm = useCustomParam + createFlags.useLatestReleasedVMVersion = useLatestReleasedVMVersionParam + createFlags.useLatestPreReleasedVMVersion = useLatestPreReleasedVMVersionParam + createFlags.useCustomVM = useCustomParam customVMRepoURL = customVMRepoURLParam customVMBranch = customVMBranchParam customVMBuildScript = customVMBuildScriptParam @@ -148,8 +148,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // version flags exclusiveness if !flags.EnsureMutuallyExclusive([]bool{ - createFlags.useLatestReleasedVmVersion, - createFlags.useLatestPreReleasedVmVersion, + createFlags.useLatestReleasedVMVersion, + createFlags.useLatestPreReleasedVMVersion, createFlags.vmVersion != "", }) { return errMutuallyExlusiveVersionOptions @@ -162,16 +162,16 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { // if given custom repo info, assumes custom VM if vmFile != "" || customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { - createFlags.useCustomVm = true + createFlags.useCustomVM = true } // vm type exclusiveness - if !flags.EnsureMutuallyExclusive([]bool{createFlags.useSubnetEvm, createFlags.useCustomVm}) { + if !flags.EnsureMutuallyExclusive([]bool{createFlags.useSubnetEvm, createFlags.useCustomVM}) { return errors.New("flags --evm,--custom are mutually exclusive") } // get vm kind - vmType, err := promptVMType(createFlags.useSubnetEvm, createFlags.useCustomVm) + vmType, err := promptVMType(createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { return err } @@ -181,13 +181,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { sc *models.Sidecar ) + var teleporterInfo *teleporter.Info + if vmType == models.SubnetEvm { // get vm version vmVersion := createFlags.vmVersion - if createFlags.useLatestReleasedVmVersion { + if createFlags.useLatestReleasedVMVersion { vmVersion = latest } - if createFlags.useLatestPreReleasedVmVersion { + if createFlags.useLatestPreReleasedVMVersion { vmVersion = preRelease } if vmVersion != latest && vmVersion != preRelease && vmVersion != "" && !semver.IsValid(vmVersion) { @@ -227,35 +229,31 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - } - } - var teleporterInfo *teleporter.Info - if params.useTeleporter || params.useExternalGasToken { - teleporterInfo, err = teleporter.GetInfo(app) - if err != nil { - return err - } - } + if params.useTeleporter || params.useExternalGasToken { + teleporterInfo, err = teleporter.GetInfo(app) + if err != nil { + return err + } + } - switch vmType { - case models.SubnetEvm: - genesisBytes, sc, err = vm.CreateEvmSubnetConfig( - app, - subnetName, - genesisFile, - vmVersion, - true, - createFlags.chainID, - tokenSymbol, - createFlags.useDefaults, - createFlags.useWarp, - teleporterInfo, - ) - if err != nil { - return err + genesisBytes, sc, err = vm.CreateEvmSubnetConfig( + app, + subnetName, + genesisFile, + vmVersion, + true, + createFlags.chainID, + tokenSymbol, + createFlags.useDefaults, + createFlags.useWarp, + teleporterInfo, + ) + if err != nil { + return err + } } - case models.CustomVM: + } else { genesisBytes, sc, err = vm.CreateCustomSubnetConfig( app, subnetName, @@ -269,11 +267,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - default: - return errors.New("not implemented") } - if params.useTeleporter { + if teleporterInfo != nil { sc.TeleporterReady = true sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version diff --git a/cmd/subnetcmd/createPrompts.go b/cmd/subnetcmd/createPrompts.go index cfc316106..e0c53e506 100644 --- a/cmd/subnetcmd/createPrompts.go +++ b/cmd/subnetcmd/createPrompts.go @@ -564,12 +564,12 @@ func promptVMVersion( vmVersion string, ) (string, error) { switch vmVersion { - case "latest": + case latest: return app.Downloader.GetLatestReleaseVersion(binutils.GetGithubLatestReleaseURL( constants.AvaLabsOrg, repoName, )) - case "pre-release": + case preRelease: return app.Downloader.GetLatestPreReleaseVersion( constants.AvaLabsOrg, repoName, diff --git a/cmd/subnetcmd/import_public.go b/cmd/subnetcmd/import_public.go index 5ac44fe49..55a298383 100644 --- a/cmd/subnetcmd/import_public.go +++ b/cmd/subnetcmd/import_public.go @@ -25,7 +25,7 @@ var ( blockchainIDstr string nodeURL string useSubnetEvm bool - useCustomVm bool + useCustomVM bool ) // avalanche subnet import public @@ -47,7 +47,7 @@ flag.`, cmd.Flags().StringVar(&nodeURL, "node-url", "", "[optional] URL of an already running subnet validator") cmd.Flags().BoolVar(&useSubnetEvm, "evm", false, "import a subnet-evm") - cmd.Flags().BoolVar(&useCustomVm, "custom", false, "use a custom VM template") + cmd.Flags().BoolVar(&useCustomVM, "custom", false, "use a custom VM template") cmd.Flags().BoolVar( &overwriteImport, "force", @@ -135,7 +135,7 @@ func importPublic(*cobra.Command, []string) error { // TODO: it's probably possible to deploy VMs with the same name on a public network // In this case, an import could clash because the tool supports unique names only - vmType, err := promptVMType(useSubnetEvm, useCustomVm) + vmType, err := promptVMType(useSubnetEvm, useCustomVM) if err != nil { return err } diff --git a/cmd/teleportercmd/bridgecmd/deploy.go b/cmd/teleportercmd/bridgecmd/deploy.go index 0501e51c6..bbadbf7eb 100644 --- a/cmd/teleportercmd/bridgecmd/deploy.go +++ b/cmd/teleportercmd/bridgecmd/deploy.go @@ -170,8 +170,7 @@ func CallDeploy(_ []string, flags DeployFlags) error { if err != nil { return err } - switch option { - case erc20Option: + if option == erc20Option { erc20TokenAddr, err := app.Prompt.CaptureAddress( "Enter the address of the ERC-20 Token", ) From 0360aa94721b0627496d6414e3ae117cc361a981 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 5 Jul 2024 10:10:42 -0300 Subject: [PATCH 39/63] nit --- pkg/vm/allowlist.go | 8 ++++---- pkg/vm/precompiles.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/vm/allowlist.go b/pkg/vm/allowlist.go index 1731f4e14..3a893c498 100644 --- a/pkg/vm/allowlist.go +++ b/pkg/vm/allowlist.go @@ -124,7 +124,7 @@ func GenerateAllowList( for { options := []string{addOption, removeOption, previewOption, confirmOption, cancelOption} - if len(adminAddresses) == 0 && len(managerAddresses) == 0 && len(enabledAddresses) == 0 { + if len(allowList.AdminAddresses) == 0 && len(allowList.ManagerAddresses) == 0 && len(allowList.EnabledAddresses) == 0 { options = utils.RemoveFromSlice(options, removeOption) } option, err := app.Prompt.CaptureList(prompt, options) @@ -176,13 +176,13 @@ func GenerateAllowList( for keepAsking { removePrompt := "What role does the address that should be removed have?" options := []string{} - if len(adminAddresses) != 0 { + if len(allowList.AdminAddresses) != 0 { options = append(options, adminOption) } - if len(managerAddresses) != 0 && managerRoleEnabled { + if len(allowList.ManagerAddresses) != 0 && managerRoleEnabled { options = append(options, managerOption) } - if len(enabledAddresses) != 0 { + if len(allowList.EnabledAddresses) != 0 { options = append(options, enabledOption) } options = append(options, cancelOption) diff --git a/pkg/vm/precompiles.go b/pkg/vm/precompiles.go index 909992394..e7703bbe0 100644 --- a/pkg/vm/precompiles.go +++ b/pkg/vm/precompiles.go @@ -44,14 +44,14 @@ func configureContractAllowList( "on your subnet.\nFor more information visit " + //nolint:goconst "https://docs.avax.network/subnets/customize-a-subnet/#restricting-smart-contract-deployers\n" ux.Logger.PrintToUser(info) - admins, managers, enabled, cancelled, err := GenerateAllowList(app, "deploy smart contracts", subnetEvmVersion) + allowList, cancelled, err := GenerateAllowList(app, "deploy smart contracts", subnetEvmVersion) if cancelled || err != nil { return config, cancelled, err } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: admins, - ManagerAddresses: managers, - EnabledAddresses: enabled, + AdminAddresses: allowList.AdminAddresses, + ManagerAddresses: allowList.ManagerAddresses, + EnabledAddresses: allowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), From 59f1ef09233ab77a0b0fb769791379f40a11fda0 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Sun, 7 Jul 2024 11:16:05 -0300 Subject: [PATCH 40/63] lint --- cmd/subnetcmd/upgradecmd/generate.go | 8 +++---- pkg/vm/allowlist.go | 4 +++- pkg/vm/precompiles.go | 32 ++++++++++++++-------------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index cac5b2a41..3bba9cc2b 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -444,15 +444,15 @@ func promptAdminManagerAndEnabledAddresses( sc *models.Sidecar, action string, ) ([]common.Address, []common.Address, []common.Address, bool, error) { - admin, manager, enabled, cancelled, err := vm.GenerateAllowList(app, action, sc.VMVersion) + allowList, cancelled, err := vm.GenerateAllowList(app, action, sc.VMVersion) if cancelled || err != nil { return nil, nil, nil, cancelled, err } - if err := ensureHaveBalance(sc, adminLabel, admin); err != nil { + if err := ensureHaveBalance(sc, adminLabel, allowList.AdminAddresses); err != nil { return nil, nil, nil, false, err } - if err := ensureHaveBalance(sc, managerLabel, admin); err != nil { + if err := ensureHaveBalance(sc, managerLabel, allowList.ManagerAddresses); err != nil { return nil, nil, nil, false, err } - return admin, manager, enabled, false, nil + return allowList.AdminAddresses, allowList.ManagerAddresses, allowList.EnabledAddresses, false, nil } diff --git a/pkg/vm/allowlist.go b/pkg/vm/allowlist.go index 3a893c498..536c7ace1 100644 --- a/pkg/vm/allowlist.go +++ b/pkg/vm/allowlist.go @@ -162,7 +162,6 @@ func GenerateAllowList( return AllowList{}, false, err } allowList.EnabledAddresses = append(allowList.EnabledAddresses, addresses...) - case explainOption: case explainOption: fmt.Println("Enabled addresses can perform the permissioned behavior (issuing transactions, deploying contracts,\netc.), but cannot modify other roles.\nManager addresses can perform the permissioned behavior and can change enabled/disable addresses.\nAdmin addresses can perform the permissioned behavior, but can also add/remove other Admins, Managers\nand Enabled addresses.") fmt.Println() @@ -211,6 +210,9 @@ func GenerateAllowList( } } case previewOption: + preview(allowList) + case confirmOption: + preview(allowList) confirmPrompt := "Confirm?" yesOption := "Yes" noOption := "No, keep editing" diff --git a/pkg/vm/precompiles.go b/pkg/vm/precompiles.go index e7703bbe0..62c42819c 100644 --- a/pkg/vm/precompiles.go +++ b/pkg/vm/precompiles.go @@ -68,14 +68,14 @@ func configureTransactionAllowList( "on your subnet.\nFor more information visit " + "https://docs.avax.network/subnets/customize-a-subnet/#restricting-who-can-submit-transactions\n" ux.Logger.PrintToUser(info) - admins, managers, enabled, cancelled, err := GenerateAllowList(app, "issue transactions", subnetEvmVersion) + allowList, cancelled, err := GenerateAllowList(app, "issue transactions", subnetEvmVersion) if cancelled || err != nil { return config, cancelled, err } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: admins, - ManagerAddresses: managers, - EnabledAddresses: enabled, + AdminAddresses: allowList.AdminAddresses, + ManagerAddresses: allowList.ManagerAddresses, + EnabledAddresses: allowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), @@ -92,14 +92,14 @@ func configureMinterList( "on your subnet.\nFor more information visit " + "https://docs.avax.network/subnets/customize-a-subnet#minting-native-coins\n" ux.Logger.PrintToUser(info) - admins, managers, enabled, cancelled, err := GenerateAllowList(app, "mint native tokens", subnetEvmVersion) + allowList, cancelled, err := GenerateAllowList(app, "mint native tokens", subnetEvmVersion) if cancelled || err != nil { return config, cancelled, err } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: admins, - ManagerAddresses: managers, - EnabledAddresses: enabled, + AdminAddresses: allowList.AdminAddresses, + ManagerAddresses: allowList.ManagerAddresses, + EnabledAddresses: allowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), @@ -116,14 +116,14 @@ func configureFeeConfigAllowList( "performing a hardfork.\nFor more information visit " + "https://docs.avax.network/subnets/customize-a-subnet#configuring-dynamic-fees\n" ux.Logger.PrintToUser(info) - admins, managers, enabled, cancelled, err := GenerateAllowList(app, "adjust the gas fees", subnetEvmVersion) + allowList, cancelled, err := GenerateAllowList(app, "adjust the gas fees", subnetEvmVersion) if cancelled || err != nil { return config, cancelled, err } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: admins, - ManagerAddresses: managers, - EnabledAddresses: enabled, + AdminAddresses: allowList.AdminAddresses, + ManagerAddresses: allowList.ManagerAddresses, + EnabledAddresses: allowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), @@ -140,14 +140,14 @@ func configureRewardManager( "https://docs.avax.network/subnets/customize-a-subnet#changing-fee-reward-mechanisms\n" ux.Logger.PrintToUser(info) config := rewardmanager.Config{} - admins, managers, enabled, cancelled, err := GenerateAllowList(app, "customize fee distribution", subnetEvmVersion) + allowList, cancelled, err := GenerateAllowList(app, "customize fee distribution", subnetEvmVersion) if cancelled || err != nil { return config, cancelled, err } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: admins, - ManagerAddresses: managers, - EnabledAddresses: enabled, + AdminAddresses: allowList.AdminAddresses, + ManagerAddresses: allowList.ManagerAddresses, + EnabledAddresses: allowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), From dcec26cc980a653472dfa210b261c82d2ceb5a9f Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 10 Jul 2024 12:00:27 -0300 Subject: [PATCH 41/63] move prompts to vm package --- cmd/subnetcmd/create.go | 9 +-- cmd/subnetcmd/import_public.go | 2 +- .../createPrompts.go => pkg/vm/prompts.go | 68 +++++++++++-------- 3 files changed, 46 insertions(+), 33 deletions(-) rename cmd/subnetcmd/createPrompts.go => pkg/vm/prompts.go (92%) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 370e9401a..627ab9423 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -171,7 +171,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } // get vm kind - vmType, err := promptVMType(createFlags.useSubnetEvm, createFlags.useCustomVM) + vmType, err := vm.PromptVMType(app, createFlags.useSubnetEvm, createFlags.useCustomVM) if err != nil { return err } @@ -195,7 +195,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if vmVersion != latest && vmVersion != preRelease && vmVersion != "" && !semver.IsValid(vmVersion) { return fmt.Errorf("invalid version string, should be semantic version (ex: v1.1.1): %s", vmVersion) } - vmVersion, err = promptVMVersion(app, constants.SubnetEVMRepoName, vmVersion) + vmVersion, err = vm.PromptVMVersion(app, constants.SubnetEVMRepoName, vmVersion) if err != nil { return err } @@ -218,7 +218,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { useTeleporterFlag = &createFlags.useTeleporter } - params, tokenSymbol, err := promptSubnetEVMGenesisParams( + params, tokenSymbol, err := vm.PromptSubnetEVMGenesisParams( + app, vmVersion, createFlags.chainID, createFlags.tokenSymbol, @@ -230,7 +231,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } - if params.useTeleporter || params.useExternalGasToken { + if params.UseTeleporter || params.UseExternalGasToken { teleporterInfo, err = teleporter.GetInfo(app) if err != nil { return err diff --git a/cmd/subnetcmd/import_public.go b/cmd/subnetcmd/import_public.go index 55a298383..94c796cd8 100644 --- a/cmd/subnetcmd/import_public.go +++ b/cmd/subnetcmd/import_public.go @@ -135,7 +135,7 @@ func importPublic(*cobra.Command, []string) error { // TODO: it's probably possible to deploy VMs with the same name on a public network // In this case, an import could clash because the tool supports unique names only - vmType, err := promptVMType(useSubnetEvm, useCustomVM) + vmType, err := vm.PromptVMType(app, useSubnetEvm, useCustomVM) if err != nil { return err } diff --git a/cmd/subnetcmd/createPrompts.go b/pkg/vm/prompts.go similarity index 92% rename from cmd/subnetcmd/createPrompts.go rename to pkg/vm/prompts.go index e0c53e506..77c019517 100644 --- a/cmd/subnetcmd/createPrompts.go +++ b/pkg/vm/prompts.go @@ -1,6 +1,6 @@ // Copyright (C) 2022, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package subnetcmd +package vm import ( "fmt" @@ -12,14 +12,17 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" "github.com/ava-labs/avalanche-cli/pkg/ux" - "github.com/ava-labs/avalanche-cli/pkg/vm" "github.com/ava-labs/subnet-evm/plugin/evm" "github.com/ethereum/go-ethereum/common" "github.com/ava-labs/avalanchego/utils/logging" ) -const explainOption = "Explain the difference" +const ( + latest = "latest" + preRelease = "pre-release" + explainOption = "Explain the difference" +) type InitialTokenAllocation struct { allocToNewKey bool @@ -44,24 +47,28 @@ type FeeConfig struct { type SubnetEVMGenesisParams struct { chainID uint64 - useTeleporter bool - useExternalGasToken bool + UseTeleporter bool + UseExternalGasToken bool initialTokenAllocation InitialTokenAllocation feeConfig FeeConfig enableNativeMinterPrecompile bool - nativeMinterPrecompileAllowList vm.AllowList + nativeMinterPrecompileAllowList AllowList enableFeeManagerPrecompile bool - feeManagerPrecompileAllowList vm.AllowList + feeManagerPrecompileAllowList AllowList enableRewardManagerPrecompile bool - rewardManagerPrecompileAllowList vm.AllowList + rewardManagerPrecompileAllowList AllowList enableTransactionPrecompile bool - transactionPrecompileAllowList vm.AllowList + transactionPrecompileAllowList AllowList enableContractDeployerPrecompile bool - contractDeployerPrecompileAllowList vm.AllowList + contractDeployerPrecompileAllowList AllowList enableWarpPrecompile bool } -func promptVMType(useSubnetEvm bool, useCustom bool) (models.VMType, error) { +func PromptVMType( + app *application.Avalanche, + useSubnetEvm bool, + useCustom bool, +) (models.VMType, error) { if useSubnetEvm { return models.SubnetEvm, nil } @@ -113,7 +120,8 @@ func promptVMType(useSubnetEvm bool, useCustom bool) (models.VMType, error) { // // prompts the user for chainID, tokenSymbol, and useTeleporter, unless // provided in call args -func promptSubnetEVMGenesisParams( +func PromptSubnetEVMGenesisParams( + app *application.Avalanche, version string, chainID uint64, tokenSymbol string, @@ -134,27 +142,27 @@ func promptSubnetEVMGenesisParams( } } // Gas Token - params, tokenSymbol, err = promptGasToken(version, tokenSymbol, useDefaults, params) + params, tokenSymbol, err = promptGasToken(app, version, tokenSymbol, useDefaults, params) if err != nil { return SubnetEVMGenesisParams{}, "", err } // Transaction / Gas Fees - params, err = promptFeeConfig(version, useDefaults, params) + params, err = promptFeeConfig(app, version, useDefaults, params) if err != nil { return SubnetEVMGenesisParams{}, "", err } // Interoperability - params, err = promptInteropt(useTeleporter, useDefaults, params) + params, err = promptInteropt(app, useTeleporter, useDefaults, params) if err != nil { return SubnetEVMGenesisParams{}, "", err } // Warp params.enableWarpPrecompile = useWarp - if (params.useTeleporter || params.useExternalGasToken) && !params.enableWarpPrecompile { + if (params.UseTeleporter || params.UseExternalGasToken) && !params.enableWarpPrecompile { return SubnetEVMGenesisParams{}, "", fmt.Errorf("warp should be enabled for teleporter to work") } // Permissioning - params, err = promptPermissioning(version, useDefaults, params) + params, err = promptPermissioning(app, version, useDefaults, params) if err != nil { return SubnetEVMGenesisParams{}, "", err } @@ -171,6 +179,7 @@ func promptSubnetEVMGenesisParams( // - use native gas token, allocating 1m to a newly created key // - disable native minter precompile func promptGasToken( + app *application.Avalanche, version string, tokenSymbol string, useDefaults bool, @@ -203,7 +212,7 @@ func promptGasToken( } switch option { case externalTokenOption: - params.useExternalGasToken = true + params.UseExternalGasToken = true case nativeTokenOption: if tokenSymbol == "" { tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") @@ -251,7 +260,7 @@ func promptGasToken( switch option { case fixedSupplyOption: case dynamicSupplyOption: - params.nativeMinterPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "mint native tokens", version) + params.nativeMinterPrecompileAllowList, cancel, err = GenerateAllowList(app, "mint native tokens", version) if err != nil { return SubnetEVMGenesisParams{}, "", err } @@ -287,6 +296,7 @@ func promptGasToken( // - disable fee manager precompile // - disable reward manager precompile func promptFeeConfig( + app *application.Avalanche, version string, useDefaults bool, params SubnetEVMGenesisParams, @@ -383,7 +393,7 @@ func promptFeeConfig( switch option { case dontChangeFeeSettingsOption: case changeFeeSettingsOption: - params.feeManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "adjust the gas fees", version) + params.feeManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, "adjust the gas fees", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -411,7 +421,7 @@ func promptFeeConfig( switch option { case burnFees: case distributeFees: - params.rewardManagerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "customize gas fees distribution", version) + params.rewardManagerPrecompileAllowList, cancel, err = GenerateAllowList(app, "customize gas fees distribution", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -433,16 +443,17 @@ func promptFeeConfig( // if using external gas token, will assume teleporter to be enabled // if other cases, prompts the user for wether to enable teleporter func promptInteropt( + app *application.Avalanche, useTeleporter *bool, useDefaults bool, params SubnetEVMGenesisParams, ) (SubnetEVMGenesisParams, error) { switch { case useTeleporter != nil: - params.useTeleporter = *useTeleporter + params.UseTeleporter = *useTeleporter case useDefaults: - params.useTeleporter = true - case params.useExternalGasToken: + params.UseTeleporter = true + case params.UseExternalGasToken: default: interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" isolatedBlockchainOption := "No, I want to run my blockchain isolated" @@ -458,7 +469,7 @@ func promptInteropt( switch option { case isolatedBlockchainOption: case interoperatingBlockchainOption: - params.useTeleporter = true + params.UseTeleporter = true case explainOption: ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") continue @@ -470,6 +481,7 @@ func promptInteropt( } func promptPermissioning( + app *application.Avalanche, version string, useDefaults bool, params SubnetEVMGenesisParams, @@ -504,7 +516,7 @@ func promptPermissioning( } switch option { case approvedCanSubmitTransactionsOption: - params.transactionPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "issue transactions", version) + params.transactionPrecompileAllowList, cancel, err = GenerateAllowList(app, "issue transactions", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -533,7 +545,7 @@ func promptPermissioning( } switch option { case approvedCanDeployContractsOption: - params.contractDeployerPrecompileAllowList, cancel, err = vm.GenerateAllowList(app, "deploy smart contracts", version) + params.contractDeployerPrecompileAllowList, cancel, err = GenerateAllowList(app, "deploy smart contracts", version) if err != nil { return SubnetEVMGenesisParams{}, err } @@ -558,7 +570,7 @@ func promptPermissioning( return params, nil } -func promptVMVersion( +func PromptVMVersion( app *application.Avalanche, repoName string, vmVersion string, From 9b9da4b72f4dbd5d001fd8e44e0825cb7496e69e Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 11 Jul 2024 13:28:09 -0300 Subject: [PATCH 42/63] partial work --- pkg/vm/createEvm.go | 186 ++++++++------------------- pkg/vm/{prompts.go => evmPrompts.go} | 0 2 files changed, 57 insertions(+), 129 deletions(-) rename pkg/vm/{prompts.go => evmPrompts.go} (100%) diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 723f7cc68..23ef10fc2 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" "math/big" - "os" "time" "github.com/ava-labs/avalanche-cli/pkg/application" @@ -18,170 +17,111 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" - "github.com/ava-labs/subnet-evm/params" + subnetevmparams "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" ) -func CreateEvmSubnetConfig( +func CreateEvmSidecar( app *application.Avalanche, subnetName string, - genesisPath string, subnetEVMVersion string, + tokenSymbol string, getRPCVersionFromBinary bool, - subnetEVMChainID uint64, - subnetEVMTokenSymbol string, - useSubnetEVMDefaults bool, - useWarp bool, - teleporterInfo *teleporter.Info, -) ([]byte, *models.Sidecar, error) { +) (*models.Sidecar, error) { var ( - genesisBytes []byte - sc *models.Sidecar - err error - rpcVersion int + err error + rpcVersion int ) if getRPCVersionFromBinary { _, vmBin, err := binutils.SetupSubnetEVM(app, subnetEVMVersion) if err != nil { - return nil, &models.Sidecar{}, fmt.Errorf("failed to install subnet-evm: %w", err) + return &models.Sidecar{}, fmt.Errorf("failed to install subnet-evm: %w", err) } rpcVersion, err = GetVMBinaryProtocolVersion(vmBin) if err != nil { - return nil, &models.Sidecar{}, fmt.Errorf("unable to get RPC version: %w", err) + return &models.Sidecar{}, fmt.Errorf("unable to get RPC version: %w", err) } } else { rpcVersion, err = GetRPCProtocolVersion(app, models.SubnetEvm, subnetEVMVersion) if err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } } - if genesisPath == "" { - genesisBytes, sc, err = createEvmGenesis( - app, - subnetName, - subnetEVMVersion, - rpcVersion, - subnetEVMChainID, - subnetEVMTokenSymbol, - useSubnetEVMDefaults, - useWarp, - teleporterInfo, - ) - if err != nil { - return nil, &models.Sidecar{}, err - } - } else { - ux.Logger.PrintToUser("importing genesis for subnet %s", subnetName) - genesisBytes, err = os.ReadFile(genesisPath) - if err != nil { - return nil, &models.Sidecar{}, err - } - - sc = &models.Sidecar{ - Name: subnetName, - VM: models.SubnetEvm, - VMVersion: subnetEVMVersion, - RPCVersion: rpcVersion, - Subnet: subnetName, - } + sc := &models.Sidecar{ + Name: subnetName, + VM: models.SubnetEvm, + VMVersion: subnetEVMVersion, + RPCVersion: rpcVersion, + Subnet: subnetName, + TokenSymbol: tokenSymbol, + TokenName: tokenSymbol + " Token", } - return genesisBytes, sc, nil + return sc, nil } -func createEvmGenesis( +func CreateEvmGenesis( app *application.Avalanche, subnetName string, + params SubnetEVMGenesisParams, subnetEVMVersion string, - rpcVersion int, subnetEVMChainID uint64, - subnetEVMTokenSymbol string, + tokenSymbol string, useSubnetEVMDefaults bool, useWarp bool, teleporterInfo *teleporter.Info, -) ([]byte, *models.Sidecar, error) { +) ([]byte, error) { ux.Logger.PrintToUser("creating genesis for subnet %s", subnetName) genesis := core.Genesis{} genesis.Timestamp = *utils.TimeToNewUint64(time.Now()) + conf := subnetevmparams.SubnetEVMDefaultChainConfig + conf.NetworkUpgrades = subnetevmparams.NetworkUpgrades{} - conf := params.SubnetEVMDefaultChainConfig - conf.NetworkUpgrades = params.NetworkUpgrades{} - - const ( - descriptorsState = "descriptors" - feeState = "fee" - airdropState = "airdrop" - precompilesState = "precompiles" - ) + chainID := new(big.Int).SetUint64(params.chainID) + conf.ChainID = chainID var ( - chainID *big.Int - tokenSymbol string - allocation core.GenesisAlloc - direction statemachine.StateDirection - err error + allocation core.GenesisAlloc + direction statemachine.StateDirection + err error ) - subnetEvmState, err := statemachine.NewStateMachine( - []string{descriptorsState, feeState, airdropState, precompilesState}, + *conf, direction, err = GetFeeConfig(*conf, app, useSubnetEVMDefaults) + + allocation, direction, err = getAllocation( + app, + subnetName, + defaultEvmAirdropAmount, + oneAvax, + fmt.Sprintf("Amount to airdrop (in %s units)", tokenSymbol), + useSubnetEVMDefaults, ) - if err != nil { - return nil, nil, err + if teleporterInfo != nil { + allocation = addTeleporterAddressToAllocations( + allocation, + teleporterInfo.FundedAddress, + teleporterInfo.FundedBalance, + ) } - for subnetEvmState.Running() { - switch subnetEvmState.CurrentState() { - case descriptorsState: - chainID, tokenSymbol, direction, err = getDescriptors( - app, - subnetEVMChainID, - subnetEVMTokenSymbol, - ) - case feeState: - *conf, direction, err = GetFeeConfig(*conf, app, useSubnetEVMDefaults) - case airdropState: - allocation, direction, err = getAllocation( - app, - subnetName, - defaultEvmAirdropAmount, - oneAvax, - fmt.Sprintf("Amount to airdrop (in %s units)", tokenSymbol), - useSubnetEVMDefaults, - ) - if teleporterInfo != nil { - allocation = addTeleporterAddressToAllocations( - allocation, - teleporterInfo.FundedAddress, - teleporterInfo.FundedBalance, - ) - } - case precompilesState: - *conf, direction, err = getPrecompiles(*conf, app, &genesis.Timestamp, useSubnetEVMDefaults, useWarp, subnetEVMVersion) - if teleporterInfo != nil { - *conf = addTeleporterAddressesToAllowLists( - *conf, - teleporterInfo.FundedAddress, - teleporterInfo.MessengerDeployerAddress, - teleporterInfo.RelayerAddress, - ) - } - default: - err = errors.New("invalid creation stage") - } - if err != nil { - return nil, nil, err - } - subnetEvmState.NextState(direction) + *conf, direction, err = getPrecompiles(*conf, app, &genesis.Timestamp, useSubnetEVMDefaults, useWarp, subnetEVMVersion) + if teleporterInfo != nil { + *conf = addTeleporterAddressesToAllowLists( + *conf, + teleporterInfo.FundedAddress, + teleporterInfo.MessengerDeployerAddress, + teleporterInfo.RelayerAddress, + ) } if conf != nil && conf.GenesisPrecompiles[txallowlist.ConfigKey] != nil { allowListCfg, ok := conf.GenesisPrecompiles[txallowlist.ConfigKey].(*txallowlist.Config) if !ok { - return nil, nil, fmt.Errorf( + return nil, fmt.Errorf( "expected config of type txallowlist.AllowListConfig, but got %T", allowListCfg, ) @@ -190,12 +130,10 @@ func createEvmGenesis( if err := ensureAdminsHaveBalance( allowListCfg.AdminAddresses, allocation); err != nil { - return nil, nil, err + return nil, err } } - conf.ChainID = chainID - genesis.Alloc = allocation genesis.Config = conf genesis.Difficulty = Difficulty @@ -203,26 +141,16 @@ func createEvmGenesis( jsonBytes, err := genesis.MarshalJSON() if err != nil { - return nil, nil, err + return nil, err } var prettyJSON bytes.Buffer err = json.Indent(&prettyJSON, jsonBytes, "", " ") if err != nil { - return nil, nil, err - } - - sc := &models.Sidecar{ - Name: subnetName, - VM: models.SubnetEvm, - VMVersion: subnetEVMVersion, - RPCVersion: rpcVersion, - Subnet: subnetName, - TokenSymbol: tokenSymbol, - TokenName: tokenSymbol + " Token", + return nil, err } - return prettyJSON.Bytes(), sc, nil + return prettyJSON.Bytes(), nil } func ensureAdminsHaveBalance(admins []common.Address, alloc core.GenesisAlloc) error { diff --git a/pkg/vm/prompts.go b/pkg/vm/evmPrompts.go similarity index 100% rename from pkg/vm/prompts.go rename to pkg/vm/evmPrompts.go From a0792c258dc146ca9aa86bf61e9f948885801441 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 11 Jul 2024 22:05:35 -0300 Subject: [PATCH 43/63] added fee config --- pkg/vm/createEvm.go | 37 ++++++------ pkg/vm/evmSettings.go | 4 +- pkg/vm/fees.go | 131 +++++++----------------------------------- 3 files changed, 43 insertions(+), 129 deletions(-) diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 23ef10fc2..2c0ec7322 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/application" "github.com/ava-labs/avalanche-cli/pkg/binutils" "github.com/ava-labs/avalanche-cli/pkg/models" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" "github.com/ava-labs/avalanche-cli/pkg/teleporter" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" @@ -38,20 +37,20 @@ func CreateEvmSidecar( if getRPCVersionFromBinary { _, vmBin, err := binutils.SetupSubnetEVM(app, subnetEVMVersion) if err != nil { - return &models.Sidecar{}, fmt.Errorf("failed to install subnet-evm: %w", err) + return nil, fmt.Errorf("failed to install subnet-evm: %w", err) } rpcVersion, err = GetVMBinaryProtocolVersion(vmBin) if err != nil { - return &models.Sidecar{}, fmt.Errorf("unable to get RPC version: %w", err) + return nil, fmt.Errorf("unable to get RPC version: %w", err) } } else { rpcVersion, err = GetRPCProtocolVersion(app, models.SubnetEvm, subnetEVMVersion) if err != nil { - return &models.Sidecar{}, err + return nil, err } } - sc := &models.Sidecar{ + sc := models.Sidecar{ Name: subnetName, VM: models.SubnetEvm, VMVersion: subnetEVMVersion, @@ -61,7 +60,7 @@ func CreateEvmSidecar( TokenName: tokenSymbol + " Token", } - return sc, nil + return &sc, nil } func CreateEvmGenesis( @@ -69,7 +68,6 @@ func CreateEvmGenesis( subnetName string, params SubnetEVMGenesisParams, subnetEVMVersion string, - subnetEVMChainID uint64, tokenSymbol string, useSubnetEVMDefaults bool, useWarp bool, @@ -85,15 +83,9 @@ func CreateEvmGenesis( chainID := new(big.Int).SetUint64(params.chainID) conf.ChainID = chainID - var ( - allocation core.GenesisAlloc - direction statemachine.StateDirection - err error - ) - - *conf, direction, err = GetFeeConfig(*conf, app, useSubnetEVMDefaults) + setFeeConfig(params, conf) - allocation, direction, err = getAllocation( + allocation, _, err := getAllocation( app, subnetName, defaultEvmAirdropAmount, @@ -101,6 +93,10 @@ func CreateEvmGenesis( fmt.Sprintf("Amount to airdrop (in %s units)", tokenSymbol), useSubnetEVMDefaults, ) + if err != nil { + return nil, err + } + if teleporterInfo != nil { allocation = addTeleporterAddressToAllocations( allocation, @@ -108,7 +104,12 @@ func CreateEvmGenesis( teleporterInfo.FundedBalance, ) } - *conf, direction, err = getPrecompiles(*conf, app, &genesis.Timestamp, useSubnetEVMDefaults, useWarp, subnetEVMVersion) + + *conf, _, err = getPrecompiles(*conf, app, &genesis.Timestamp, useSubnetEVMDefaults, useWarp, subnetEVMVersion) + if err != nil { + return nil, err + } + if teleporterInfo != nil { *conf = addTeleporterAddressesToAllowLists( *conf, @@ -126,10 +127,10 @@ func CreateEvmGenesis( allowListCfg, ) } - if err := ensureAdminsHaveBalance( allowListCfg.AdminAddresses, - allocation); err != nil { + allocation, + ); err != nil { return nil, err } } diff --git a/pkg/vm/evmSettings.go b/pkg/vm/evmSettings.go index ee78ba72f..f3bf16477 100644 --- a/pkg/vm/evmSettings.go +++ b/pkg/vm/evmSettings.go @@ -18,9 +18,9 @@ const ( var ( Difficulty = big.NewInt(0) - slowTarget = big.NewInt(15_000_000) + lowTarget = big.NewInt(15_000_000) mediumTarget = big.NewInt(20_000_000) - fastTarget = big.NewInt(50_000_000) + highTarget = big.NewInt(50_000_000) // This is the current c-chain gas config StarterFeeConfig = commontype.FeeConfig{ diff --git a/pkg/vm/fees.go b/pkg/vm/fees.go index b1b54ba1c..839ec9056 100644 --- a/pkg/vm/fees.go +++ b/pkg/vm/fees.go @@ -4,127 +4,40 @@ package vm import ( - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" - "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/params" ) -func GetFeeConfig(config params.ChainConfig, app *application.Avalanche, useDefault bool) ( - params.ChainConfig, - statemachine.StateDirection, - error, +func setFeeConfig( + params SubnetEVMGenesisParams, + config *params.ChainConfig, ) { - const ( - useFast = "High disk use / High Throughput 5 mil gas/s" - useMedium = "Medium disk use / Medium Throughput 2 mil gas/s" - useSlow = "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" - customFee = "Customize fee config" - ) - config.FeeConfig = StarterFeeConfig - if useDefault { - config.FeeConfig.TargetGas = slowTarget - return config, statemachine.Forward, nil - } - - feeConfigOptions := []string{useSlow, useMedium, useFast, customFee, goBackMsg} - - feeDefault, err := app.Prompt.CaptureList( - "How would you like to set fees", - feeConfigOptions, - ) - if err != nil { - return config, statemachine.Stop, err - } - - switch feeDefault { - case useFast: - config.FeeConfig.TargetGas = fastTarget - return config, statemachine.Forward, nil - case useMedium: + switch { + case params.feeConfig.highThroughput: + config.FeeConfig.TargetGas = highTarget + case params.feeConfig.mediumThroughput: config.FeeConfig.TargetGas = mediumTarget - return config, statemachine.Forward, nil - case useSlow: - config.FeeConfig.TargetGas = slowTarget - return config, statemachine.Forward, nil - case goBackMsg: - return config, statemachine.Backward, nil + case params.feeConfig.lowThroughput: + config.FeeConfig.TargetGas = lowTarget default: - ux.Logger.PrintToUser("Customizing fee config") + setCustomFeeConfig(params, config) } - return CustomizeFeeConfig(config, app) } -func CustomizeFeeConfig(config params.ChainConfig, app *application.Avalanche) ( - params.ChainConfig, - statemachine.StateDirection, - error, +func setCustomFeeConfig( + params SubnetEVMGenesisParams, + config *params.ChainConfig, ) { - const ( - setGasLimit = "Set gas limit" - setBlockRate = "Set target block rate" - setMinBaseFee = "Set min base fee" - setTargetGas = "Set target gas" - setBaseFeeChangeDenominator = "Set base fee change denominator" - setMinBlockGas = "Set min block gas cost" - setMaxBlockGas = "Set max block gas cost" - setGasStep = "Set block gas cost step" - ) - gasLimit, err := app.Prompt.CapturePositiveBigInt(setGasLimit) - if err != nil { - return config, statemachine.Stop, err - } - - blockRate, err := app.Prompt.CapturePositiveBigInt(setBlockRate) - if err != nil { - return config, statemachine.Stop, err - } - - minBaseFee, err := app.Prompt.CapturePositiveBigInt(setMinBaseFee) - if err != nil { - return config, statemachine.Stop, err - } - - targetGas, err := app.Prompt.CapturePositiveBigInt(setTargetGas) - if err != nil { - return config, statemachine.Stop, err + config.FeeConfig = commontype.FeeConfig{ + GasLimit: params.feeConfig.gasLimit, + TargetBlockRate: params.feeConfig.blockRate.Uint64(), + MinBaseFee: params.feeConfig.minBaseFee, + TargetGas: params.feeConfig.targetGas, + BaseFeeChangeDenominator: params.feeConfig.baseDenominator, + MinBlockGasCost: params.feeConfig.minBlockGas, + MaxBlockGasCost: params.feeConfig.maxBlockGas, + BlockGasCostStep: params.feeConfig.gasStep, } - - baseDenominator, err := app.Prompt.CapturePositiveBigInt(setBaseFeeChangeDenominator) - if err != nil { - return config, statemachine.Stop, err - } - - minBlockGas, err := app.Prompt.CapturePositiveBigInt(setMinBlockGas) - if err != nil { - return config, statemachine.Stop, err - } - - maxBlockGas, err := app.Prompt.CapturePositiveBigInt(setMaxBlockGas) - if err != nil { - return config, statemachine.Stop, err - } - - gasStep, err := app.Prompt.CapturePositiveBigInt(setGasStep) - if err != nil { - return config, statemachine.Stop, err - } - - feeConf := commontype.FeeConfig{ - GasLimit: gasLimit, - TargetBlockRate: blockRate.Uint64(), - MinBaseFee: minBaseFee, - TargetGas: targetGas, - BaseFeeChangeDenominator: baseDenominator, - MinBlockGasCost: minBlockGas, - MaxBlockGasCost: maxBlockGas, - BlockGasCostStep: gasStep, - } - - config.FeeConfig = feeConf - - return config, statemachine.Forward, nil } From e6f3a9abf68e2b3916eaefe59a5205d4eed777f0 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 11 Jul 2024 22:48:34 -0300 Subject: [PATCH 44/63] added allocations and teleporter stuff --- pkg/teleporter/teleporter.go | 6 +- pkg/vm/airdrop.go | 139 ----------------------------------- pkg/vm/allocations.go | 87 ++++++++++++++++++++++ pkg/vm/createEvm.go | 40 +++++++--- pkg/vm/precompiles.go | 25 +++---- 5 files changed, 129 insertions(+), 168 deletions(-) delete mode 100644 pkg/vm/airdrop.go create mode 100644 pkg/vm/allocations.go diff --git a/pkg/teleporter/teleporter.go b/pkg/teleporter/teleporter.go index 72b30cbd8..c034c7313 100644 --- a/pkg/teleporter/teleporter.go +++ b/pkg/teleporter/teleporter.go @@ -29,12 +29,10 @@ const ( ) var ( - messengerDeployerRequiredBalance = big.NewInt(0). - Mul(big.NewInt(1e18), big.NewInt(10)) // 10 AVAX - TeleporterPrefundedAddressBalance = big.NewInt(0). - Mul(big.NewInt(1e18), big.NewInt(600)) + messengerDeployerRequiredBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) // 600 AVAX + TeleporterPrefundedAddressBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600)) ) func getTeleporterURLs(version string) (string, string, string, string) { diff --git a/pkg/vm/airdrop.go b/pkg/vm/airdrop.go deleted file mode 100644 index 2b1647bc7..000000000 --- a/pkg/vm/airdrop.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package vm - -import ( - "errors" - "math/big" - - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/models" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" - "github.com/ava-labs/avalanche-cli/pkg/utils" - "github.com/ava-labs/avalanche-cli/pkg/ux" - "github.com/ava-labs/subnet-evm/core" - "github.com/ethereum/go-ethereum/common" -) - -const ( - newAirdrop = "Airdrop 1 million tokens to a new address (stored key)" - ewoqAirdrop = "Airdrop 1 million tokens to the default ewoq address (do not use in production)" - customAirdrop = "Customize your airdrop" - extendAirdrop = "Would you like to airdrop more tokens?" -) - -func addAllocation(alloc core.GenesisAlloc, address string, amount *big.Int) { - alloc[common.HexToAddress(address)] = core.GenesisAccount{ - Balance: amount, - } -} - -func getNewAllocation(app *application.Avalanche, subnetName string, defaultAirdropAmount string) (core.GenesisAlloc, error) { - keyName := utils.GetDefaultSubnetAirdropKeyName(subnetName) - k, err := app.GetKey(keyName, models.NewLocalNetwork(), true) - if err != nil { - return core.GenesisAlloc{}, err - } - ux.Logger.PrintToUser("prefunding address %s with balance %s", k.C(), defaultAirdropAmount) - allocation := core.GenesisAlloc{} - defaultAmount, ok := new(big.Int).SetString(defaultAirdropAmount, 10) - if !ok { - return allocation, errors.New("unable to decode default allocation") - } - addAllocation(allocation, k.C(), defaultAmount) - return allocation, nil -} - -func getEwoqAllocation(defaultAirdropAmount string) (core.GenesisAlloc, error) { - allocation := core.GenesisAlloc{} - defaultAmount, ok := new(big.Int).SetString(defaultAirdropAmount, 10) - if !ok { - return allocation, errors.New("unable to decode default allocation") - } - - ux.Logger.PrintToUser("prefunding address %s with balance %s", PrefundedEwoqAddress, defaultAirdropAmount) - addAllocation(allocation, PrefundedEwoqAddress.String(), defaultAmount) - return allocation, nil -} - -func addTeleporterAddressToAllocations( - alloc core.GenesisAlloc, - teleporterKeyAddress string, - teleporterKeyBalance *big.Int, -) core.GenesisAlloc { - if alloc != nil { - addAllocation(alloc, teleporterKeyAddress, teleporterKeyBalance) - } - return alloc -} - -func getAllocation( - app *application.Avalanche, - subnetName string, - defaultAirdropAmount string, - multiplier *big.Int, - captureAmountLabel string, - useDefaults bool, -) (core.GenesisAlloc, statemachine.StateDirection, error) { - if useDefaults { - alloc, err := getNewAllocation(app, subnetName, defaultAirdropAmount) - return alloc, statemachine.Forward, err - } - - allocation := core.GenesisAlloc{} - - airdropType, err := app.Prompt.CaptureList( - "How would you like to distribute funds", - []string{newAirdrop, ewoqAirdrop, customAirdrop, goBackMsg}, - ) - if err != nil { - return allocation, statemachine.Stop, err - } - - if airdropType == newAirdrop { - alloc, err := getNewAllocation(app, subnetName, defaultAirdropAmount) - return alloc, statemachine.Forward, err - } - - if airdropType == ewoqAirdrop { - alloc, err := getEwoqAllocation(defaultAirdropAmount) - return alloc, statemachine.Forward, err - } - - if airdropType == goBackMsg { - return allocation, statemachine.Backward, nil - } - - var addressHex common.Address - - for { - addressHex, err = app.Prompt.CaptureAddress("Address to airdrop to") - if err != nil { - return nil, statemachine.Stop, err - } - - amount, err := app.Prompt.CapturePositiveBigInt(captureAmountLabel) - if err != nil { - return nil, statemachine.Stop, err - } - - amount = amount.Mul(amount, multiplier) - - account, ok := allocation[addressHex] - if !ok { - account.Balance = big.NewInt(0) - } - account.Balance.Add(account.Balance, amount) - - allocation[addressHex] = account - - continueAirdrop, err := app.Prompt.CaptureNoYes(extendAirdrop) - if err != nil { - return nil, statemachine.Stop, err - } - if !continueAirdrop { - return allocation, statemachine.Forward, nil - } - } -} diff --git a/pkg/vm/allocations.go b/pkg/vm/allocations.go new file mode 100644 index 000000000..308edc158 --- /dev/null +++ b/pkg/vm/allocations.go @@ -0,0 +1,87 @@ +// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package vm + +import ( + "errors" + "math/big" + + "github.com/ava-labs/avalanche-cli/pkg/application" + "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/utils" + "github.com/ava-labs/avalanche-cli/pkg/ux" + "github.com/ava-labs/subnet-evm/core" + "github.com/ethereum/go-ethereum/common" +) + +func addAllocation(allocations core.GenesisAlloc, address string, amount *big.Int) { + allocations[common.HexToAddress(address)] = core.GenesisAccount{ + Balance: amount, + } +} + +func getNewAllocation( + app *application.Avalanche, + subnetName string, + defaultAirdropAmount string, +) (core.GenesisAlloc, error) { + keyName := utils.GetDefaultSubnetAirdropKeyName(subnetName) + k, err := app.GetKey(keyName, models.NewLocalNetwork(), true) + if err != nil { + return core.GenesisAlloc{}, err + } + ux.Logger.PrintToUser("prefunding address %s with balance %s", k.C(), defaultAirdropAmount) + allocations := core.GenesisAlloc{} + defaultAmount, ok := new(big.Int).SetString(defaultAirdropAmount, 10) + if !ok { + return allocations, errors.New("unable to decode default allocation") + } + addAllocation(allocations, k.C(), defaultAmount) + return allocations, nil +} + +func getEwoqAllocation(defaultAirdropAmount string) (core.GenesisAlloc, error) { + allocations := core.GenesisAlloc{} + defaultAmount, ok := new(big.Int).SetString(defaultAirdropAmount, 10) + if !ok { + return allocations, errors.New("unable to decode default allocation") + } + + ux.Logger.PrintToUser("prefunding address %s with balance %s", PrefundedEwoqAddress, defaultAirdropAmount) + addAllocation(allocations, PrefundedEwoqAddress.String(), defaultAmount) + return allocations, nil +} + +func addTeleporterAllocation( + allocations core.GenesisAlloc, + teleporterKeyAddress string, + teleporterKeyBalance *big.Int, +) core.GenesisAlloc { + if allocations != nil { + addAllocation(allocations, teleporterKeyAddress, teleporterKeyBalance) + } + return allocations +} + +func getAllocation( + params SubnetEVMGenesisParams, + app *application.Avalanche, + subnetName string, + defaultAirdropAmount string, + multiplier *big.Int, +) (core.GenesisAlloc, error) { + if params.initialTokenAllocation.allocToNewKey { + return getNewAllocation(app, subnetName, defaultAirdropAmount) + } + + if params.initialTokenAllocation.allocToEwoq { + return getEwoqAllocation(defaultAirdropAmount) + } + + allocations := core.GenesisAlloc{} + amount := new(big.Int).SetUint64(params.initialTokenAllocation.customBalance) + amount = amount.Mul(amount, multiplier) + addAllocation(allocations, params.initialTokenAllocation.customAddress.Hex(), amount) + return allocations, nil +} diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 2c0ec7322..ea04d5d40 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -22,6 +22,15 @@ import ( "github.com/ethereum/go-ethereum/common" ) +var ( + // 600 AVAX: to deploy teleporter contract, registry contract, and fund + // starting relayer operations + teleporterBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600)) + // 1000 AVAX: to deploy teleporter contract, registry contract, fund + // starting relayer operations, deploy bridge contracts + externalGasTokenBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) +) + func CreateEvmSidecar( app *application.Avalanche, subnetName string, @@ -85,23 +94,30 @@ func CreateEvmGenesis( setFeeConfig(params, conf) - allocation, _, err := getAllocation( + allocations, err := getAllocation( + params, app, subnetName, defaultEvmAirdropAmount, oneAvax, - fmt.Sprintf("Amount to airdrop (in %s units)", tokenSymbol), - useSubnetEVMDefaults, ) if err != nil { return nil, err } - if teleporterInfo != nil { - allocation = addTeleporterAddressToAllocations( - allocation, + if (params.UseTeleporter || params.UseExternalGasToken) && teleporterInfo == nil { + return nil, fmt.Errorf("teleporter info not provided but a teleporter ready blockchain was requested") + } + + if params.UseTeleporter || params.UseExternalGasToken { + balance := teleporterBalance + if params.UseExternalGasToken { + balance = externalGasTokenBalance + } + allocations = addTeleporterAllocation( + allocations, teleporterInfo.FundedAddress, - teleporterInfo.FundedBalance, + balance, ) } @@ -110,9 +126,9 @@ func CreateEvmGenesis( return nil, err } - if teleporterInfo != nil { - *conf = addTeleporterAddressesToAllowLists( - *conf, + if params.UseTeleporter || params.UseExternalGasToken { + addTeleporterAddressesToAllowLists( + conf, teleporterInfo.FundedAddress, teleporterInfo.MessengerDeployerAddress, teleporterInfo.RelayerAddress, @@ -129,13 +145,13 @@ func CreateEvmGenesis( } if err := ensureAdminsHaveBalance( allowListCfg.AdminAddresses, - allocation, + allocations, ); err != nil { return nil, err } } - genesis.Alloc = allocation + genesis.Alloc = allocations genesis.Config = conf genesis.Difficulty = Difficulty genesis.GasLimit = conf.FeeConfig.GasLimit.Uint64() diff --git a/pkg/vm/precompiles.go b/pkg/vm/precompiles.go index 62c42819c..1fcc9e684 100644 --- a/pkg/vm/precompiles.go +++ b/pkg/vm/precompiles.go @@ -211,19 +211,19 @@ func removePrecompile(arr []string, s string) ([]string, error) { // adds teleporter-related addresses (main funded key, messenger deploy key, relayer key) // to the allow list of relevant enabled precompiles func addTeleporterAddressesToAllowLists( - config params.ChainConfig, + config *params.ChainConfig, teleporterAddress string, teleporterMessengerDeployerAddress string, relayerAddress string, -) params.ChainConfig { +) { // tx allow list: // teleporterAddress funds the other two and also deploys the registry // teleporterMessengerDeployerAddress deploys the messenger // relayerAddress is used by the relayer to send txs to the target chain - for _, address := range []string{teleporterAddress, teleporterMessengerDeployerAddress, relayerAddress} { - precompileConfig := config.GenesisPrecompiles[txallowlist.ConfigKey] - if precompileConfig != nil { - txAllowListConfig := precompileConfig.(*txallowlist.Config) + precompileConfig := config.GenesisPrecompiles[txallowlist.ConfigKey] + if precompileConfig != nil { + txAllowListConfig := precompileConfig.(*txallowlist.Config) + for _, address := range []string{teleporterAddress, teleporterMessengerDeployerAddress, relayerAddress} { txAllowListConfig.AllowListConfig = addAddressToAllowed( txAllowListConfig.AllowListConfig, address, @@ -233,17 +233,16 @@ func addTeleporterAddressesToAllowLists( // contract deploy allow list: // teleporterAddress deploys the registry // teleporterMessengerDeployerAddress deploys the messenger - for _, address := range []string{teleporterAddress, teleporterMessengerDeployerAddress} { - precompileConfig := config.GenesisPrecompiles[deployerallowlist.ConfigKey] - if precompileConfig != nil { - txAllowListConfig := precompileConfig.(*deployerallowlist.Config) - txAllowListConfig.AllowListConfig = addAddressToAllowed( - txAllowListConfig.AllowListConfig, + precompileConfig = config.GenesisPrecompiles[deployerallowlist.ConfigKey] + if precompileConfig != nil { + deployerAllowListConfig := precompileConfig.(*deployerallowlist.Config) + for _, address := range []string{teleporterAddress, teleporterMessengerDeployerAddress} { + deployerAllowListConfig.AllowListConfig = addAddressToAllowed( + deployerAllowListConfig.AllowListConfig, address, ) } } - return config } // adds an address to the given allowlist, as an Allowed address, From d93bab606f664553a3641db97674bd2988ce09a4 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 10:59:56 -0300 Subject: [PATCH 45/63] added precompiles --- pkg/vm/createEvm.go | 17 ++- pkg/vm/precompiles.go | 339 +++++++----------------------------------- 2 files changed, 65 insertions(+), 291 deletions(-) diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index ea04d5d40..cdbbf6012 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -105,8 +105,12 @@ func CreateEvmGenesis( return nil, err } + if (params.UseTeleporter || params.UseExternalGasToken) && !params.enableWarpPrecompile { + return nil, fmt.Errorf("a teleporter enabled blockchain was requested but warp precompile is disabled") + } + if (params.UseTeleporter || params.UseExternalGasToken) && teleporterInfo == nil { - return nil, fmt.Errorf("teleporter info not provided but a teleporter ready blockchain was requested") + return nil, fmt.Errorf("a teleporter enabled blockchain was requested but no teleporter info was provided") } if params.UseTeleporter || params.UseExternalGasToken { @@ -121,11 +125,16 @@ func CreateEvmGenesis( ) } - *conf, _, err = getPrecompiles(*conf, app, &genesis.Timestamp, useSubnetEVMDefaults, useWarp, subnetEVMVersion) - if err != nil { - return nil, err + if params.UseExternalGasToken { + params.enableNativeMinterPrecompile = true + params.nativeMinterPrecompileAllowList.AdminAddresses = append( + params.nativeMinterPrecompileAllowList.AdminAddresses, + common.HexToAddress(teleporterInfo.FundedAddress), + ) } + getPrecompiles(conf, params, &genesis.Timestamp) + if params.UseTeleporter || params.UseExternalGasToken { addTeleporterAddressesToAllowLists( conf, diff --git a/pkg/vm/precompiles.go b/pkg/vm/precompiles.go index 1fcc9e684..ea317077c 100644 --- a/pkg/vm/precompiles.go +++ b/pkg/vm/precompiles.go @@ -4,13 +4,7 @@ package vm import ( - "errors" - - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/prompts" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" "github.com/ava-labs/avalanche-cli/pkg/utils" - "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/precompile/allowlist" "github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist" @@ -24,169 +18,79 @@ import ( "github.com/ethereum/go-ethereum/common" ) -type Precompile string - -const ( - NativeMint = "Native Minting" - ContractAllowList = "Contract Deployment Allow List" - TxAllowList = "Transaction Allow List" - FeeManager = "Adjust Fee Settings Post Deploy" - RewardManager = "Customize Fees Distribution" - Warp = "Warp" -) - -func configureContractAllowList( - app *application.Avalanche, - subnetEvmVersion string, -) (deployerallowlist.Config, bool, error) { +func configureContractDeployerAllowList( + params SubnetEVMGenesisParams, +) deployerallowlist.Config { config := deployerallowlist.Config{} - info := "\nThis precompile restricts who has the ability to deploy contracts " + - "on your subnet.\nFor more information visit " + //nolint:goconst - "https://docs.avax.network/subnets/customize-a-subnet/#restricting-smart-contract-deployers\n" - ux.Logger.PrintToUser(info) - allowList, cancelled, err := GenerateAllowList(app, "deploy smart contracts", subnetEvmVersion) - if cancelled || err != nil { - return config, cancelled, err - } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: allowList.AdminAddresses, - ManagerAddresses: allowList.ManagerAddresses, - EnabledAddresses: allowList.EnabledAddresses, + AdminAddresses: params.contractDeployerPrecompileAllowList.AdminAddresses, + ManagerAddresses: params.contractDeployerPrecompileAllowList.ManagerAddresses, + EnabledAddresses: params.contractDeployerPrecompileAllowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), } - return config, cancelled, nil + return config } func configureTransactionAllowList( - app *application.Avalanche, - subnetEvmVersion string, -) (txallowlist.Config, bool, error) { + params SubnetEVMGenesisParams, +) txallowlist.Config { config := txallowlist.Config{} - info := "\nThis precompile restricts who has the ability to issue transactions " + - "on your subnet.\nFor more information visit " + - "https://docs.avax.network/subnets/customize-a-subnet/#restricting-who-can-submit-transactions\n" - ux.Logger.PrintToUser(info) - allowList, cancelled, err := GenerateAllowList(app, "issue transactions", subnetEvmVersion) - if cancelled || err != nil { - return config, cancelled, err - } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: allowList.AdminAddresses, - ManagerAddresses: allowList.ManagerAddresses, - EnabledAddresses: allowList.EnabledAddresses, + AdminAddresses: params.transactionPrecompileAllowList.AdminAddresses, + ManagerAddresses: params.transactionPrecompileAllowList.ManagerAddresses, + EnabledAddresses: params.transactionPrecompileAllowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), } - return config, cancelled, nil + return config } -func configureMinterList( - app *application.Avalanche, - subnetEvmVersion string, -) (nativeminter.Config, bool, error) { +func configureNativeMinter( + params SubnetEVMGenesisParams, +) nativeminter.Config { config := nativeminter.Config{} - info := "\nThis precompile allows admins to permit designated contracts to mint the native token " + - "on your subnet.\nFor more information visit " + - "https://docs.avax.network/subnets/customize-a-subnet#minting-native-coins\n" - ux.Logger.PrintToUser(info) - allowList, cancelled, err := GenerateAllowList(app, "mint native tokens", subnetEvmVersion) - if cancelled || err != nil { - return config, cancelled, err - } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: allowList.AdminAddresses, - ManagerAddresses: allowList.ManagerAddresses, - EnabledAddresses: allowList.EnabledAddresses, + AdminAddresses: params.nativeMinterPrecompileAllowList.AdminAddresses, + ManagerAddresses: params.nativeMinterPrecompileAllowList.ManagerAddresses, + EnabledAddresses: params.nativeMinterPrecompileAllowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), } - return config, cancelled, nil + return config } -func configureFeeConfigAllowList( - app *application.Avalanche, - subnetEvmVersion string, -) (feemanager.Config, bool, error) { +func configureFeeManager( + params SubnetEVMGenesisParams, +) feemanager.Config { config := feemanager.Config{} - info := "\nThis precompile allows admins to adjust chain gas and fee parameters without " + - "performing a hardfork.\nFor more information visit " + - "https://docs.avax.network/subnets/customize-a-subnet#configuring-dynamic-fees\n" - ux.Logger.PrintToUser(info) - allowList, cancelled, err := GenerateAllowList(app, "adjust the gas fees", subnetEvmVersion) - if cancelled || err != nil { - return config, cancelled, err - } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: allowList.AdminAddresses, - ManagerAddresses: allowList.ManagerAddresses, - EnabledAddresses: allowList.EnabledAddresses, + AdminAddresses: params.feeManagerPrecompileAllowList.AdminAddresses, + ManagerAddresses: params.feeManagerPrecompileAllowList.ManagerAddresses, + EnabledAddresses: params.feeManagerPrecompileAllowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), } - return config, cancelled, nil + return config } func configureRewardManager( - app *application.Avalanche, - subnetEvmVersion string, -) (rewardmanager.Config, bool, error) { - info := "\nThis precompile allows to configure the fee reward mechanism " + - "on your subnet, including burning or sending fees.\nFor more information visit " + - "https://docs.avax.network/subnets/customize-a-subnet#changing-fee-reward-mechanisms\n" - ux.Logger.PrintToUser(info) + params SubnetEVMGenesisParams, +) rewardmanager.Config { config := rewardmanager.Config{} - allowList, cancelled, err := GenerateAllowList(app, "customize fee distribution", subnetEvmVersion) - if cancelled || err != nil { - return config, cancelled, err - } config.AllowListConfig = allowlist.AllowListConfig{ - AdminAddresses: allowList.AdminAddresses, - ManagerAddresses: allowList.ManagerAddresses, - EnabledAddresses: allowList.EnabledAddresses, + AdminAddresses: params.rewardManagerPrecompileAllowList.AdminAddresses, + ManagerAddresses: params.rewardManagerPrecompileAllowList.ManagerAddresses, + EnabledAddresses: params.rewardManagerPrecompileAllowList.EnabledAddresses, } config.Upgrade = precompileconfig.Upgrade{ BlockTimestamp: subnetevmutils.NewUint64(0), } - config.InitialRewardConfig, err = ConfigureInitialRewardConfig(app) - return config, cancelled, err -} - -func ConfigureInitialRewardConfig( - app *application.Avalanche, -) (*rewardmanager.InitialRewardConfig, error) { - config := &rewardmanager.InitialRewardConfig{} - - burnPrompt := "Should fees be burnt?" - burnFees, err := app.Prompt.CaptureYesNo(burnPrompt) - if err != nil { - return config, err - } - if burnFees { - return config, nil - } - - feeRcpdPrompt := "Allow block producers to claim fees?" - allowFeeRecipients, err := app.Prompt.CaptureYesNo(feeRcpdPrompt) - if err != nil { - return config, err - } - if allowFeeRecipients { - config.AllowFeeRecipients = true - return config, nil - } - - rewardPrompt := "Provide the address to which fees will be sent to" - rewardAddress, err := app.Prompt.CaptureAddress(rewardPrompt) - if err != nil { - return config, err - } - config.RewardAddress = rewardAddress - return config, nil + return config } func configureWarp(timestamp *uint64) warp.Config { @@ -199,15 +103,6 @@ func configureWarp(timestamp *uint64) warp.Config { return config } -func removePrecompile(arr []string, s string) ([]string, error) { - for i, val := range arr { - if val == s { - return append(arr[:i], arr[i+1:]...), nil - } - } - return arr, errors.New("string not in array") -} - // adds teleporter-related addresses (main funded key, messenger deploy key, relayer key) // to the allow list of relevant enabled precompiles func addTeleporterAddressesToAllowLists( @@ -281,164 +176,34 @@ func addAddressToAllowed( } func getPrecompiles( - config params.ChainConfig, - app *application.Avalanche, + config *params.ChainConfig, + params SubnetEVMGenesisParams, genesisTimestamp *uint64, - useDefaults bool, - useWarp bool, - subnetEvmVersion string, -) ( - params.ChainConfig, - statemachine.StateDirection, - error, ) { - if useDefaults || useWarp { + if params.enableWarpPrecompile { warpConfig := configureWarp(genesisTimestamp) config.GenesisPrecompiles[warp.ConfigKey] = &warpConfig } - if useDefaults { - return config, statemachine.Forward, nil + if params.enableNativeMinterPrecompile { + mintConfig := configureNativeMinter(params) + config.GenesisPrecompiles[nativeminter.ConfigKey] = &mintConfig } - const cancel = "Cancel" - - first := true - - remainingPrecompiles := []string{ - Warp, - NativeMint, - ContractAllowList, - TxAllowList, - FeeManager, - RewardManager, - cancel, + if params.enableContractDeployerPrecompile { + contractConfig := configureContractDeployerAllowList(params) + config.GenesisPrecompiles[deployerallowlist.ConfigKey] = &contractConfig } - if useWarp { - remainingPrecompiles = []string{ - NativeMint, - ContractAllowList, - TxAllowList, - FeeManager, - RewardManager, - cancel, - } + if params.enableTransactionPrecompile { + txConfig := configureTransactionAllowList(params) + config.GenesisPrecompiles[txallowlist.ConfigKey] = &txConfig } - - for { - firstStr := "Advanced: Would you like to add a custom precompile to modify the EVM?" - secondStr := "Would you like to add additional precompiles?" - - var promptStr string - if promptStr = secondStr; first { - promptStr = firstStr - first = false - } - - addPrecompile, err := app.Prompt.CaptureList( - promptStr, - []string{prompts.No, prompts.Yes, goBackMsg}, - ) - if err != nil { - return config, statemachine.Stop, err - } - - switch addPrecompile { - case prompts.No: - return config, statemachine.Forward, nil - case goBackMsg: - return config, statemachine.Backward, nil - } - - precompileDecision, err := app.Prompt.CaptureListWithSize( - "Choose precompile", - remainingPrecompiles, - len(remainingPrecompiles), - ) - if err != nil { - return config, statemachine.Stop, err - } - - switch precompileDecision { - case NativeMint: - mintConfig, cancelled, err := configureMinterList(app, subnetEvmVersion) - if err != nil { - return config, statemachine.Stop, err - } - if !cancelled { - config.GenesisPrecompiles[nativeminter.ConfigKey] = &mintConfig - remainingPrecompiles, err = removePrecompile(remainingPrecompiles, NativeMint) - if err != nil { - return config, statemachine.Stop, err - } - } - case ContractAllowList: - contractConfig, cancelled, err := configureContractAllowList(app, subnetEvmVersion) - if err != nil { - return config, statemachine.Stop, err - } - if !cancelled { - config.GenesisPrecompiles[deployerallowlist.ConfigKey] = &contractConfig - remainingPrecompiles, err = removePrecompile( - remainingPrecompiles, - ContractAllowList, - ) - if err != nil { - return config, statemachine.Stop, err - } - } - case TxAllowList: - txConfig, cancelled, err := configureTransactionAllowList(app, subnetEvmVersion) - if err != nil { - return config, statemachine.Stop, err - } - if !cancelled { - config.GenesisPrecompiles[txallowlist.ConfigKey] = &txConfig - remainingPrecompiles, err = removePrecompile(remainingPrecompiles, TxAllowList) - if err != nil { - return config, statemachine.Stop, err - } - } - case FeeManager: - feeConfig, cancelled, err := configureFeeConfigAllowList(app, subnetEvmVersion) - if err != nil { - return config, statemachine.Stop, err - } - if !cancelled { - config.GenesisPrecompiles[feemanager.ConfigKey] = &feeConfig - remainingPrecompiles, err = removePrecompile(remainingPrecompiles, FeeManager) - if err != nil { - return config, statemachine.Stop, err - } - } - case RewardManager: - rewardManagerConfig, cancelled, err := configureRewardManager(app, subnetEvmVersion) - if err != nil { - return config, statemachine.Stop, err - } - if !cancelled { - config.GenesisPrecompiles[rewardmanager.ConfigKey] = &rewardManagerConfig - remainingPrecompiles, err = removePrecompile(remainingPrecompiles, RewardManager) - if err != nil { - return config, statemachine.Stop, err - } - } - case Warp: - warpConfig := configureWarp(genesisTimestamp) - config.GenesisPrecompiles[warp.ConfigKey] = &warpConfig - remainingPrecompiles, err = removePrecompile(remainingPrecompiles, Warp) - if err != nil { - return config, statemachine.Stop, err - } - - case cancel: - return config, statemachine.Forward, nil - } - - // When all precompiles have been added, the len of remainingPrecompiles will be 1 - // (the cancel option stays in the list). Safe to return. - if len(remainingPrecompiles) == 1 { - return config, statemachine.Forward, nil - } + if params.enableFeeManagerPrecompile { + feeConfig := configureFeeManager(params) + config.GenesisPrecompiles[feemanager.ConfigKey] = &feeConfig + } + if params.enableRewardManagerPrecompile { + rewardManagerConfig := configureRewardManager(params) + config.GenesisPrecompiles[rewardmanager.ConfigKey] = &rewardManagerConfig } } From 2c9f9bd884c01cfab0467e09d17aff2e1a467a5a Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 11:22:47 -0300 Subject: [PATCH 46/63] createEvm is ready --- pkg/vm/createEvm.go | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index cdbbf6012..5e58504c4 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -17,7 +17,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/subnet-evm/core" subnetevmparams "github.com/ava-labs/subnet-evm/params" - "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ava-labs/subnet-evm/utils" "github.com/ethereum/go-ethereum/common" ) @@ -27,7 +26,7 @@ var ( // starting relayer operations teleporterBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600)) // 1000 AVAX: to deploy teleporter contract, registry contract, fund - // starting relayer operations, deploy bridge contracts + // starting relayer operations, and deploy bridge contracts externalGasTokenBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(1000)) ) @@ -105,6 +104,12 @@ func CreateEvmGenesis( return nil, err } + if params.enableTransactionPrecompile { + if !someAllowedHasBalance(params.transactionPrecompileAllowList, allocations) { + return nil, errors.New("none of the addresses in the transaction allow list precompile have any tokens allocated to them. Currently, no address can transact on the network. Allocate some funds to one of the allow list addresses to continue") + } + } + if (params.UseTeleporter || params.UseExternalGasToken) && !params.enableWarpPrecompile { return nil, fmt.Errorf("a teleporter enabled blockchain was requested but warp precompile is disabled") } @@ -144,22 +149,6 @@ func CreateEvmGenesis( ) } - if conf != nil && conf.GenesisPrecompiles[txallowlist.ConfigKey] != nil { - allowListCfg, ok := conf.GenesisPrecompiles[txallowlist.ConfigKey].(*txallowlist.Config) - if !ok { - return nil, fmt.Errorf( - "expected config of type txallowlist.AllowListConfig, but got %T", - allowListCfg, - ) - } - if err := ensureAdminsHaveBalance( - allowListCfg.AdminAddresses, - allocations, - ); err != nil { - return nil, err - } - } - genesis.Alloc = allocations genesis.Config = conf genesis.Difficulty = Difficulty @@ -179,20 +168,15 @@ func CreateEvmGenesis( return prettyJSON.Bytes(), nil } -func ensureAdminsHaveBalance(admins []common.Address, alloc core.GenesisAlloc) error { - if len(admins) < 1 { - return nil - } - - for _, admin := range admins { - // we can break at the first admin who has a non-zero balance - if bal, ok := alloc[admin]; ok && +func someAllowedHasBalance(allowList AllowList, allocations core.GenesisAlloc) bool { + addrs := append(append(allowList.AdminAddresses, allowList.ManagerAddresses...), allowList.EnabledAddresses...) + for _, addr := range addrs { + // we can break at the first address that has a non-zero balance + if bal, ok := allocations[addr]; ok && bal.Balance != nil && bal.Balance.Uint64() > uint64(0) { - return nil + return true } } - return errors.New( - "none of the addresses in the transaction allow list precompile have any tokens allocated to them. Currently, no address can transact on the network. Airdrop some funds to one of the allow list addresses to continue", - ) + return false } From bbe0aa4220f7fa4c21596caefb0ee746717e5621 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 12:05:31 -0300 Subject: [PATCH 47/63] start making sense --- cmd/subnetcmd/create.go | 29 +++-- cmd/subnetcmd/upgradecmd/generate.go | 167 +++++++++++++++++++++++++-- pkg/models/sidecar.go | 1 + pkg/vm/createEvm.go | 3 - pkg/vm/evmPrompts.go | 26 +++-- pkg/vm/evmSettings.go | 6 +- pkg/vm/fees.go | 6 +- 7 files changed, 199 insertions(+), 39 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 627ab9423..6f94971fb 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -207,11 +207,22 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } else if !evmCompatibleGenesis { return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") } + tokenSymbol, err := vm.PromptTokenSymbol(app, createFlags.tokenSymbol) + if err != nil { + return err + } ux.Logger.PrintToUser("importing genesis for subnet %s", subnetName) genesisBytes, err = os.ReadFile(genesisFile) if err != nil { return err } + sc, err = vm.CreateEvmSidecar( + app, + subnetName, + vmVersion, + tokenSymbol, + true, + ) } else { var useTeleporterFlag *bool flagName := "teleporter" @@ -238,21 +249,24 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } } - genesisBytes, sc, err = vm.CreateEvmSubnetConfig( + genesisBytes, err = vm.CreateEvmGenesis( app, subnetName, - genesisFile, - vmVersion, - true, - createFlags.chainID, + params, tokenSymbol, - createFlags.useDefaults, - createFlags.useWarp, teleporterInfo, ) if err != nil { return err } + + sc, err = vm.CreateEvmSidecar( + app, + subnetName, + vmVersion, + tokenSymbol, + true, + ) } } else { genesisBytes, sc, err = vm.CreateCustomSubnetConfig( @@ -291,7 +305,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } - sc.ImportedFromAPM = false if err = app.CreateSidecar(sc); err != nil { return err } diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index 3bba9cc2b..bc2010f75 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -43,6 +43,12 @@ const ( enabledLabel = "enabled" managerLabel = "manager" adminLabel = "admin" + + NativeMint = "Native Minting" + ContractAllowList = "Contract Deployment Allow List" + TxAllowList = "Transaction Allow List" + FeeManager = "Adjust Fee Settings Post Deploy" + RewardManager = "Customize Fees Distribution" ) var subnetName string @@ -92,11 +98,11 @@ func upgradeGenerateCmd(_ *cobra.Command, args []string) error { } allPreComps := []string{ - vm.ContractAllowList, - vm.FeeManager, - vm.NativeMint, - vm.TxAllowList, - vm.RewardManager, + ContractAllowList, + FeeManager, + NativeMint, + TxAllowList, + RewardManager, } fmt.Println() @@ -197,15 +203,15 @@ func promptParams(precomp string, precompiles *[]params.PrecompileUpgrade) (bool return false, err } switch precomp { - case vm.ContractAllowList: + case ContractAllowList: return promptContractAllowListParams(&sc, precompiles, date) - case vm.TxAllowList: + case TxAllowList: return promptTxAllowListParams(&sc, precompiles, date) - case vm.NativeMint: + case NativeMint: return promptNativeMintParams(&sc, precompiles, date) - case vm.FeeManager: + case FeeManager: return promptFeeManagerParams(&sc, precompiles, date) - case vm.RewardManager: + case RewardManager: return promptRewardManagerParams(&sc, precompiles, date) default: return false, fmt.Errorf("unexpected precompile identifier: %q", precomp) @@ -277,7 +283,7 @@ func promptRewardManagerParams( if cancelled || err != nil { return cancelled, err } - initialConfig, err := vm.ConfigureInitialRewardConfig(app) + initialConfig, err := ConfigureInitialRewardConfig() if err != nil { return false, err } @@ -295,6 +301,37 @@ func promptRewardManagerParams( return false, nil } +func ConfigureInitialRewardConfig() (*rewardmanager.InitialRewardConfig, error) { + config := &rewardmanager.InitialRewardConfig{} + + burnPrompt := "Should fees be burnt?" + burnFees, err := app.Prompt.CaptureYesNo(burnPrompt) + if err != nil { + return config, err + } + if burnFees { + return config, nil + } + + feeRcpdPrompt := "Allow block producers to claim fees?" + allowFeeRecipients, err := app.Prompt.CaptureYesNo(feeRcpdPrompt) + if err != nil { + return config, err + } + if allowFeeRecipients { + config.AllowFeeRecipients = true + return config, nil + } + + rewardPrompt := "Provide the address to which fees will be sent to" + rewardAddress, err := app.Prompt.CaptureAddress(rewardPrompt) + if err != nil { + return config, err + } + config.RewardAddress = rewardAddress + return config, nil +} + func promptFeeManagerParams( sc *models.Sidecar, precompiles *[]params.PrecompileUpgrade, @@ -311,7 +348,7 @@ func promptFeeManagerParams( } var feeConfig *commontype.FeeConfig if yes { - chainConfig, _, err := vm.GetFeeConfig(params.ChainConfig{}, app, false) + chainConfig, err := GetFeeConfig(params.ChainConfig{}, false) if err != nil { return false, err } @@ -331,6 +368,112 @@ func promptFeeManagerParams( return false, nil } +func GetFeeConfig(config params.ChainConfig, useDefault bool) ( + params.ChainConfig, + error, +) { + const ( + useFast = "High disk use / High Throughput 5 mil gas/s" + useMedium = "Medium disk use / Medium Throughput 2 mil gas/s" + useSlow = "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" + customFee = "Customize fee config" + + setGasLimit = "Set gas limit" + setBlockRate = "Set target block rate" + setMinBaseFee = "Set min base fee" + setTargetGas = "Set target gas" + setBaseFeeChangeDenominator = "Set base fee change denominator" + setMinBlockGas = "Set min block gas cost" + setMaxBlockGas = "Set max block gas cost" + setGasStep = "Set block gas cost step" + ) + + config.FeeConfig = vm.StarterFeeConfig + + if useDefault { + config.FeeConfig.TargetGas = vm.LowTarget + return config, nil + } + + feeConfigOptions := []string{useSlow, useMedium, useFast, customFee} + + feeDefault, err := app.Prompt.CaptureList( + "How would you like to set fees", + feeConfigOptions, + ) + if err != nil { + return config, err + } + + switch feeDefault { + case useFast: + config.FeeConfig.TargetGas = vm.HighTarget + return config, nil + case useMedium: + config.FeeConfig.TargetGas = vm.MediumTarget + return config, nil + case useSlow: + config.FeeConfig.TargetGas = vm.LowTarget + return config, nil + default: + ux.Logger.PrintToUser("Customizing fee config") + } + + gasLimit, err := app.Prompt.CapturePositiveBigInt(setGasLimit) + if err != nil { + return config, err + } + + blockRate, err := app.Prompt.CapturePositiveBigInt(setBlockRate) + if err != nil { + return config, err + } + + minBaseFee, err := app.Prompt.CapturePositiveBigInt(setMinBaseFee) + if err != nil { + return config, err + } + + targetGas, err := app.Prompt.CapturePositiveBigInt(setTargetGas) + if err != nil { + return config, err + } + + baseDenominator, err := app.Prompt.CapturePositiveBigInt(setBaseFeeChangeDenominator) + if err != nil { + return config, err + } + + minBlockGas, err := app.Prompt.CapturePositiveBigInt(setMinBlockGas) + if err != nil { + return config, err + } + + maxBlockGas, err := app.Prompt.CapturePositiveBigInt(setMaxBlockGas) + if err != nil { + return config, err + } + + gasStep, err := app.Prompt.CapturePositiveBigInt(setGasStep) + if err != nil { + return config, err + } + + feeConf := commontype.FeeConfig{ + GasLimit: gasLimit, + TargetBlockRate: blockRate.Uint64(), + MinBaseFee: minBaseFee, + TargetGas: targetGas, + BaseFeeChangeDenominator: baseDenominator, + MinBlockGasCost: minBlockGas, + MaxBlockGasCost: maxBlockGas, + BlockGasCostStep: gasStep, + } + + config.FeeConfig = feeConf + + return config, nil +} func promptContractAllowListParams( sc *models.Sidecar, precompiles *[]params.PrecompileUpgrade, diff --git a/pkg/models/sidecar.go b/pkg/models/sidecar.go index 06861f598..ba41f7ecf 100644 --- a/pkg/models/sidecar.go +++ b/pkg/models/sidecar.go @@ -35,6 +35,7 @@ type Sidecar struct { VMVersion string RPCVersion int Subnet string + ExternalToken bool TokenName string TokenSymbol string ChainID string diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 5e58504c4..35a69b383 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -75,10 +75,7 @@ func CreateEvmGenesis( app *application.Avalanche, subnetName string, params SubnetEVMGenesisParams, - subnetEVMVersion string, tokenSymbol string, - useSubnetEVMDefaults bool, - useWarp bool, teleporterInfo *teleporter.Info, ) ([]byte, error) { ux.Logger.PrintToUser("creating genesis for subnet %s", subnetName) diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 77c019517..9fdf0ff32 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -64,6 +64,16 @@ type SubnetEVMGenesisParams struct { enableWarpPrecompile bool } +func PromptTokenSymbol( + app *application.Avalanche, + tokenSymbol string, +) (string, error) { + if tokenSymbol != "" { + return tokenSymbol, nil + } + return app.Prompt.CaptureString("Token Symbol") +} + func PromptVMType( app *application.Avalanche, useSubnetEvm bool, @@ -190,11 +200,9 @@ func promptGasToken( cancel bool ) if useDefaults { - if tokenSymbol == "" { - tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } + tokenSymbol, err = PromptTokenSymbol(app, tokenSymbol) + if err != nil { + return SubnetEVMGenesisParams{}, "", err } params.initialTokenAllocation.allocToNewKey = true return params, tokenSymbol, nil @@ -214,11 +222,9 @@ func promptGasToken( case externalTokenOption: params.UseExternalGasToken = true case nativeTokenOption: - if tokenSymbol == "" { - tokenSymbol, err = app.Prompt.CaptureString("Token Symbol") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } + tokenSymbol, err = PromptTokenSymbol(app, tokenSymbol) + if err != nil { + return SubnetEVMGenesisParams{}, "", err } allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" diff --git a/pkg/vm/evmSettings.go b/pkg/vm/evmSettings.go index f3bf16477..caa71e96e 100644 --- a/pkg/vm/evmSettings.go +++ b/pkg/vm/evmSettings.go @@ -18,9 +18,9 @@ const ( var ( Difficulty = big.NewInt(0) - lowTarget = big.NewInt(15_000_000) - mediumTarget = big.NewInt(20_000_000) - highTarget = big.NewInt(50_000_000) + LowTarget = big.NewInt(15_000_000) + MediumTarget = big.NewInt(20_000_000) + HighTarget = big.NewInt(50_000_000) // This is the current c-chain gas config StarterFeeConfig = commontype.FeeConfig{ diff --git a/pkg/vm/fees.go b/pkg/vm/fees.go index 839ec9056..bd5be3376 100644 --- a/pkg/vm/fees.go +++ b/pkg/vm/fees.go @@ -16,11 +16,11 @@ func setFeeConfig( switch { case params.feeConfig.highThroughput: - config.FeeConfig.TargetGas = highTarget + config.FeeConfig.TargetGas = HighTarget case params.feeConfig.mediumThroughput: - config.FeeConfig.TargetGas = mediumTarget + config.FeeConfig.TargetGas = MediumTarget case params.feeConfig.lowThroughput: - config.FeeConfig.TargetGas = lowTarget + config.FeeConfig.TargetGas = LowTarget default: setCustomFeeConfig(params, config) } From 098662a818ec4a4aad2bc88365aa59c0efe1949b Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 14:41:30 -0300 Subject: [PATCH 48/63] add teleporter prompt to create custom --- cmd/subnetcmd/create.go | 73 +++++++++++++++++++++-------------------- pkg/vm/evmPrompts.go | 26 +++++++-------- 2 files changed, 51 insertions(+), 48 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 6f94971fb..e34fc1b7d 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -177,11 +177,22 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } var ( - genesisBytes []byte - sc *models.Sidecar + genesisBytes []byte + sc *models.Sidecar + useTeleporterFlag *bool + deployTeleporter bool + useExternalGasToken bool ) - var teleporterInfo *teleporter.Info + flagName := "teleporter" + if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { + useTeleporterFlag = &createFlags.useTeleporter + } + + teleporterInfo, err := teleporter.GetInfo(app) + if err != nil { + return err + } if vmType == models.SubnetEvm { // get vm version @@ -200,14 +211,19 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { return err } + var tokenSymbol string + if genesisFile != "" { - // load given genesis if evmCompatibleGenesis, err := utils.FileIsSubnetEVMGenesis(genesisFile); err != nil { return err } else if !evmCompatibleGenesis { return fmt.Errorf("the provided genesis file has no proper Subnet-EVM format") } - tokenSymbol, err := vm.PromptTokenSymbol(app, createFlags.tokenSymbol) + tokenSymbol, err = vm.PromptTokenSymbol(app, createFlags.tokenSymbol) + if err != nil { + return err + } + deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) if err != nil { return err } @@ -216,20 +232,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - sc, err = vm.CreateEvmSidecar( - app, - subnetName, - vmVersion, - tokenSymbol, - true, - ) } else { - var useTeleporterFlag *bool - flagName := "teleporter" - if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { - useTeleporterFlag = &createFlags.useTeleporter - } - params, tokenSymbol, err := vm.PromptSubnetEVMGenesisParams( + var params vm.SubnetEVMGenesisParams + params, tokenSymbol, err = vm.PromptSubnetEVMGenesisParams( app, vmVersion, createFlags.chainID, @@ -241,14 +246,8 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - - if params.UseTeleporter || params.UseExternalGasToken { - teleporterInfo, err = teleporter.GetInfo(app) - if err != nil { - return err - } - } - + deployTeleporter = params.UseTeleporter + useExternalGasToken = params.UseExternalGasToken genesisBytes, err = vm.CreateEvmGenesis( app, subnetName, @@ -259,15 +258,14 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - - sc, err = vm.CreateEvmSidecar( - app, - subnetName, - vmVersion, - tokenSymbol, - true, - ) } + sc, err = vm.CreateEvmSidecar( + app, + subnetName, + vmVersion, + tokenSymbol, + true, + ) } else { genesisBytes, sc, err = vm.CreateCustomSubnetConfig( app, @@ -282,10 +280,15 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } + deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) + if err != nil { + return err + } } - if teleporterInfo != nil { + if deployTeleporter || useExternalGasToken { sc.TeleporterReady = true + sc.ExternalToken = useExternalGasToken sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version if genesisFile != "" { diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 9fdf0ff32..40c7574f6 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -162,7 +162,7 @@ func PromptSubnetEVMGenesisParams( return SubnetEVMGenesisParams{}, "", err } // Interoperability - params, err = promptInteropt(app, useTeleporter, useDefaults, params) + params.UseTeleporter, err = PromptInteropt(app, useTeleporter, useDefaults, params.UseExternalGasToken) if err != nil { return SubnetEVMGenesisParams{}, "", err } @@ -448,18 +448,19 @@ func promptFeeConfig( // is useDefaults is true, will enable teleporter // if using external gas token, will assume teleporter to be enabled // if other cases, prompts the user for wether to enable teleporter -func promptInteropt( +func PromptInteropt( app *application.Avalanche, - useTeleporter *bool, + useTeleporterFlag *bool, useDefaults bool, - params SubnetEVMGenesisParams, -) (SubnetEVMGenesisParams, error) { + useExternalGasToken bool, +) (bool, error) { switch { - case useTeleporter != nil: - params.UseTeleporter = *useTeleporter + case useTeleporterFlag != nil: + return *useTeleporterFlag, nil case useDefaults: - params.UseTeleporter = true - case params.UseExternalGasToken: + return true, nil + case useExternalGasToken: + return true, nil default: interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" isolatedBlockchainOption := "No, I want to run my blockchain isolated" @@ -470,20 +471,19 @@ func promptInteropt( options, ) if err != nil { - return SubnetEVMGenesisParams{}, err + return false, err } switch option { case isolatedBlockchainOption: + return false, nil case interoperatingBlockchainOption: - params.UseTeleporter = true + return true, nil case explainOption: ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") continue } - break } } - return params, nil } func promptPermissioning( From 5fe867a137c62e013cae81894d2df1fbf0964aaf Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 14:49:36 -0300 Subject: [PATCH 49/63] add check for compatible subnet evm genesis when asking for teleporter on custom --- cmd/subnetcmd/create.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index e34fc1b7d..1ade9d922 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -280,9 +280,11 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if err != nil { return err } - deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) - if err != nil { - return err + if evmCompatibleGenesis := utils.ByteSliceIsSubnetEvmGenesis(genesisBytes); evmCompatibleGenesis { + deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) + if err != nil { + return err + } } } From 4f711054192abd107d31675e798cda57539d49db Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 16:25:16 -0300 Subject: [PATCH 50/63] add tokensymbol to custom vm --- cmd/subnetcmd/create.go | 25 +++++++++++++++++-------- pkg/vm/createCustom.go | 32 ++++++++++++++++---------------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 1ade9d922..3f0ce0444 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -267,25 +267,34 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { true, ) } else { - genesisBytes, sc, err = vm.CreateCustomSubnetConfig( + genesisBytes, err = vm.LoadCustomGenesis(app, genesisFile) + if err != nil { + return err + } + var tokenSymbol string + if evmCompatibleGenesis := utils.ByteSliceIsSubnetEvmGenesis(genesisBytes); evmCompatibleGenesis { + tokenSymbol, err = vm.PromptTokenSymbol(app, createFlags.tokenSymbol) + if err != nil { + return err + } + deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) + if err != nil { + return err + } + } + sc, err = vm.CreateCustomSidecar( app, subnetName, - genesisFile, useRepo, customVMRepoURL, customVMBranch, customVMBuildScript, vmFile, + tokenSymbol, ) if err != nil { return err } - if evmCompatibleGenesis := utils.ByteSliceIsSubnetEvmGenesis(genesisBytes); evmCompatibleGenesis { - deployTeleporter, err = vm.PromptInteropt(app, useTeleporterFlag, createFlags.useDefaults, false) - if err != nil { - return err - } - } } if deployTeleporter || useExternalGasToken { diff --git a/pkg/vm/createCustom.go b/pkg/vm/createCustom.go index 0c6b527c8..83b918c63 100644 --- a/pkg/vm/createCustom.go +++ b/pkg/vm/createCustom.go @@ -16,29 +16,29 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/ux" ) -func CreateCustomSubnetConfig( +func CreateCustomSidecar( app *application.Avalanche, subnetName string, - genesisPath string, useRepo bool, customVMRepoURL string, customVMBranch string, customVMBuildScript string, vmPath string, -) ([]byte, *models.Sidecar, error) { + tokenSymbol string, +) (*models.Sidecar, error) { ux.Logger.PrintToUser("creating custom VM subnet %s", subnetName) - genesisBytes, err := loadCustomGenesis(app, genesisPath) - if err != nil { - return nil, &models.Sidecar{}, err - } - sc := &models.Sidecar{ Name: subnetName, VM: models.CustomVM, Subnet: subnetName, } + if tokenSymbol != "" { + sc.TokenSymbol = tokenSymbol + sc.TokenName = tokenSymbol + " Token" + } + if customVMRepoURL != "" || customVMBranch != "" || customVMBuildScript != "" { useRepo = true } @@ -48,42 +48,42 @@ func CreateCustomSubnetConfig( options := []string{githubOption, localOption} option, err := app.Prompt.CaptureList("How do you want to set up the VM binary?", options) if err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } if option == githubOption { useRepo = true } else { vmPath, err = app.Prompt.CaptureExistingFilepath("Enter path to VM binary") if err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } } } if useRepo { if err := SetCustomVMSourceCodeFields(app, sc, customVMRepoURL, customVMBranch, customVMBuildScript); err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } if err := BuildCustomVM(app, sc); err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } vmPath = app.GetCustomVMPath(subnetName) } else { if err := app.CopyVMBinary(vmPath, subnetName); err != nil { - return nil, &models.Sidecar{}, err + return &models.Sidecar{}, err } } rpcVersion, err := GetVMBinaryProtocolVersion(vmPath) if err != nil { - return nil, &models.Sidecar{}, fmt.Errorf("unable to get RPC version: %w", err) + return &models.Sidecar{}, fmt.Errorf("unable to get RPC version: %w", err) } sc.RPCVersion = rpcVersion - return genesisBytes, sc, nil + return sc, nil } -func loadCustomGenesis(app *application.Avalanche, genesisPath string) ([]byte, error) { +func LoadCustomGenesis(app *application.Avalanche, genesisPath string) ([]byte, error) { var err error if genesisPath == "" { genesisPath, err = app.Prompt.CaptureExistingFilepath("Enter path to custom genesis") From 6c0f2dfe095b456b5825efec82a9089d1b62efc4 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 16:31:14 -0300 Subject: [PATCH 51/63] nit --- pkg/prompts/prompts.go | 1 + pkg/prompts/validations.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 68207c105..7a27837f8 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -485,6 +485,7 @@ func (*realPrompter) CaptureExistingFilepath(promptStr string) (string, error) { if err != nil { return "", err } + pathStr = utils.ExpandHome(pathStr) return pathStr, nil } diff --git a/pkg/prompts/validations.go b/pkg/prompts/validations.go index eb671a8bd..416b036d3 100644 --- a/pkg/prompts/validations.go +++ b/pkg/prompts/validations.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/constants" "github.com/ava-labs/avalanche-cli/pkg/models" + "github.com/ava-labs/avalanche-cli/pkg/utils" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanchego/ids" avagoconstants "github.com/ava-labs/avalanchego/utils/constants" @@ -110,6 +111,7 @@ func validateAddresses(input string) error { } func validateExistingFilepath(input string) error { + input = utils.ExpandHome(input) if fileInfo, err := os.Stat(input); err == nil && !fileInfo.IsDir() { return nil } From aa60c12551935375dbdfd9a6e3e49e0fc201fc57 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 16:51:26 -0300 Subject: [PATCH 52/63] fix relayer conf for the moment --- cmd/subnetcmd/create.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 3f0ce0444..53bce8836 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -299,6 +299,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { if deployTeleporter || useExternalGasToken { sc.TeleporterReady = true + sc.RunRelayer = true // TODO: remove this once deploy asks if deploying relayer sc.ExternalToken = useExternalGasToken sc.TeleporterKey = constants.TeleporterKeyName sc.TeleporterVersion = teleporterInfo.Version From 3764c79521475e7b2ea097bbb9ddc47cc24699c0 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 16:58:45 -0300 Subject: [PATCH 53/63] nits --- cmd/subnetcmd/create.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 53bce8836..4d3339e0b 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -184,11 +184,13 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { useExternalGasToken bool ) + // get teleporter flag as a pointer (3 values: undef/true/false) flagName := "teleporter" if flag := cmd.Flags().Lookup(flagName); flag != nil && flag.Changed { useTeleporterFlag = &createFlags.useTeleporter } + // get teleporter info teleporterInfo, err := teleporter.GetInfo(app) if err != nil { return err From 1e555842c59e21e00b97e831610a4b295e878b64 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 17:24:51 -0300 Subject: [PATCH 54/63] lint --- cmd/subnetcmd/create.go | 4 +- cmd/subnetcmd/export_test.go | 15 +++--- cmd/subnetcmd/upgradecmd/generate.go | 1 + pkg/vm/airdrop_test.go | 81 ---------------------------- pkg/vm/createEvm.go | 1 - pkg/vm/createEvm_test.go | 72 ++++++------------------- pkg/vm/descriptors.go | 34 ------------ 7 files changed, 28 insertions(+), 180 deletions(-) delete mode 100644 pkg/vm/airdrop_test.go diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 4d3339e0b..5874b5dc0 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -254,7 +254,6 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { app, subnetName, params, - tokenSymbol, teleporterInfo, ) if err != nil { @@ -268,6 +267,9 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { tokenSymbol, true, ) + if err != nil { + return err + } } else { genesisBytes, err = vm.LoadCustomGenesis(app, genesisFile) if err != nil { diff --git a/cmd/subnetcmd/export_test.go b/cmd/subnetcmd/export_test.go index 3bc897415..25eb5f087 100644 --- a/cmd/subnetcmd/export_test.go +++ b/cmd/subnetcmd/export_test.go @@ -15,7 +15,6 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" - "github.com/ava-labs/avalanche-cli/tests/e2e/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -35,17 +34,19 @@ func TestExportImportSubnet(t *testing.T) { app.Setup(testDir, logging.NoLog{}, nil, prompts.NewPrompter(), &mockAppDownloader) ux.NewUserLog(logging.NoLog{}, io.Discard) - genBytes, sc, err := vm.CreateEvmSubnetConfig( + genBytes, err := vm.CreateEvmGenesis( + app, + testSubnet, + vm.SubnetEVMGenesisParams{}, + nil, + ) + require.NoError(err) + sc, err := vm.CreateEvmSidecar( app, testSubnet, - "../../"+utils.SubnetEvmGenesisPath, vmVersion, - false, - 0, "", false, - false, - nil, ) require.NoError(err) err = app.WriteGenesisFile(testSubnet, genBytes) diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index bc2010f75..9ae41d9f4 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -474,6 +474,7 @@ func GetFeeConfig(config params.ChainConfig, useDefault bool) ( return config, nil } + func promptContractAllowListParams( sc *models.Sidecar, precompiles *[]params.PrecompileUpgrade, diff --git a/pkg/vm/airdrop_test.go b/pkg/vm/airdrop_test.go deleted file mode 100644 index 6b86af550..000000000 --- a/pkg/vm/airdrop_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2022, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package vm - -import ( - "math/big" - "testing" - - "github.com/ava-labs/avalanche-cli/internal/mocks" - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/mock" -) - -var testAirdropAddress = common.HexToAddress("0x098B69E43b1720Bd12378225519d74e5F3aD0eA5") - -func TestGetAllocationCustomUnits(t *testing.T) { - require := setupTest(t) - app := application.New() - mockPrompt := &mocks.Prompter{} - app.Prompt = mockPrompt - - airdropInputAmount := new(big.Int) - airdropInputAmount.SetString("1000000", 10) - - expectedAmount := new(big.Int) - expectedAmount.SetString(defaultEvmAirdropAmount, 10) - - mockPrompt.On("CaptureList", mock.Anything, mock.Anything).Return(customAirdrop, nil) - mockPrompt.On("CaptureAddress", mock.Anything).Return(testAirdropAddress, nil) - mockPrompt.On("CapturePositiveBigInt", mock.Anything).Return(airdropInputAmount, nil) - mockPrompt.On("CaptureNoYes", mock.Anything).Return(false, nil) - - alloc, direction, err := getAllocation(app, "", defaultEvmAirdropAmount, oneAvax, "", false) - require.NoError(err) - require.Equal(direction, statemachine.Forward) - - require.Equal(alloc[testAirdropAddress].Balance, expectedAmount) -} - -func TestMultipleAirdropsSameAddress(t *testing.T) { - require := setupTest(t) - app := application.New() - mockPrompt := &mocks.Prompter{} - app.Prompt = mockPrompt - - airdropInputAmount := new(big.Int) - airdropInputAmount.SetString("500000", 10) - airdropInputAmount2 := new(big.Int) - airdropInputAmount2.SetString("500000", 10) - - expectedAmount := new(big.Int) - expectedAmount.SetString(defaultEvmAirdropAmount, 10) - - mockPrompt.On("CaptureList", mock.Anything, mock.Anything).Return(customAirdrop, nil).Once() - - captureAddress := mockPrompt.On("CaptureAddress", mock.Anything). - Return(testAirdropAddress, nil). - Once() - captureInt := mockPrompt.On("CapturePositiveBigInt", mock.Anything). - Return(airdropInputAmount, nil). - Once() - captureNoYes := mockPrompt.On("CaptureNoYes", mock.Anything).Return(true, nil).Once() - mockPrompt.On("CaptureAddress", mock.Anything). - Return(testAirdropAddress, nil). - Once(). - NotBefore(captureAddress) - mockPrompt.On("CapturePositiveBigInt", mock.Anything). - Return(airdropInputAmount2, nil). - Once(). - NotBefore(captureInt) - mockPrompt.On("CaptureNoYes", mock.Anything).Return(false, nil).Once().NotBefore(captureNoYes) - - alloc, direction, err := getAllocation(app, "", defaultEvmAirdropAmount, oneAvax, "", false) - require.NoError(err) - require.Equal(direction, statemachine.Forward) - - require.Equal(alloc[testAirdropAddress].Balance, expectedAmount) -} diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 35a69b383..78bd1a18c 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -75,7 +75,6 @@ func CreateEvmGenesis( app *application.Avalanche, subnetName string, params SubnetEVMGenesisParams, - tokenSymbol string, teleporterInfo *teleporter.Info, ) ([]byte, error) { ux.Logger.PrintToUser("creating genesis for subnet %s", subnetName) diff --git a/pkg/vm/createEvm_test.go b/pkg/vm/createEvm_test.go index 696831640..68ffd996b 100644 --- a/pkg/vm/createEvm_test.go +++ b/pkg/vm/createEvm_test.go @@ -3,7 +3,6 @@ package vm import ( - "errors" "math/big" "testing" @@ -20,7 +19,7 @@ func Test_ensureAdminsFunded(t *testing.T) { type test struct { name string alloc core.GenesisAlloc - admins []common.Address + allowList AllowList shouldFail bool } tests := []test{ @@ -33,7 +32,9 @@ func Test_ensureAdminsFunded(t *testing.T) { }, addrs[2]: {}, }, - admins: []common.Address{addrs[1]}, + allowList: AllowList{ + AdminAddresses: []common.Address{addrs[1]}, + }, shouldFail: false, }, { @@ -47,7 +48,9 @@ func Test_ensureAdminsFunded(t *testing.T) { Balance: big.NewInt(42), }, }, - admins: []common.Address{addrs[3], addrs[4]}, + allowList: AllowList{ + AdminAddresses: []common.Address{addrs[3], addrs[4]}, + }, shouldFail: false, }, { @@ -59,7 +62,9 @@ func Test_ensureAdminsFunded(t *testing.T) { addrs[1]: {}, addrs[2]: {}, }, - admins: []common.Address{addrs[0], addrs[2]}, + allowList: AllowList{ + AdminAddresses: []common.Address{addrs[0], addrs[2]}, + }, shouldFail: true, }, { @@ -69,7 +74,9 @@ func Test_ensureAdminsFunded(t *testing.T) { addrs[1]: {}, addrs[2]: {}, }, - admins: []common.Address{addrs[3], addrs[4]}, + allowList: AllowList{ + AdminAddresses: []common.Address{addrs[3], addrs[4]}, + }, shouldFail: true, }, } @@ -77,59 +84,12 @@ func Test_ensureAdminsFunded(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require := require.New(t) - - err := ensureAdminsHaveBalance(tt.admins, tt.alloc) + b := someAllowedHasBalance(tt.allowList, tt.alloc) if tt.shouldFail { - require.Error(err) + require.Equal(b, false) } else { - require.NoError(err) + require.Equal(b, true) } }) } } - -func Test_removePrecompile(t *testing.T) { - allowList := "allow list" - minter := "minter" - - type test struct { - name string - precompileList []string - toRemove string - expectedResult []string - expectedErr error - } - tests := []test{ - { - name: "Success", - precompileList: []string{allowList, minter}, - toRemove: allowList, - expectedResult: []string{minter}, - expectedErr: nil, - }, - { - name: "Success reverse", - precompileList: []string{allowList, minter}, - toRemove: minter, - expectedResult: []string{allowList}, - expectedErr: nil, - }, - { - name: "Failure", - precompileList: []string{minter}, - toRemove: allowList, - expectedResult: []string{minter}, - expectedErr: errors.New("string not in array"), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) - - // Check how many selected - shortenedList, err := removePrecompile(tt.precompileList, tt.toRemove) - require.Equal(tt.expectedResult, shortenedList) - require.Equal(tt.expectedErr, err) - }) - } -} diff --git a/pkg/vm/descriptors.go b/pkg/vm/descriptors.go index af36b01c4..188cc2a7d 100644 --- a/pkg/vm/descriptors.go +++ b/pkg/vm/descriptors.go @@ -4,21 +4,10 @@ package vm import ( - "math/big" - "github.com/ava-labs/avalanche-cli/pkg/application" - "github.com/ava-labs/avalanche-cli/pkg/statemachine" "github.com/ava-labs/avalanche-cli/pkg/ux" ) -func getChainID(app *application.Avalanche, subnetEVMChainID uint64) (*big.Int, error) { - if subnetEVMChainID != 0 { - return new(big.Int).SetUint64(subnetEVMChainID), nil - } - ux.Logger.PrintToUser("Enter your subnet's ChainId. It can be any positive integer.") - return app.Prompt.CapturePositiveBigInt("ChainId") -} - func getTokenSymbol(app *application.Avalanche, subnetEVMTokenSymbol string) (string, error) { if subnetEVMTokenSymbol != "" { return subnetEVMTokenSymbol, nil @@ -31,26 +20,3 @@ func getTokenSymbol(app *application.Avalanche, subnetEVMTokenSymbol string) (st return tokenSymbol, nil } - -func getDescriptors( - app *application.Avalanche, - subnetEVMChainID uint64, - subnetEVMTokenSymbol string, -) ( - *big.Int, - string, - statemachine.StateDirection, - error, -) { - chainID, err := getChainID(app, subnetEVMChainID) - if err != nil { - return nil, "", statemachine.Stop, err - } - - tokenSymbol, err := getTokenSymbol(app, subnetEVMTokenSymbol) - if err != nil { - return nil, "", statemachine.Stop, err - } - - return chainID, tokenSymbol, statemachine.Forward, nil -} From 21fd24c2368a010da1106378b564b77c7a665847 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 17:32:49 -0300 Subject: [PATCH 55/63] unit test --- cmd/subnetcmd/export_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/subnetcmd/export_test.go b/cmd/subnetcmd/export_test.go index 25eb5f087..0177c307f 100644 --- a/cmd/subnetcmd/export_test.go +++ b/cmd/subnetcmd/export_test.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/avalanche-cli/pkg/prompts" "github.com/ava-labs/avalanche-cli/pkg/ux" "github.com/ava-labs/avalanche-cli/pkg/vm" + "github.com/ava-labs/avalanche-cli/tests/e2e/utils" "github.com/ava-labs/avalanchego/utils/logging" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -34,18 +35,16 @@ func TestExportImportSubnet(t *testing.T) { app.Setup(testDir, logging.NoLog{}, nil, prompts.NewPrompter(), &mockAppDownloader) ux.NewUserLog(logging.NoLog{}, io.Discard) - genBytes, err := vm.CreateEvmGenesis( + genBytes, err := vm.LoadCustomGenesis( app, - testSubnet, - vm.SubnetEVMGenesisParams{}, - nil, + "../../"+utils.SubnetEvmGenesisPath, ) require.NoError(err) sc, err := vm.CreateEvmSidecar( app, testSubnet, vmVersion, - "", + "Test", false, ) require.NoError(err) @@ -81,7 +80,7 @@ func TestExportImportSubnet(t *testing.T) { require.Equal(control["VMVersion"], vmVersion) require.Equal(control["Subnet"], testSubnet) require.Equal(control["TokenName"], "Test Token") - require.Equal(control["TokenSymbol"], "TEST") + require.Equal(control["TokenSymbol"], "Test") require.Equal(control["Version"], constants.SidecarVersion) require.Equal(control["Networks"], nil) From 00c90dd9410415a8b5b1007050304ce964777b9a Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 17:42:48 -0300 Subject: [PATCH 56/63] fix e2e --- tests/e2e/commands/subnet.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/e2e/commands/subnet.go b/tests/e2e/commands/subnet.go index 60807a61f..514a794c9 100644 --- a/tests/e2e/commands/subnet.go +++ b/tests/e2e/commands/subnet.go @@ -47,6 +47,8 @@ func CreateSubnetEvmConfigWithVersion(subnetName string, genesisPath string, ver subnetName, "--" + constants.SkipUpdateFlag, "--teleporter=false", + "--evm-token", + "TOK", } if version == "" { cmdArgs = append(cmdArgs, "--latest") @@ -128,6 +130,8 @@ func CreateCustomVMConfig(subnetName string, genesisPath string, vmPath string) vmPath, "--"+constants.SkipUpdateFlag, "--teleporter=false", + "--evm-token", + "TOK", ) output, err := cmd.CombinedOutput() if err != nil { From 4d284f74cb55ca1784fe1c05be9f2fb488685d07 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Fri, 12 Jul 2024 19:44:28 -0300 Subject: [PATCH 57/63] fix flag exclusion for genesis agains tokenSymbol --- cmd/subnetcmd/create.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 5874b5dc0..85f99a77d 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -55,7 +55,7 @@ var ( errIllegalNameCharacter = errors.New( "illegal name character: only letters, no special characters allowed") errMutuallyExlusiveVersionOptions = errors.New("version flags --latest,--pre-release,vm-version are mutually exclusive") - errMutuallyExclusiveVMConfigOptions = errors.New("specifying --genesis flag disable flags --evm-chain-id,--evm-token,--evm-defaults") + errMutuallyExclusiveVMConfigOptions = errors.New("--genesis flag disables --evm-chain-id,--evm-defaults") ) // avalanche subnet create @@ -156,7 +156,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } // genesis flags exclusiveness - if genesisFile != "" && (createFlags.chainID != 0 || createFlags.tokenSymbol != "" || createFlags.useDefaults) { + if genesisFile != "" && (createFlags.chainID != 0 || createFlags.useDefaults) { return errMutuallyExclusiveVMConfigOptions } From 0d1c5b10d190d780b7a5d07563e6953388389433 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Mon, 22 Jul 2024 22:24:10 -0300 Subject: [PATCH 58/63] latest changes besides ux changes --- cmd/subnetcmd/create.go | 3 + pkg/vm/allocations.go | 23 ++--- pkg/vm/createEvm.go | 1 + pkg/vm/evmPrompts.go | 212 ++++++++++++++++++++++++++-------------- 4 files changed, 152 insertions(+), 87 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 85f99a77d..6e1a14690 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -43,6 +43,7 @@ type CreateFlags struct { vmVersion string useLatestReleasedVMVersion bool useLatestPreReleasedVMVersion bool + useExternalGasToken bool } var ( @@ -96,6 +97,7 @@ configuration, pass the -f flag.`, cmd.Flags().BoolVar(&useRepo, "from-github-repo", false, "generate custom VM binary from github repository") cmd.Flags().BoolVar(&createFlags.useWarp, "warp", true, "generate a vm with warp support (needed for teleporter)") cmd.Flags().BoolVar(&createFlags.useTeleporter, "teleporter", false, "interoperate with other blockchains using teleporter") + cmd.Flags().BoolVar(&createFlags.useExternalGasToken, "external-gas-token", false, "use a gas token from another blockchain") return cmd } @@ -244,6 +246,7 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { useTeleporterFlag, createFlags.useDefaults, createFlags.useWarp, + createFlags.useExternalGasToken, ) if err != nil { return err diff --git a/pkg/vm/allocations.go b/pkg/vm/allocations.go index 308edc158..97682e6d4 100644 --- a/pkg/vm/allocations.go +++ b/pkg/vm/allocations.go @@ -70,18 +70,19 @@ func getAllocation( subnetName string, defaultAirdropAmount string, multiplier *big.Int, + useExternalGasToken bool, ) (core.GenesisAlloc, error) { - if params.initialTokenAllocation.allocToNewKey { - return getNewAllocation(app, subnetName, defaultAirdropAmount) - } - - if params.initialTokenAllocation.allocToEwoq { - return getEwoqAllocation(defaultAirdropAmount) - } - allocations := core.GenesisAlloc{} - amount := new(big.Int).SetUint64(params.initialTokenAllocation.customBalance) - amount = amount.Mul(amount, multiplier) - addAllocation(allocations, params.initialTokenAllocation.customAddress.Hex(), amount) + if !useExternalGasToken { + if params.initialTokenAllocation.allocToNewKey { + return getNewAllocation(app, subnetName, defaultAirdropAmount) + } + if params.initialTokenAllocation.allocToEwoq { + return getEwoqAllocation(defaultAirdropAmount) + } + amount := new(big.Int).SetUint64(params.initialTokenAllocation.customBalance) + amount = amount.Mul(amount, multiplier) + addAllocation(allocations, params.initialTokenAllocation.customAddress.Hex(), amount) + } return allocations, nil } diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 78bd1a18c..05ec87401 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -95,6 +95,7 @@ func CreateEvmGenesis( subnetName, defaultEvmAirdropAmount, oneAvax, + params.UseExternalGasToken, ) if err != nil { return nil, err diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 40c7574f6..05796c012 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -19,9 +19,10 @@ import ( ) const ( - latest = "latest" - preRelease = "pre-release" - explainOption = "Explain the difference" + latest = "latest" + preRelease = "pre-release" + explainOption = "Explain the difference" + enableExternalGasTokenPrompt = false ) type InitialTokenAllocation struct { @@ -138,6 +139,7 @@ func PromptSubnetEVMGenesisParams( useTeleporter *bool, useDefaults bool, useWarp bool, + useExternalGasToken bool, ) (SubnetEVMGenesisParams, string, error) { var ( err error @@ -151,11 +153,23 @@ func PromptSubnetEVMGenesisParams( return SubnetEVMGenesisParams{}, "", err } } - // Gas Token - params, tokenSymbol, err = promptGasToken(app, version, tokenSymbol, useDefaults, params) + // Defaults + useDefaults, err = promptDefaults(app, useDefaults) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + // Gas Kind + params, err = promptGasTokenKind(app, useDefaults, useExternalGasToken, params) if err != nil { return SubnetEVMGenesisParams{}, "", err } + // Native Gas Details + if !params.UseExternalGasToken { + params, tokenSymbol, err = promptNativeGasToken(app, version, tokenSymbol, useDefaults, params) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } // Transaction / Gas Fees params, err = promptFeeConfig(app, version, useDefaults, params) if err != nil { @@ -179,16 +193,95 @@ func PromptSubnetEVMGenesisParams( return params, tokenSymbol, nil } -// prompts for wether to use a remote or native gas token, -// and in the case of native, also prompts for token symbol, -// initial token allocation, and native minter precompile +// prompts for wether to use a remote or native gas token +func promptGasTokenKind( + app *application.Avalanche, + useDefaults bool, + useExternalGasToken bool, + params SubnetEVMGenesisParams, +) (SubnetEVMGenesisParams, error) { + if useExternalGasToken { + params.UseExternalGasToken = true + } else if enableExternalGasTokenPrompt && !useDefaults { + var err error + nativeTokenOption := "The blockchain's native token" + externalTokenOption := "A token from another blockchain" + options := []string{nativeTokenOption, externalTokenOption, explainOption} + for { + var option string + if enableExternalGasTokenPrompt { + option, err = app.Prompt.CaptureList( + "Which token will be used for transaction fee payments?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + } else { + option = nativeTokenOption + } + switch option { + case externalTokenOption: + params.UseExternalGasToken = true + case nativeTokenOption: + case explainOption: + ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) + ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and therefore the transaction fees are completely isolated.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) + ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") + ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") + continue + } + break + } + } + return params, nil +} + +// prompts for wether to use defaults to build the config +func promptDefaults( + app *application.Avalanche, + useDefaults bool, +) (bool, error) { + if !useDefaults { + specifyMyValuesOption := "No, I want to set each config value" + useDefaultsOption := "Yes, I want to use CLI default values" + options := []string{specifyMyValuesOption, useDefaultsOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want to use CLI defaults for most common blockchain options?", + options, + ) + if err != nil { + return false, err + } + switch option { + case useDefaultsOption: + useDefaults = true + case specifyMyValuesOption: + useDefaults = false + case explainOption: + ux.Logger.PrintToUser("You can either let CLI set default values for token allocation, gas fees, precompiles\nand interop, or you can set your own config values if you need so.\n\nCLI defaults involve:\n- allocating 1m tokens to a newly created key\n- disable addicional minting of tokens besides the allocated ones\n- customize gas fee config for low throughput\n- disable further changes in gas fee config\n- always burn fees\n- enable interoperation of the blockchain with other blockchains\n- disable permissioned controls over transaction submission and contracts deployment") + continue + } + break + } + } + return useDefaults, nil +} + +// prompts for token symbol, initial token allocation, and native minter precompile // configuration // // if tokenSymbol is not defined, will prompt for it // is useDefaults is true, will: // - use native gas token, allocating 1m to a newly created key // - disable native minter precompile -func promptGasToken( +func promptNativeGasToken( app *application.Avalanche, version string, tokenSymbol string, @@ -199,97 +292,64 @@ func promptGasToken( err error cancel bool ) + tokenSymbol, err = PromptTokenSymbol(app, tokenSymbol) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } if useDefaults { - tokenSymbol, err = PromptTokenSymbol(app, tokenSymbol) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } params.initialTokenAllocation.allocToNewKey = true - return params, tokenSymbol, nil - } - nativeTokenOption := "The blockchain's native token" - externalTokenOption := "A token from another blockchain" - options := []string{nativeTokenOption, externalTokenOption, explainOption} - for { + } else { + allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" + allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" + customAllocationOption := "Define a custom allocation (Recommended for production)" + options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} option, err := app.Prompt.CaptureList( - "Which token will be used for transaction fee payments?", + "How should the initial token allocation be structured?", options, ) if err != nil { return SubnetEVMGenesisParams{}, "", err } switch option { - case externalTokenOption: - params.UseExternalGasToken = true - case nativeTokenOption: - tokenSymbol, err = PromptTokenSymbol(app, tokenSymbol) + case allocateToNewKeyOption: + params.initialTokenAllocation.allocToNewKey = true + case allocateToEwoqOption: + params.initialTokenAllocation.allocToEwoq = true + case customAllocationOption: + params.initialTokenAllocation.customAddress, err = app.Prompt.CaptureAddress("Address to allocate to") if err != nil { return SubnetEVMGenesisParams{}, "", err } - allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" - allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" - customAllocationOption := "Define a custom allocation (Recommended for production)" - options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} - option, err := app.Prompt.CaptureList( - "How should the initial token allocation be structured?", + params.initialTokenAllocation.customBalance, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) + if err != nil { + return SubnetEVMGenesisParams{}, "", err + } + } + for { + fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" + dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" + options = []string{fixedSupplyOption, dynamicSupplyOption} + option, err = app.Prompt.CaptureList( + "Allow minting new native Tokens? (Native Minter Precompile)", options, ) if err != nil { return SubnetEVMGenesisParams{}, "", err } switch option { - case allocateToNewKeyOption: - params.initialTokenAllocation.allocToNewKey = true - case allocateToEwoqOption: - params.initialTokenAllocation.allocToEwoq = true - case customAllocationOption: - params.initialTokenAllocation.customAddress, err = app.Prompt.CaptureAddress("Address to allocate to") - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - params.initialTokenAllocation.customBalance, err = app.Prompt.CaptureUint64(fmt.Sprintf("Amount to allocate (in %s units)", tokenSymbol)) + case fixedSupplyOption: + case dynamicSupplyOption: + params.nativeMinterPrecompileAllowList, cancel, err = GenerateAllowList(app, "mint native tokens", version) if err != nil { return SubnetEVMGenesisParams{}, "", err } - } - for { - fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" - dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" - options = []string{fixedSupplyOption, dynamicSupplyOption} - option, err = app.Prompt.CaptureList( - "Allow minting new native Tokens? (Native Minter Precompile)", - options, - ) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - switch option { - case fixedSupplyOption: - case dynamicSupplyOption: - params.nativeMinterPrecompileAllowList, cancel, err = GenerateAllowList(app, "mint native tokens", version) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } - if cancel { - continue - } - params.enableNativeMinterPrecompile = true + if cancel { + continue } - break + params.enableNativeMinterPrecompile = true } - case explainOption: - ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) - ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and therefore the transaction fees are completely isolated.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) - ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") - continue + break } - break } return params, tokenSymbol, nil } From bd0b3057aace4a7c75f2fe94b08869bb95231e56 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Tue, 23 Jul 2024 13:55:09 -0300 Subject: [PATCH 59/63] also include latest release download in defaults --- cmd/subnetcmd/create.go | 10 +++++++++- pkg/vm/evmPrompts.go | 15 +++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/cmd/subnetcmd/create.go b/cmd/subnetcmd/create.go index 6e1a14690..9c4b184ea 100644 --- a/cmd/subnetcmd/create.go +++ b/cmd/subnetcmd/create.go @@ -199,9 +199,17 @@ func createSubnetConfig(cmd *cobra.Command, args []string) error { } if vmType == models.SubnetEvm { + if genesisFile == "" { + // Default + createFlags.useDefaults, err = vm.PromptDefaults(app, createFlags.useDefaults) + if err != nil { + return err + } + } + // get vm version vmVersion := createFlags.vmVersion - if createFlags.useLatestReleasedVMVersion { + if createFlags.useLatestReleasedVMVersion || createFlags.useDefaults { vmVersion = latest } if createFlags.useLatestPreReleasedVMVersion { diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 05796c012..460e2d02e 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -153,11 +153,6 @@ func PromptSubnetEVMGenesisParams( return SubnetEVMGenesisParams{}, "", err } } - // Defaults - useDefaults, err = promptDefaults(app, useDefaults) - if err != nil { - return SubnetEVMGenesisParams{}, "", err - } // Gas Kind params, err = promptGasTokenKind(app, useDefaults, useExternalGasToken, params) if err != nil { @@ -243,17 +238,17 @@ func promptGasTokenKind( } // prompts for wether to use defaults to build the config -func promptDefaults( +func PromptDefaults( app *application.Avalanche, useDefaults bool, ) (bool, error) { if !useDefaults { - specifyMyValuesOption := "No, I want to set each config value" useDefaultsOption := "Yes, I want to use CLI default values" - options := []string{specifyMyValuesOption, useDefaultsOption, explainOption} + specifyMyValuesOption := "No, I want to set each config value" + options := []string{useDefaultsOption, specifyMyValuesOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to use CLI defaults for most common blockchain options?", + "Do you want to use CLI defaults for common blockchain options?", options, ) if err != nil { @@ -265,7 +260,7 @@ func promptDefaults( case specifyMyValuesOption: useDefaults = false case explainOption: - ux.Logger.PrintToUser("You can either let CLI set default values for token allocation, gas fees, precompiles\nand interop, or you can set your own config values if you need so.\n\nCLI defaults involve:\n- allocating 1m tokens to a newly created key\n- disable addicional minting of tokens besides the allocated ones\n- customize gas fee config for low throughput\n- disable further changes in gas fee config\n- always burn fees\n- enable interoperation of the blockchain with other blockchains\n- disable permissioned controls over transaction submission and contracts deployment") + ux.Logger.PrintToUser("You can either let CLI set default values for token allocation, gas fees, precompiles\nand interop, or you can set your own config values if you need so.\n\nCLI defaults involve:\n-downloading latest release\n- allocating 1m tokens to a newly created key\n- disable addicional minting of tokens besides the allocated ones\n- customize gas fee config for low throughput\n- disable further changes in gas fee config\n- always burn fees\n- enable interoperation of the blockchain with other blockchains\n- disable permissioned controls over transaction submission and contracts deployment") continue } break From 13410375377fd2362943432415dc2785330d2767 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Wed, 24 Jul 2024 11:12:21 -0300 Subject: [PATCH 60/63] address PR comments --- pkg/vm/evmPrompts.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 460e2d02e..f2bb4560b 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -243,12 +243,12 @@ func PromptDefaults( useDefaults bool, ) (bool, error) { if !useDefaults { - useDefaultsOption := "Yes, I want to use CLI default values" - specifyMyValuesOption := "No, I want to set each config value" + useDefaultsOption := "Use default values" + specifyMyValuesOption := "Don't use default values" options := []string{useDefaultsOption, specifyMyValuesOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to use CLI defaults for common blockchain options?", + "Do you want to use default values for the Blockchain configuration?", options, ) if err != nil { @@ -260,7 +260,7 @@ func PromptDefaults( case specifyMyValuesOption: useDefaults = false case explainOption: - ux.Logger.PrintToUser("You can either let CLI set default values for token allocation, gas fees, precompiles\nand interop, or you can set your own config values if you need so.\n\nCLI defaults involve:\n-downloading latest release\n- allocating 1m tokens to a newly created key\n- disable addicional minting of tokens besides the allocated ones\n- customize gas fee config for low throughput\n- disable further changes in gas fee config\n- always burn fees\n- enable interoperation of the blockchain with other blockchains\n- disable permissioned controls over transaction submission and contracts deployment") + ux.Logger.PrintToUser("Subnet configuration default values:\n- Use latest Subnet-EVM release\n- Allocate 1 million tokens to a newly created key\n- Supply of the native token will be hard-capped\n- Set gas fee config as low throughput (12 mil gas per block)\n- Disable further adjustments in transaction fee configuration\n- Transaction fees are burned\n- Enable interoperation with other blockchains\n- Allow any user to deploy smart contracts, send transactions, and interact with your blockchain.") continue } break From cf8dd80098c5b335ff621ecf5df43814a668fe18 Mon Sep 17 00:00:00 2001 From: felipemadero Date: Thu, 25 Jul 2024 13:40:24 -0300 Subject: [PATCH 61/63] Update cmd/subnetcmd/upgradecmd/generate.go Co-authored-by: Martin Eckardt Signed-off-by: felipemadero --- cmd/subnetcmd/upgradecmd/generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index 9ae41d9f4..5eb29970f 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -304,7 +304,7 @@ func promptRewardManagerParams( func ConfigureInitialRewardConfig() (*rewardmanager.InitialRewardConfig, error) { config := &rewardmanager.InitialRewardConfig{} - burnPrompt := "Should fees be burnt?" + burnPrompt := "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). (Reward Manager Precompile)" burnFees, err := app.Prompt.CaptureYesNo(burnPrompt) if err != nil { return config, err From 5e2b16c4996c4969721eb4e6aafa11421993a261 Mon Sep 17 00:00:00 2001 From: Felipe Madero Date: Thu, 25 Jul 2024 13:50:35 -0300 Subject: [PATCH 62/63] address PR comments --- cmd/subnetcmd/upgradecmd/generate.go | 11 ++-- pkg/teleporter/teleporter.go | 4 +- pkg/vm/allocations.go | 2 +- pkg/vm/createEvm.go | 2 +- pkg/vm/evmPrompts.go | 80 ++++++++++++++-------------- 5 files changed, 52 insertions(+), 47 deletions(-) diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index 5eb29970f..d1018bc70 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -304,12 +304,17 @@ func promptRewardManagerParams( func ConfigureInitialRewardConfig() (*rewardmanager.InitialRewardConfig, error) { config := &rewardmanager.InitialRewardConfig{} - burnPrompt := "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). (Reward Manager Precompile)" - burnFees, err := app.Prompt.CaptureYesNo(burnPrompt) + burnFees := "I am fine with transaction fees being burned
(Reward Manager Precompile OFF)" + distributeFees := "I want to customize the transaction fee distribution
(Reward Manager Precompile ON)" + options := []string{burnFees, distributeFees} + option, err := app.Prompt.CaptureList( + "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). (Reward Manager Precompile)", + options, + ) if err != nil { return config, err } - if burnFees { + if option == burnFees { return config, nil } diff --git a/pkg/teleporter/teleporter.go b/pkg/teleporter/teleporter.go index c034c7313..e8bcd488c 100644 --- a/pkg/teleporter/teleporter.go +++ b/pkg/teleporter/teleporter.go @@ -32,7 +32,7 @@ var ( // 10 AVAX messengerDeployerRequiredBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) // 600 AVAX - TeleporterPrefundedAddressBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600)) + InterchainMessagingPrefundedAddressBalance = big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(600)) ) func getTeleporterURLs(version string) (string, string, string, string) { @@ -434,7 +434,7 @@ func getTeleporterKeyInfo( if err != nil { return "", "", nil, err } - return k.C(), k.PrivKeyHex(), TeleporterPrefundedAddressBalance, nil + return k.C(), k.PrivKeyHex(), InterchainMessagingPrefundedAddressBalance, nil } type Info struct { diff --git a/pkg/vm/allocations.go b/pkg/vm/allocations.go index 308edc158..c853adc80 100644 --- a/pkg/vm/allocations.go +++ b/pkg/vm/allocations.go @@ -53,7 +53,7 @@ func getEwoqAllocation(defaultAirdropAmount string) (core.GenesisAlloc, error) { return allocations, nil } -func addTeleporterAllocation( +func addInterchainMessagingAllocation( allocations core.GenesisAlloc, teleporterKeyAddress string, teleporterKeyBalance *big.Int, diff --git a/pkg/vm/createEvm.go b/pkg/vm/createEvm.go index 78bd1a18c..b35565074 100644 --- a/pkg/vm/createEvm.go +++ b/pkg/vm/createEvm.go @@ -119,7 +119,7 @@ func CreateEvmGenesis( if params.UseExternalGasToken { balance = externalGasTokenBalance } - allocations = addTeleporterAllocation( + allocations = addInterchainMessagingAllocation( allocations, teleporterInfo.FundedAddress, balance, diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index 40c7574f6..9cc898129 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -91,7 +91,7 @@ func PromptVMType( var subnetTypeStr string for { option, err := app.Prompt.CaptureList( - "VM", + "Which Virtual Machine would you like to use?", options, ) if err != nil { @@ -104,10 +104,10 @@ func PromptVMType( subnetTypeStr = models.CustomVM case explainOption: ux.Logger.PrintToUser("Virtual machines are the blueprint the defines the application-level logic of a blockchain. It determines the language and rules for writing and executing smart contracts, as well as other blockchain logic.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("Subnet-EVM is a EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. Subnet-EVM can be configured with this CLI to meet the developers requirements without writing code. For more information, please visit: https://github.com/ava-labs/subnet-evm") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("Custom VMs created with SDKs such as the Precompile-EVM, HyperSDK, Rust-SDK and that are written in golang or rust can be deployed on Avalanche using the second option. You can provide the path to the binary directly or provide the code as well as the build script. In addition to the VM you need to provide the genesis file. More information can be found in the docs at https://docs.avax.network/learn/avalanche/virtual-machines.") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Subnet-EVM is an EVM-compatible virtual machine that supports smart contract development in Solidity. This VM is an out-of-the-box solution for Subnet deployers who want a dApp development experience that is nearly identical to Ethereum, without having to manage or create a custom virtual machine. For more information, please visit: https://github.com/ava-labs/subnet-evm") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Custom VMs are virtual machines created using SDKs such as Precompile-EVM, HyperSDK, Rust-SDK. For more information please visit: https://docs.avax.network/learn/avalanche/virtual-machines.") continue } break @@ -226,7 +226,7 @@ func promptGasToken( if err != nil { return SubnetEVMGenesisParams{}, "", err } - allocateToNewKeyOption := "Allocate 1m tokens to a newly created account" + allocateToNewKeyOption := "Allocate 1m tokens to a new account" allocateToEwoqOption := "Allocate 1m to the ewoq account 0x8db...2FC (Only recommended for testing, not recommended for production)" customAllocationOption := "Define a custom allocation (Recommended for production)" options := []string{allocateToNewKeyOption, allocateToEwoqOption, customAllocationOption} @@ -253,11 +253,11 @@ func promptGasToken( } } for { - fixedSupplyOption := "No, I want the supply of the native token be hard-capped. (Native Minter Precompile OFF)" - dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens. (Native Minter Precompile ON)" + fixedSupplyOption := "No, I want the supply of the native tokens be hard-capped" + dynamicSupplyOption := "Yes, I want to be able to mint additional the native tokens (Native Minter Precompile ON)" options = []string{fixedSupplyOption, dynamicSupplyOption} option, err = app.Prompt.CaptureList( - "Allow minting new native Tokens? (Native Minter Precompile)", + "Allow minting of new native tokens?", options, ) if err != nil { @@ -279,14 +279,14 @@ func promptGasToken( } case explainOption: ux.Logger.PrintToUser("Every blockchain uses a token to manage access to its limited resources. For example, ETH is the native token of Ethereum, and AVAX is the native token of the Avalanche C-Chain. Users pay transaction fees with these tokens. If demand exceeds capacity, transaction fees increase, requiring users to pay more tokens for their transactions.") - ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("") ux.Logger.PrintToUser(logging.Bold.Wrap("The blockchain's native token")) ux.Logger.PrintToUser("Each blockchain on Avalanche has its own transaction fee token. To issue transactions users don't need to acquire ETH or AVAX and therefore the transaction fees are completely isolated.") - ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("") ux.Logger.PrintToUser(logging.Bold.Wrap("A token from another blockchain")) - ux.Logger.PrintToUser("You can use an ERC-20 token (e.g., USDC, WETH) or the native token (e.g., AVAX) of another blockchain within the Avalanche network as the transaction fee token. This is achieved through a bridge contract and the Native Minter Precompile. When a user bridges a token from another blockchain, it is locked on the home chain, a message is relayed to the Subnet, and the token is minted to the sender’s account.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter is required and activated automatically.") + ux.Logger.PrintToUser("Use an ERC-20 token (USDC, WETH, etc.) or the native token (e.g. AVAX) of another blockchain within the Avalanche network as the transaction fee token.") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("If a token from another blockchain is used, the interoperability protocol Teleporter will be activated automatically. For more info on Teleporter, visit: https://github.com/ava-labs/teleporter") continue } break @@ -376,21 +376,21 @@ func promptFeeConfig( return SubnetEVMGenesisParams{}, err } case explainOption: - ux.Logger.PrintToUser("The two gas fee variables that have the largest impact on performance are the gas limit, the maximum amount of gas that fits in a block, and the gas target, the expected amount of gas consumed in a rolling ten-second period.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("By increasing the gas limit, you can fit more transactions into a single block which in turn increases your max throughput. Increasing the gas target has the same effect; if the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") - ux.Logger.PrintToUser(" ") - ux.Logger.PrintToUser("There is a long-term risk of increasing your gas parameters. By allowing more transactions to occur on your network, the network state will increase at a faster rate, meaning infrastructure costs and requirements will increase.") + ux.Logger.PrintToUser("Gas limit is the maximum amount of gas that fits in a block and gas target is the expected amount of gas consumed in a rolling ten-second period") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("Higher gas limit and higher gas target both increase your max throughput. If the targeted amount of gas is not consumed, the dynamic fee algorithm will decrease the base fee until it reaches the minimum.") + ux.Logger.PrintToUser("") + ux.Logger.PrintToUser("By allowing more transactions to occur on your network, the network state will increase at a faster rate, which will lead to higher infrastructure costs.") continue } break } - dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block. (Fee Manager Precompile OFF)" - changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production. (Fee Manager Precompile ON)" + dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block" + changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production (Fee Manager Precompile ON)" options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Should transaction fees be adjustable without a network upgrade? (Fee Manager Precompile)", + "Should transaction fees be adjustable without a network upgrade?", options, ) if err != nil { @@ -408,17 +408,17 @@ func promptFeeConfig( } params.enableFeeManagerPrecompile = true case explainOption: - ux.Logger.PrintToUser("The Fee Manager Precompile enables you to give certain account the right to change the fee parameters set in the previous step on the fly without a network upgrade. This list can be dynamically changed by calling the precompile.") + ux.Logger.PrintToUser("The Fee Manager Precompile enables specified accounts to change the fee parameters without a network upgrade.") continue } break } - burnFees := "Yes, I am fine with transaction fees being burned (Reward Manager Precompile OFF)" + burnFees := "Yes, I want the transaction fees to be burned" distributeFees := "No, I want to customize accumulated transaction fees distribution (Reward Manager Precompile ON)" options = []string{burnFees, distributeFees, explainOption} for { option, err := app.Prompt.CaptureList( - "By default, all transaction fees on Avalanche are burned (sent to a blackhole address). Should the fees be burned??", + "Do you want the transaction fees to be burned (sent to a blackhole address)? All transaction fees on Avalanche are burned by default", options, ) if err != nil { @@ -436,7 +436,7 @@ func promptFeeConfig( } params.enableRewardManagerPrecompile = true case explainOption: - ux.Logger.PrintToUser("The fee reward mechanism can be configured with a stateful precompile contract called the RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") + ux.Logger.PrintToUser("Fee reward mechanism is configured with stateful precompile contract RewardManager. The configuration can include burning fees, sending fees to a predefined address, or enabling fees to be collected by block producers. For more info, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#changing-fee-reward-mechanisms") continue } break @@ -462,12 +462,12 @@ func PromptInteropt( case useExternalGasToken: return true, nil default: - interoperatingBlockchainOption := "Yes, I want my blockchain to be able to interoperate with other blockchains and the C-Chain" + interoperatingBlockchainOption := "Yes, I want to enable my blockchain to interoperate with other blockchains and the C-Chain" isolatedBlockchainOption := "No, I want to run my blockchain isolated" options := []string{interoperatingBlockchainOption, isolatedBlockchainOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to connect your blockchain with other blockchains or the C-Chain? (Deploy Teleporter along with its Registry)", + "Do you want to connect your blockchain with other blockchains or the C-Chain?", options, ) if err != nil { @@ -479,7 +479,7 @@ func PromptInteropt( case interoperatingBlockchainOption: return true, nil case explainOption: - ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains with the VM-agnostic Avalanche Warp Messaging protocol (AWM). Teleporter is a messaging protocol built on top of AWM that provides a developer-friendly interface for sending and receiving cross-chain messages to and from EVM-compatible blockchains. This communication protocol can be used for bridges and other protocols.") + ux.Logger.PrintToUser("Avalanche enables native interoperability between blockchains through Avalanche Warp Messaging protocol (AWM). For more information about interoperability in Avalanche, please visit: https://docs.avax.network/build/cross-chain/awm/overview") continue } } @@ -498,23 +498,23 @@ func promptPermissioning( var cancel bool noOption := "No" yesOption := "Yes" - options := []string{noOption, yesOption, explainOption} + options := []string{yesOption, noOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to add permissioning to your blockchain?", + "Do you want to enable anyone to issue transactions and deploy smart contracts to your blockchain?", options, ) if err != nil { return SubnetEVMGenesisParams{}, err } switch option { - case yesOption: - anyoneCanSubmitTransactionsOption := "No, I want anyone to be able to issue transactions on my blockchain. (Transaction Allow List OFF)" - approvedCanSubmitTransactionsOption := "Yes, I want only approved addresses to issue transactions on my blockchain. (Transaction Allow List ON)" + case noOption: + anyoneCanSubmitTransactionsOption := "Yes, I want anyone to be able to issue transactions on my blockchain" + approvedCanSubmitTransactionsOption := "No, I want only approved addresses to issue transactions on my blockchain (Transaction Allow List ON)" options := []string{anyoneCanSubmitTransactionsOption, approvedCanSubmitTransactionsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to issue transactions? (Transaction Allow List Precompile)", + "Do you want to enable anyone to issue transactions to your blockchain?", options, ) if err != nil { @@ -532,18 +532,18 @@ func promptPermissioning( params.enableTransactionPrecompile = true case explainOption: ux.Logger.PrintToUser("The Transaction Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to submit transactions to your blockchain. This list can be dynamically changed by calling the precompile.") - ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("") ux.Logger.PrintToUser("This feature is useful for permissioning your blockchain and lets you easiliy implement KYC measures. Only authorized users can send transactions or deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-who-can-submit-transactions.") continue } break } - anyoneCanDeployContractsOption := "No, I want anyone to be able to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List OFF)" - approvedCanDeployContractsOption := "Yes, I want only approved addresses to deploy smart contracts on my blockchain. (Smart Contract Deployer Allow List ON)" + anyoneCanDeployContractsOption := "Yes, I want anyone to be able to deploy smart contracts on my blockchain" + approvedCanDeployContractsOption := "No, I want only approved addresses to deploy smart contracts on my blockchain (Smart Contract Deployer Allow List ON)" options = []string{anyoneCanDeployContractsOption, approvedCanDeployContractsOption, explainOption} for { option, err := app.Prompt.CaptureList( - "Do you want to allow only certain addresses to deploy smart contracts on your blockchain? (Smart Contract Deployer Allow List Precompile)", + "Do you want to enable anyone to deploy smart contracts on your blockchain?", options, ) if err != nil { @@ -561,7 +561,7 @@ func promptPermissioning( params.enableContractDeployerPrecompile = true case explainOption: ux.Logger.PrintToUser("While you may wish to allow anyone to interact with the contract on your blockchain to your blockchain, you may want to restrict who can deploy smart contracts and create dApps on your chain.") - ux.Logger.PrintToUser(" ") + ux.Logger.PrintToUser("") ux.Logger.PrintToUser("The Smart Contract Deployer Allow List is a precompile contract that allows you to specify a list of addresses that are allowed to deploy smart contracts on your blockchain. For more information, please visit: https://docs.avax.network/build/subnet/upgrade/customize-a-subnet#restricting-smart-contract-deployers.") continue } From 1922c6af170a95d6d8ce77b0d1853cf5186433ab Mon Sep 17 00:00:00 2001 From: felipemadero Date: Thu, 25 Jul 2024 14:20:10 -0300 Subject: [PATCH 63/63] Use avacloud gas limit settings + add dynamic fees/not dynamic fees option (#2050) * use avacloud values for standard gas control * nit * also change upgrade * fix defaults msg --- cmd/subnetcmd/upgradecmd/generate.go | 37 +++++++++++++++++----------- pkg/vm/evmPrompts.go | 32 +++++++++++++++++++++--- pkg/vm/evmSettings.go | 14 ++++++++--- pkg/vm/fees.go | 25 +++++++++++++++---- 4 files changed, 81 insertions(+), 27 deletions(-) diff --git a/cmd/subnetcmd/upgradecmd/generate.go b/cmd/subnetcmd/upgradecmd/generate.go index d1018bc70..860715851 100644 --- a/cmd/subnetcmd/upgradecmd/generate.go +++ b/cmd/subnetcmd/upgradecmd/generate.go @@ -378,10 +378,10 @@ func GetFeeConfig(config params.ChainConfig, useDefault bool) ( error, ) { const ( - useFast = "High disk use / High Throughput 5 mil gas/s" - useMedium = "Medium disk use / Medium Throughput 2 mil gas/s" - useSlow = "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" - customFee = "Customize fee config" + lowOption = "Low block size / Low Throughput 12 mil gas per block" + mediumOption = "Medium block size / Medium Throughput 15 mil gas per block (C-Chain's setting)" + highOption = "High block size / High Throughput 20 mil gas per block" + customFee = "Customize fee config" setGasLimit = "Set gas limit" setBlockRate = "Set target block rate" @@ -396,11 +396,12 @@ func GetFeeConfig(config params.ChainConfig, useDefault bool) ( config.FeeConfig = vm.StarterFeeConfig if useDefault { - config.FeeConfig.TargetGas = vm.LowTarget + config.FeeConfig.GasLimit = vm.LowGasLimit + config.FeeConfig.TargetGas = config.FeeConfig.TargetGas.Mul(config.FeeConfig.GasLimit, vm.NoDynamicFeesGasLimitToTargetGasFactor) return config, nil } - feeConfigOptions := []string{useSlow, useMedium, useFast, customFee} + feeConfigOptions := []string{lowOption, mediumOption, highOption, customFee} feeDefault, err := app.Prompt.CaptureList( "How would you like to set fees", @@ -410,16 +411,24 @@ func GetFeeConfig(config params.ChainConfig, useDefault bool) ( return config, err } + useDynamicFees := false + if feeDefault != customFee { + useDynamicFees, err = app.Prompt.CaptureYesNo("Do you want to enable dynamic fees?") + if err != nil { + return config, err + } + } + switch feeDefault { - case useFast: - config.FeeConfig.TargetGas = vm.HighTarget - return config, nil - case useMedium: - config.FeeConfig.TargetGas = vm.MediumTarget - return config, nil - case useSlow: - config.FeeConfig.TargetGas = vm.LowTarget + case lowOption: + vm.SetStandardGas(&config, vm.LowGasLimit, vm.LowTargetGas, useDynamicFees) return config, nil + case mediumOption: + vm.SetStandardGas(&config, vm.MediumGasLimit, vm.MediumTargetGas, useDynamicFees) + return config, err + case highOption: + vm.SetStandardGas(&config, vm.HighGasLimit, vm.HighTargetGas, useDynamicFees) + return config, err default: ux.Logger.PrintToUser("Customizing fee config") } diff --git a/pkg/vm/evmPrompts.go b/pkg/vm/evmPrompts.go index bd718d91d..0cc7e8439 100644 --- a/pkg/vm/evmPrompts.go +++ b/pkg/vm/evmPrompts.go @@ -36,6 +36,7 @@ type FeeConfig struct { lowThroughput bool mediumThroughput bool highThroughput bool + useDynamicFees bool gasLimit *big.Int blockRate *big.Int minBaseFee *big.Int @@ -260,7 +261,7 @@ func PromptDefaults( case specifyMyValuesOption: useDefaults = false case explainOption: - ux.Logger.PrintToUser("Subnet configuration default values:\n- Use latest Subnet-EVM release\n- Allocate 1 million tokens to a newly created key\n- Supply of the native token will be hard-capped\n- Set gas fee config as low throughput (12 mil gas per block)\n- Disable further adjustments in transaction fee configuration\n- Transaction fees are burned\n- Enable interoperation with other blockchains\n- Allow any user to deploy smart contracts, send transactions, and interact with your blockchain.") + ux.Logger.PrintToUser("Subnet configuration default values:\n- Use latest Subnet-EVM release\n- Allocate 1 million tokens to a newly created key\n- Supply of the native token will be hard-capped\n- Set gas fee config as low throughput (12 mil gas per block)\n- Use constant gas prices\n- Disable further adjustments in transaction fee configuration\n- Transaction fees are burned\n- Enable interoperation with other blockchains\n- Allow any user to deploy smart contracts, send transactions, and interact with your blockchain.") continue } break @@ -364,13 +365,14 @@ func promptFeeConfig( ) (SubnetEVMGenesisParams, error) { if useDefaults { params.feeConfig.lowThroughput = true + params.feeConfig.useDynamicFees = false return params, nil } var cancel bool customizeOption := "Customize fee config" - lowOption := "Low disk use / Low Throughput 1.5 mil gas/s (C-Chain's setting)" - mediumOption := "Medium disk use / Medium Throughput 2 mil gas/s" - highOption := "High disk use / High Throughput 5 mil gas/s" + lowOption := "Low block size / Low Throughput 12 mil gas per block" + mediumOption := "Medium block size / Medium Throughput 15 mil gas per block (C-Chain's setting)" + highOption := "High block size / High Throughput 20 mil gas per block" options := []string{lowOption, mediumOption, highOption, customizeOption, explainOption} for { option, err := app.Prompt.CaptureList( @@ -440,6 +442,28 @@ func promptFeeConfig( } break } + dontUseDynamicFeesOption := "No, I prefer to have constant gas prices" + useDynamicFeesOption := "Yes, I would like my blockchain to have dynamic fees" + options = []string{dontUseDynamicFeesOption, useDynamicFeesOption, explainOption} + for { + option, err := app.Prompt.CaptureList( + "Do you want dynamic fees on your blockchain?", + options, + ) + if err != nil { + return SubnetEVMGenesisParams{}, err + } + switch option { + case dontUseDynamicFeesOption: + params.feeConfig.useDynamicFees = false + case useDynamicFeesOption: + params.feeConfig.useDynamicFees = true + case explainOption: + ux.Logger.PrintToUser("By disabling dynamic fees you effectively make your gas fees constant. In that case, you may\nwant to have your own congestion control, by fully controlling activity on the chain.\nIf setting dynamic fees, gas fees will be automatically adjusted giving automatic congestion control.") + continue + } + break + } dontChangeFeeSettingsOption := "No, use the transaction fee configuration set in the genesis block" changeFeeSettingsOption := "Yes, allow adjustment of the transaction fee configuration as needed. Recommended for production (Fee Manager Precompile ON)" options = []string{dontChangeFeeSettingsOption, changeFeeSettingsOption, explainOption} diff --git a/pkg/vm/evmSettings.go b/pkg/vm/evmSettings.go index caa71e96e..f930585bf 100644 --- a/pkg/vm/evmSettings.go +++ b/pkg/vm/evmSettings.go @@ -18,13 +18,19 @@ const ( var ( Difficulty = big.NewInt(0) - LowTarget = big.NewInt(15_000_000) - MediumTarget = big.NewInt(20_000_000) - HighTarget = big.NewInt(50_000_000) + // current avacloud settings + LowGasLimit = big.NewInt(12_000_000) + MediumGasLimit = big.NewInt(15_000_000) // C-Chain value + HighGasLimit = big.NewInt(20_000_000) + LowTargetGas = big.NewInt(25_000_000) // ~ 2.1x of gas limit + MediumTargetGas = big.NewInt(45_000_000) // 3x of gas limit (also, 3x bigger than C-Chain) + HighTargetGas = big.NewInt(60_000_000) // 3x of gas limit + + NoDynamicFeesGasLimitToTargetGasFactor = big.NewInt(5) // This is the current c-chain gas config StarterFeeConfig = commontype.FeeConfig{ - GasLimit: big.NewInt(8_000_000), + GasLimit: big.NewInt(15_000_000), MinBaseFee: big.NewInt(25_000_000_000), TargetGas: big.NewInt(15_000_000), BaseFeeChangeDenominator: big.NewInt(36), diff --git a/pkg/vm/fees.go b/pkg/vm/fees.go index bd5be3376..5598af492 100644 --- a/pkg/vm/fees.go +++ b/pkg/vm/fees.go @@ -4,10 +4,25 @@ package vm import ( + "math/big" + "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/params" ) +func SetStandardGas( + config *params.ChainConfig, + gasLimit *big.Int, + targetGas *big.Int, + useDynamicFees bool, +) { + config.FeeConfig.GasLimit = gasLimit + config.FeeConfig.TargetGas = targetGas + if !useDynamicFees { + config.FeeConfig.TargetGas = config.FeeConfig.TargetGas.Mul(config.FeeConfig.GasLimit, NoDynamicFeesGasLimitToTargetGasFactor) + } +} + func setFeeConfig( params SubnetEVMGenesisParams, config *params.ChainConfig, @@ -15,12 +30,12 @@ func setFeeConfig( config.FeeConfig = StarterFeeConfig switch { - case params.feeConfig.highThroughput: - config.FeeConfig.TargetGas = HighTarget - case params.feeConfig.mediumThroughput: - config.FeeConfig.TargetGas = MediumTarget case params.feeConfig.lowThroughput: - config.FeeConfig.TargetGas = LowTarget + SetStandardGas(config, LowGasLimit, LowTargetGas, params.feeConfig.useDynamicFees) + case params.feeConfig.mediumThroughput: + SetStandardGas(config, MediumGasLimit, MediumTargetGas, params.feeConfig.useDynamicFees) + case params.feeConfig.highThroughput: + SetStandardGas(config, HighGasLimit, HighTargetGas, params.feeConfig.useDynamicFees) default: setCustomFeeConfig(params, config) }