mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
814 lines
25 KiB
Go
814 lines
25 KiB
Go
// Copyright (C) 2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package tokentransferrercmd
|
|
|
|
import (
|
|
_ "embed"
|
|
"fmt"
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
cmdflags "github.com/luxfi/cli/v2/cmd/flags"
|
|
"github.com/luxfi/cli/v2/pkg/application"
|
|
"github.com/luxfi/cli/v2/pkg/cobrautils"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/contract"
|
|
"github.com/luxfi/cli/v2/pkg/warp"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/networkoptions"
|
|
"github.com/luxfi/cli/v2/pkg/precompiles"
|
|
"github.com/luxfi/cli/v2/pkg/prompts"
|
|
"github.com/luxfi/cli/v2/pkg/utils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
sdkutils "github.com/luxfi/cli/v2/sdk/utils"
|
|
"github.com/luxfi/node/v2/v2/utils/logging"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
type HomeFlags struct {
|
|
chainFlags contract.ChainSpec
|
|
homeAddress string
|
|
native bool
|
|
erc20Address string
|
|
privateKeyFlags contract.PrivateKeyFlags
|
|
RPCEndpoint string
|
|
}
|
|
|
|
type RemoteFlags struct {
|
|
chainFlags contract.ChainSpec
|
|
native bool
|
|
removeMinterAdmin bool
|
|
privateKeyFlags contract.PrivateKeyFlags
|
|
RPCEndpoint string
|
|
Decimals uint8
|
|
}
|
|
|
|
type DeployFlags struct {
|
|
Network networkoptions.NetworkFlags
|
|
homeFlags HomeFlags
|
|
remoteFlags RemoteFlags
|
|
version string
|
|
}
|
|
|
|
var deployFlags DeployFlags
|
|
|
|
// Lux Warp deploy
|
|
func NewDeployCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "deploy",
|
|
Short: "Deploys a Token Transferrer into a given Network and Subnets",
|
|
Long: "Deploys a Token Transferrer into a given Network and Subnets",
|
|
RunE: deploy,
|
|
Args: cobrautils.ExactArgs(0),
|
|
}
|
|
networkoptions.AddNetworkFlagsToCmd(cmd, &deployFlags.Network, true, networkoptions.DefaultSupportedNetworkOptions)
|
|
deployFlags.homeFlags.chainFlags.SetFlagNames(
|
|
"home-blockchain",
|
|
"c-chain-home",
|
|
"",
|
|
"",
|
|
"",
|
|
)
|
|
deployFlags.homeFlags.chainFlags.AddToCmd(cmd, "set the Transferrer's Home Chain into %s")
|
|
deployFlags.remoteFlags.chainFlags.SetFlagNames(
|
|
"remote-blockchain",
|
|
"c-chain-remote",
|
|
"",
|
|
"",
|
|
"",
|
|
)
|
|
deployFlags.remoteFlags.chainFlags.AddToCmd(cmd, "set the Transferrer's Remote Chain into %s")
|
|
cmd.Flags().BoolVar(&deployFlags.homeFlags.native, "deploy-native-home", false, "deploy a Transferrer Home for the Chain's Native Token")
|
|
cmd.Flags().StringVar(&deployFlags.homeFlags.erc20Address, "deploy-erc20-home", "", "deploy a Transferrer Home for the given Chain's ERC20 Token")
|
|
cmd.Flags().StringVar(&deployFlags.homeFlags.homeAddress, "use-home", "", "use the given Transferrer's Home Address")
|
|
cmd.Flags().StringVar(&deployFlags.version, "version", constants.WarpVersion, "tag/branch/commit of Lux Warp to be used")
|
|
cmd.Flags().BoolVar(&deployFlags.remoteFlags.native, "deploy-native-remote", false, "deploy a Transferrer Remote for the Chain's Native Token")
|
|
cmd.Flags().BoolVar(&deployFlags.remoteFlags.removeMinterAdmin, "remove-minter-admin", false, "remove the native minter precompile admin found on remote blockchain genesis")
|
|
deployFlags.homeFlags.privateKeyFlags.SetFlagNames("home-private-key", "home-key", "home-genesis-key")
|
|
deployFlags.homeFlags.privateKeyFlags.AddToCmd(cmd, "to deploy Transferrer Home")
|
|
deployFlags.remoteFlags.privateKeyFlags.SetFlagNames("remote-private-key", "remote-key", "remote-genesis-key")
|
|
deployFlags.remoteFlags.privateKeyFlags.AddToCmd(cmd, "to deploy Transferrer Remote")
|
|
cmd.Flags().StringVar(&deployFlags.homeFlags.RPCEndpoint, "home-rpc", "", "use the given RPC URL to connect to the home blockchain")
|
|
cmd.Flags().StringVar(&deployFlags.remoteFlags.RPCEndpoint, "remote-rpc", "", "use the given RPC URL to connect to the remote blockchain")
|
|
cmd.Flags().Uint8Var(&deployFlags.remoteFlags.Decimals, "remote-token-decimals", 0, "use the given number of token decimals for the Transferrer Remote [defaults to token home's decimals (18 for a new wrapped native home token)]")
|
|
return cmd
|
|
}
|
|
|
|
func deploy(_ *cobra.Command, args []string) error {
|
|
return CallDeploy(args, deployFlags)
|
|
}
|
|
|
|
func getHomeKeyAndAddress(app *application.Lux, network models.Network, homeFlags HomeFlags) (string, string, error) {
|
|
// First check if there is a genesis key able to be used.
|
|
genesisAddress, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey(
|
|
app,
|
|
network,
|
|
homeFlags.chainFlags,
|
|
)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
homeKey, err := homeFlags.privateKeyFlags.GetPrivateKey(app, genesisPrivateKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
if homeKey == "" {
|
|
// Propmt for the key to be used.
|
|
// Note that this key must have enough funds to cover gas cost on the
|
|
// home chain, and also enough of the token on the home chain to collateralize
|
|
// the remote chain if it is a native token remote.
|
|
homeKey, err = prompts.PromptPrivateKey(
|
|
app.Prompt,
|
|
"pay for home deployment fees, and collateralization (if necessary)",
|
|
app.GetKeyDir(),
|
|
app.GetKey,
|
|
genesisAddress,
|
|
genesisPrivateKey,
|
|
)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
}
|
|
|
|
// Calculate the address for the key.
|
|
pk, err := crypto.HexToECDSA(homeKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
homeKeyAddress := crypto.PubkeyToAddress(pk.PublicKey).Hex()
|
|
|
|
return homeKey, homeKeyAddress, nil
|
|
}
|
|
|
|
func CallDeploy(_ []string, flags DeployFlags) error {
|
|
if !warp.FoundryIsInstalled() {
|
|
if err := warp.InstallFoundry(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
network, err := networkoptions.GetNetworkFromCmdLineFlags(
|
|
app,
|
|
"On what Network do you want to deploy the Transferrer?",
|
|
flags.Network,
|
|
true,
|
|
false,
|
|
networkoptions.DefaultSupportedNetworkOptions,
|
|
"",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// flags exclusiveness
|
|
if err := flags.homeFlags.chainFlags.CheckMutuallyExclusiveFields(); err != nil {
|
|
return err
|
|
}
|
|
if !cmdflags.EnsureMutuallyExclusive([]bool{
|
|
flags.homeFlags.homeAddress != "",
|
|
flags.homeFlags.erc20Address != "",
|
|
flags.homeFlags.native,
|
|
}) {
|
|
return fmt.Errorf("--deploy-native-home, --deploy-erc20-home, and --use-home are mutually exclusive flags")
|
|
}
|
|
if err := flags.remoteFlags.chainFlags.CheckMutuallyExclusiveFields(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Home Chain Prompts
|
|
if !flags.homeFlags.chainFlags.Defined() {
|
|
prompt := "Where is the Token origin?"
|
|
if cancel, err := contract.PromptChain(app, network, prompt, "", &flags.homeFlags.chainFlags); err != nil {
|
|
return err
|
|
} else if cancel {
|
|
return nil
|
|
}
|
|
}
|
|
homeRPCEndpoint := flags.homeFlags.RPCEndpoint
|
|
if homeRPCEndpoint == "" {
|
|
homeRPCEndpoint, _, err = contract.GetBlockchainEndpoints(app, network, flags.homeFlags.chainFlags, true, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.PrintToUser(logging.Yellow.Wrap("Home RPC Endpoint: %s"), homeRPCEndpoint)
|
|
}
|
|
|
|
// Home Chain Validations
|
|
if flags.homeFlags.chainFlags.BlockchainName != "" {
|
|
if err := validateSubnet(network, flags.homeFlags.chainFlags.BlockchainName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Home Contract Prompts
|
|
if flags.homeFlags.homeAddress == "" && flags.homeFlags.erc20Address == "" && !flags.homeFlags.native {
|
|
nativeTokenSymbol, err := getNativeTokenSymbol(
|
|
flags.homeFlags.chainFlags.BlockchainName,
|
|
flags.homeFlags.chainFlags.CChain,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
prompt := "What kind of token do you want to be able to transfer?"
|
|
popularOption := "A popular token (e.g. WLUX, USDC, ...) (recommended)"
|
|
homeDeployedOption := "A token that already has a Home deployed (recommended)"
|
|
deployNewHomeOption := "Deploy a new Home for the token"
|
|
explainOption := "Explain the difference"
|
|
goBackOption := "Go Back"
|
|
homeChain := "C-Chain"
|
|
if !flags.homeFlags.chainFlags.CChain {
|
|
homeChain = flags.homeFlags.chainFlags.BlockchainName
|
|
}
|
|
popularTokensInfo, err := GetPopularTokensInfo(network, homeChain)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
popularTokensDesc := sdkutils.Map(
|
|
popularTokensInfo,
|
|
func(i PopularTokenInfo) string {
|
|
return i.Desc()
|
|
},
|
|
)
|
|
options := []string{popularOption, homeDeployedOption, deployNewHomeOption, explainOption}
|
|
if len(popularTokensDesc) == 0 {
|
|
options = []string{homeDeployedOption, deployNewHomeOption, explainOption}
|
|
}
|
|
for {
|
|
option, err := app.Prompt.CaptureList(
|
|
prompt,
|
|
options,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch option {
|
|
case popularOption:
|
|
options := popularTokensDesc
|
|
options = append(options, goBackOption)
|
|
option, err := app.Prompt.CaptureList(
|
|
"Choose Token",
|
|
options,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if option == goBackOption {
|
|
continue
|
|
}
|
|
p := utils.Find(popularTokensInfo, func(p PopularTokenInfo) bool { return p.Desc() == option })
|
|
if p == nil {
|
|
return fmt.Errorf("expected to have found a popular token from option")
|
|
}
|
|
flags.homeFlags.homeAddress = p.TransferrerHomeAddress
|
|
case homeDeployedOption:
|
|
addr, err := app.Prompt.CaptureAddress(
|
|
"Enter the address of the Home",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
flags.homeFlags.homeAddress = addr.Hex()
|
|
case deployNewHomeOption:
|
|
nativeOption := "The native token " + nativeTokenSymbol
|
|
erc20Option := "An ERC-20 token"
|
|
options := []string{nativeOption, erc20Option}
|
|
option, err := app.Prompt.CaptureList(
|
|
"What kind of token do you want to deploy the Home for?",
|
|
options,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch option {
|
|
case nativeOption:
|
|
flags.homeFlags.native = true
|
|
case erc20Option:
|
|
erc20TokenAddr, err := app.Prompt.CaptureAddress(
|
|
"Enter the address of the ERC-20 Token",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
flags.homeFlags.erc20Address = erc20TokenAddr.Hex()
|
|
if p := utils.Find(popularTokensInfo, func(p PopularTokenInfo) bool { return p.TokenContractAddress == erc20TokenAddr.Hex() }); p != nil {
|
|
ux.Logger.PrintToUser("There already is a Token Home for %s deployed on %s.", p.TokenName, homeChain)
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("Home Address: %s", p.TransferrerHomeAddress)
|
|
useTheExistingHomeOption := "Yes, use the existing Home (recommended)"
|
|
deployANewHupOption := "No, deploy my own Home"
|
|
options := []string{useTheExistingHomeOption, deployANewHupOption, explainOption}
|
|
option, err := app.Prompt.CaptureList(
|
|
"Do you want to use the existing Home?",
|
|
options,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch option {
|
|
case useTheExistingHomeOption:
|
|
flags.homeFlags.homeAddress = p.TransferrerHomeAddress
|
|
flags.homeFlags.erc20Address = ""
|
|
case deployANewHupOption:
|
|
case explainOption:
|
|
ux.Logger.PrintToUser("There is already a Transferrer Home deployed for the popular token %s on %s.",
|
|
p.TokenName,
|
|
homeChain,
|
|
)
|
|
ux.Logger.PrintToUser("Connect to that Home to participate in standard cross chain transfers")
|
|
ux.Logger.PrintToUser("for the token, including transfers to any of the registered Remote subnets.")
|
|
ux.Logger.PrintToUser("Deploy a new Home if wanting to have isolated cross chain transfers for")
|
|
ux.Logger.PrintToUser("your application, or if wanting to provide a new Transferrer alternative")
|
|
ux.Logger.PrintToUser("for the token.")
|
|
}
|
|
}
|
|
}
|
|
case explainOption:
|
|
ux.Logger.PrintToUser("An Lux Warp Transfer consists of one Home and at least one but possibly many Remotes.")
|
|
ux.Logger.PrintToUser("The Home manages the asset to be shared to Remote instances. It lives on the Subnet")
|
|
ux.Logger.PrintToUser("where the asset exists")
|
|
ux.Logger.PrintToUser("The Remotes live on the other Subnets that want to import the asset managed by the Home.")
|
|
ux.Logger.PrintToUser("")
|
|
if len(popularTokensDesc) != 0 {
|
|
ux.Logger.PrintToUser("A popular token of a subnet is assumed to already have a Home Deployed. In this case")
|
|
ux.Logger.PrintToUser("the Home parameters will be automatically obtained, and a new Remote will be created on")
|
|
ux.Logger.PrintToUser("the other Subnet, to access the popular token.")
|
|
}
|
|
ux.Logger.PrintToUser("For a token that already has a Home deployed, the Home parameters will be prompted,")
|
|
ux.Logger.PrintToUser("and a new Remote will be created on the other Subnet to access that token.")
|
|
ux.Logger.PrintToUser("If deploying a new Home for the token, the token parameters will be prompted,")
|
|
ux.Logger.PrintToUser("and both a new Home will be created on the token Subnet, and a new Remote will be created")
|
|
ux.Logger.PrintToUser("on the other Subnet to access that token.")
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
// Get the key to be used to deploy the token home contract and collateralize the remote
|
|
// in the case that it is a native token remote.
|
|
homeKey, homeKeyAddress, err := getHomeKeyAndAddress(app, network, flags.homeFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Home Contract Validations
|
|
if flags.homeFlags.homeAddress != "" {
|
|
if err := prompts.ValidateAddress(flags.homeFlags.homeAddress); err != nil {
|
|
return fmt.Errorf("failure validating %s: %w", flags.homeFlags.homeAddress, err)
|
|
}
|
|
}
|
|
if flags.homeFlags.erc20Address != "" {
|
|
if err := prompts.ValidateAddress(flags.homeFlags.erc20Address); err != nil {
|
|
return fmt.Errorf("failure validating %s: %w", flags.homeFlags.erc20Address, err)
|
|
}
|
|
}
|
|
|
|
// Remote Chain Prompts
|
|
if !flags.remoteFlags.chainFlags.Defined() {
|
|
prompt := "Where should the token be available as an ERC-20?"
|
|
if flags.remoteFlags.native {
|
|
prompt = "Where should the token be available as a Native Token?"
|
|
}
|
|
flags.remoteFlags.chainFlags.SetEnabled(
|
|
true,
|
|
!flags.homeFlags.chainFlags.CChain,
|
|
false,
|
|
false,
|
|
false,
|
|
)
|
|
if cancel, err := contract.PromptChain(
|
|
app,
|
|
network,
|
|
prompt,
|
|
flags.homeFlags.chainFlags.BlockchainName,
|
|
&flags.remoteFlags.chainFlags,
|
|
); err != nil {
|
|
return err
|
|
} else if cancel {
|
|
return nil
|
|
}
|
|
}
|
|
remoteRPCEndpoint := flags.remoteFlags.RPCEndpoint
|
|
if remoteRPCEndpoint == "" {
|
|
remoteRPCEndpoint, _, err = contract.GetBlockchainEndpoints(app, network, flags.remoteFlags.chainFlags, true, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.PrintToUser(logging.Yellow.Wrap("Remote RPC Endpoint: %s"), remoteRPCEndpoint)
|
|
}
|
|
|
|
genesisAddress, genesisPrivateKey, err := contract.GetEVMSubnetPrefundedKey(
|
|
app,
|
|
network,
|
|
flags.remoteFlags.chainFlags,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteKey, err := flags.remoteFlags.privateKeyFlags.GetPrivateKey(app, genesisPrivateKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if remoteKey == "" {
|
|
remoteKey, err = prompts.PromptPrivateKey(
|
|
app.Prompt,
|
|
"pay for remote deploy fees",
|
|
app.GetKeyDir(),
|
|
app.GetKey,
|
|
genesisAddress,
|
|
genesisPrivateKey,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
pk, err := crypto.HexToECDSA(remoteKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteKeyAddress := crypto.PubkeyToAddress(pk.PublicKey).Hex()
|
|
|
|
// Remote Chain Validations
|
|
if flags.remoteFlags.chainFlags.BlockchainName != "" {
|
|
if err := validateSubnet(network, flags.remoteFlags.chainFlags.BlockchainName); err != nil {
|
|
return err
|
|
}
|
|
if flags.remoteFlags.chainFlags.BlockchainName == flags.homeFlags.chainFlags.BlockchainName {
|
|
return fmt.Errorf("trying to make an Transferrer were home and remote are on the same subnet")
|
|
}
|
|
}
|
|
if flags.remoteFlags.chainFlags.CChain && flags.homeFlags.chainFlags.CChain {
|
|
return fmt.Errorf("trying to make an Transferrer were home and remote are on the same subnet")
|
|
}
|
|
|
|
// Checkout minter availability for native remote before doing something else
|
|
remoteBlockchainDesc, err := contract.GetBlockchainDesc(flags.remoteFlags.chainFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var (
|
|
remoteMinterManagerPrivKey, remoteMinterManagerAddress string
|
|
remoteMinterManagerIsAdmin bool
|
|
)
|
|
if flags.remoteFlags.native {
|
|
var remoteMinterAdminFound, remoteManagedMinterAdmin bool
|
|
remoteMinterAdminFound, remoteManagedMinterAdmin, _, remoteMinterManagerAddress, remoteMinterManagerPrivKey, err = contract.GetEVMSubnetGenesisNativeMinterAdmin(
|
|
app,
|
|
network,
|
|
flags.remoteFlags.chainFlags,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !remoteManagedMinterAdmin {
|
|
remoteMinterAdminAddress := remoteMinterManagerAddress
|
|
var remoteMinterManagerFound, remoteManagedMinterManager bool
|
|
remoteMinterManagerFound, remoteManagedMinterManager, _, remoteMinterManagerAddress, remoteMinterManagerPrivKey, err = contract.GetEVMSubnetGenesisNativeMinterManager(
|
|
app,
|
|
network,
|
|
flags.remoteFlags.chainFlags,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !remoteMinterManagerFound {
|
|
return fmt.Errorf("there is no native minter precompile admin or manager on %s", remoteBlockchainDesc)
|
|
}
|
|
if !remoteManagedMinterManager {
|
|
if remoteMinterAdminFound {
|
|
ux.Logger.PrintToUser("no managed key found for native minter admin %s on %s. add a CLI key for it using 'lux key create --file'", remoteMinterAdminAddress, remoteBlockchainDesc)
|
|
}
|
|
return fmt.Errorf("no managed key found for native minter manager %s on %s. add a CLI key for it using 'lux key create --file'", remoteMinterManagerAddress, remoteBlockchainDesc)
|
|
}
|
|
} else {
|
|
remoteMinterManagerIsAdmin = true
|
|
}
|
|
}
|
|
|
|
// Setup Contracts
|
|
ux.Logger.PrintToUser("Downloading Lux Warp Contracts")
|
|
if err := warp.DownloadRepo(app, flags.version); err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.PrintToUser("Compiling Lux Warp Contracts")
|
|
if err := warp.BuildContracts(app); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Home Deploy
|
|
warpSrcDir, err := warp.RepoDir(app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var homeAddress common.Address
|
|
// TODO: need registry address, manager address, private key for the home chain (academy for testnet)
|
|
homeBlockchainID, err := contract.GetBlockchainID(app, network, flags.homeFlags.chainFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
homeRegistryAddress, _, err := contract.GetWarpInfo(app, network, flags.homeFlags.chainFlags, true, false, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if flags.homeFlags.homeAddress != "" {
|
|
homeAddress = common.HexToAddress(flags.homeFlags.homeAddress)
|
|
}
|
|
if flags.homeFlags.erc20Address != "" {
|
|
tokenHomeAddress := common.HexToAddress(flags.homeFlags.erc20Address)
|
|
tokenHomeDecimals, err := warp.GetTokenDecimals(
|
|
homeRPCEndpoint,
|
|
tokenHomeAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
homeAddress, err = warp.DeployERC20Home(
|
|
warpSrcDir,
|
|
homeRPCEndpoint,
|
|
homeKey,
|
|
common.HexToAddress(homeRegistryAddress),
|
|
common.HexToAddress(homeKeyAddress),
|
|
tokenHomeAddress,
|
|
tokenHomeDecimals,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failure deploying ERC20 Home: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Home Deployed to %s", homeRPCEndpoint)
|
|
ux.Logger.PrintToUser("Home Address: %s", homeAddress)
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
if flags.homeFlags.native {
|
|
nativeTokenSymbol, err := getNativeTokenSymbol(
|
|
flags.homeFlags.chainFlags.BlockchainName,
|
|
flags.homeFlags.chainFlags.CChain,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
wrappedNativeTokenAddress, err := warp.DeployWrappedNativeToken(
|
|
warpSrcDir,
|
|
homeRPCEndpoint,
|
|
homeKey,
|
|
nativeTokenSymbol,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failure deploying Wrapped Native Token: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Wrapped Native Token Deployed to %s", homeRPCEndpoint)
|
|
ux.Logger.PrintToUser("%s Address: %s", nativeTokenSymbol, wrappedNativeTokenAddress)
|
|
ux.Logger.PrintToUser("")
|
|
homeAddress, err = warp.DeployNativeHome(
|
|
warpSrcDir,
|
|
homeRPCEndpoint,
|
|
homeKey,
|
|
common.HexToAddress(homeRegistryAddress),
|
|
common.HexToAddress(homeKeyAddress),
|
|
wrappedNativeTokenAddress,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failure deploying Native Home: %w", err)
|
|
}
|
|
ux.Logger.PrintToUser("Home Deployed to %s", homeRPCEndpoint)
|
|
ux.Logger.PrintToUser("Home Address: %s", homeAddress)
|
|
ux.Logger.PrintToUser("")
|
|
}
|
|
|
|
// Remote Deploy
|
|
remoteBlockchainID, err := contract.GetBlockchainID(app, network, flags.remoteFlags.chainFlags)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteRegistryAddress, _, err := contract.GetWarpInfo(app, network, flags.remoteFlags.chainFlags, true, false, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var (
|
|
remoteAddress common.Address
|
|
remoteSupply *big.Int
|
|
)
|
|
|
|
// get token home symbol, name, decimals
|
|
endpointKind, err := warp.GetEndpointKind(homeRPCEndpoint, homeAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var tokenHomeAddress common.Address
|
|
switch endpointKind {
|
|
case warp.ERC20TokenHome:
|
|
tokenHomeAddress, err = warp.ERC20TokenHomeGetTokenAddress(homeRPCEndpoint, homeAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case warp.NativeTokenHome:
|
|
tokenHomeAddress, err = warp.NativeTokenHomeGetTokenAddress(homeRPCEndpoint, homeAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("unsupported warp endpoint kind %d", endpointKind)
|
|
}
|
|
tokenHomeSymbol, tokenHomeName, _, err := warp.GetTokenParams(
|
|
homeRPCEndpoint,
|
|
tokenHomeAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
homeDecimals, err := warp.TokenHomeGetDecimals(homeRPCEndpoint, homeAddress)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !flags.remoteFlags.native {
|
|
// we default token remote decimals to be the same as token home decimals,
|
|
// but allow to be overridden by a user's provided flag
|
|
remoteDecimals := homeDecimals
|
|
if flags.remoteFlags.Decimals != 0 {
|
|
remoteDecimals = flags.remoteFlags.Decimals
|
|
}
|
|
remoteAddress, err = warp.DeployERC20Remote(
|
|
warpSrcDir,
|
|
remoteRPCEndpoint,
|
|
remoteKey,
|
|
common.HexToAddress(remoteRegistryAddress),
|
|
common.HexToAddress(remoteKeyAddress),
|
|
homeBlockchainID,
|
|
homeAddress,
|
|
homeDecimals,
|
|
tokenHomeName,
|
|
tokenHomeSymbol,
|
|
remoteDecimals,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failure deploying ERC20 Remote: %w", err)
|
|
}
|
|
} else {
|
|
nativeTokenSymbol, err := getNativeTokenSymbol(
|
|
flags.remoteFlags.chainFlags.BlockchainName,
|
|
flags.remoteFlags.chainFlags.CChain,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteSupply, err = contract.GetEVMSubnetGenesisSupply(
|
|
app,
|
|
network,
|
|
flags.remoteFlags.chainFlags,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
remoteAddress, err = warp.DeployNativeRemote(
|
|
warpSrcDir,
|
|
remoteRPCEndpoint,
|
|
remoteKey,
|
|
common.HexToAddress(remoteRegistryAddress),
|
|
common.HexToAddress(remoteKeyAddress),
|
|
homeBlockchainID,
|
|
homeAddress,
|
|
homeDecimals,
|
|
nativeTokenSymbol,
|
|
remoteSupply,
|
|
big.NewInt(0),
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("failure deploying Native Remote: %w", err)
|
|
}
|
|
}
|
|
ux.Logger.PrintToUser("Remote Deployed to %s", remoteRPCEndpoint)
|
|
ux.Logger.PrintToUser("Remote Address: %s", remoteAddress)
|
|
|
|
if err := warp.RegisterRemote(
|
|
remoteRPCEndpoint,
|
|
remoteKey,
|
|
remoteAddress,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
checkInterval := 100 * time.Millisecond
|
|
checkTimeout := 10 * time.Second
|
|
t0 := time.Now()
|
|
var collateralNeeded *big.Int
|
|
for {
|
|
registeredRemote, err := warp.TokenHomeGetRegisteredRemote(
|
|
homeRPCEndpoint,
|
|
homeAddress,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if registeredRemote.Registered {
|
|
collateralNeeded = registeredRemote.CollateralNeeded
|
|
break
|
|
}
|
|
elapsed := time.Since(t0)
|
|
if elapsed > checkTimeout {
|
|
return fmt.Errorf("timeout waiting for remote endpoint registration")
|
|
}
|
|
time.Sleep(checkInterval)
|
|
}
|
|
|
|
// Collateralize the remote contract on the home contract if necessary
|
|
if collateralNeeded.Cmp(big.NewInt(0)) != 0 {
|
|
err = warp.TokenHomeAddCollateral(
|
|
homeRPCEndpoint,
|
|
homeAddress,
|
|
homeKey,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
collateralNeeded,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Check that the remote is collateralized on the home contract now.
|
|
registeredRemote, err := warp.TokenHomeGetRegisteredRemote(
|
|
homeRPCEndpoint,
|
|
homeAddress,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if registeredRemote.CollateralNeeded.Cmp(big.NewInt(0)) != 0 {
|
|
return fmt.Errorf("failure setting collateral in home endpoint: remaining collateral=%d", registeredRemote.CollateralNeeded)
|
|
}
|
|
}
|
|
|
|
if flags.remoteFlags.native {
|
|
ux.Logger.PrintToUser("Enabling native token remote contract to mint native tokens")
|
|
if err := precompiles.SetEnabled(
|
|
remoteRPCEndpoint,
|
|
precompiles.NativeMinterPrecompile,
|
|
remoteMinterManagerPrivKey,
|
|
remoteAddress,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Send a single token unit to report that the remote is collateralized.
|
|
_, _, err = warp.Send(
|
|
homeRPCEndpoint,
|
|
homeAddress,
|
|
homeKey,
|
|
remoteBlockchainID,
|
|
remoteAddress,
|
|
common.HexToAddress(homeKeyAddress),
|
|
big.NewInt(1),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
t0 := time.Now()
|
|
for {
|
|
isCollateralized, err := warp.TokenRemoteIsCollateralized(
|
|
remoteRPCEndpoint,
|
|
remoteAddress,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if isCollateralized {
|
|
break
|
|
}
|
|
elapsed := time.Since(t0)
|
|
if elapsed > checkTimeout {
|
|
return fmt.Errorf("timeout waiting for remote endpoint collateralization")
|
|
}
|
|
time.Sleep(checkInterval)
|
|
}
|
|
|
|
if flags.remoteFlags.removeMinterAdmin && remoteMinterManagerIsAdmin {
|
|
ux.Logger.PrintToUser("Removing minter admin %s", remoteMinterManagerAddress)
|
|
if err := precompiles.SetNone(
|
|
remoteRPCEndpoint,
|
|
precompiles.NativeMinterPrecompile,
|
|
remoteMinterManagerPrivKey,
|
|
common.HexToAddress(remoteMinterManagerAddress),
|
|
); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
minterRole := "admin"
|
|
if !remoteMinterManagerIsAdmin {
|
|
minterRole = "manager"
|
|
}
|
|
ux.Logger.PrintToUser("Original minter %s %s is left in place", minterRole, remoteMinterManagerAddress)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|