diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index c9f49ee..3dec86f 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -11,6 +11,7 @@ import ( "fmt" "math/big" "os" + "time" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" @@ -20,6 +21,7 @@ import ( "github.com/ava-labs/subnet-evm/commontype" "github.com/ava-labs/subnet-evm/params" "github.com/ava-labs/subnet-evm/params/extras" + "github.com/ava-labs/subnet-evm/utils" "go.uber.org/zap" "github.com/ava-labs/avalanche-tooling-sdk-go/evm" @@ -214,14 +216,14 @@ func New(subnetParams *SubnetParams) (*Subnet, error) { case subnetParams.GenesisFilePath != "": genesisBytes, err = os.ReadFile(subnetParams.GenesisFilePath) case subnetParams.SubnetEVM != nil: - genesisBytes, err = createEvmGenesis(subnetParams.SubnetEVM) + genesisBytes, err = CreateEvmGenesis(subnetParams.SubnetEVM) default: } if err != nil { return nil, err } - vmID, err := vmID(subnetParams.Name) + vmID, err := VMID(subnetParams.Name) if err != nil { return nil, fmt.Errorf("failed to create VM ID from %s: %w", subnetParams.Name, err) } @@ -237,7 +239,7 @@ func (c *Subnet) SetSubnetID(subnetID ids.ID) { c.SubnetID = subnetID } -func createEvmGenesis( +func CreateEvmGenesis( subnetEVMParams *SubnetEVMParams, ) ([]byte, error) { genesis := core.Genesis{} @@ -291,7 +293,23 @@ func createEvmGenesis( return prettyJSON.Bytes(), nil } -func vmID(vmName string) (ids.ID, error) { +func GetDefaultSubnetEVMGenesis(initialAllocationAddress string) SubnetEVMParams { + genesisBlock0Timestamp := utils.TimeToNewUint64(time.Now()) + allocation := core.GenesisAlloc{} + defaultAmount, _ := new(big.Int).SetString(vm.DefaultEvmAirdropAmount, 10) + allocation[common.HexToAddress(initialAllocationAddress)] = core.GenesisAccount{ + Balance: defaultAmount, + } + return SubnetEVMParams{ + ChainID: big.NewInt(123456), + FeeConfig: vm.StarterFeeConfig, + Allocation: allocation, + Precompiles: extras.Precompiles{}, + Timestamp: genesisBlock0Timestamp, + } +} + +func VMID(vmName string) (ids.ID, error) { if len(vmName) > 32 { return ids.Empty, fmt.Errorf("VM name must be <= 32 bytes, found %d", len(vmName)) } diff --git a/constants/constants.go b/constants/constants.go index 47791a3..373bc0f 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -10,3 +10,8 @@ const ( UserOnlyWriteReadExecPerms = 0o700 WriteReadUserOnlyPerms = 0o600 ) + +// Chain type constants +const ( + ChainTypePChain = "P-Chain" +) diff --git a/example/create_chain.go b/example/create_chain.go new file mode 100644 index 0000000..8610890 --- /dev/null +++ b/example/create_chain.go @@ -0,0 +1,108 @@ +//go:build create_chain +// +build create_chain + +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package main + +import ( + "fmt" + "os" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/blockchain" + "github.com/ava-labs/avalanche-tooling-sdk-go/network" + "github.com/ava-labs/avalanche-tooling-sdk-go/utils" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/local" + "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/types" + + pchainTxs "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/txs/p-chain" + avagoTxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" +) + +func CreateChain(subnetID string) error { + ctx, cancel := utils.GetTimedContext(120 * time.Second) + defer cancel() + network := network.FujiNetwork() + + localWallet, err := local.NewLocalWallet() + if err != nil { + return fmt.Errorf("failed to create wallet: %w", err) + } + + existingAccount, err := localWallet.ImportAccount("EXISTING_KEY_PATH") + if err != nil { + return fmt.Errorf("failed to ImportAccount: %w", err) + } + + evmGenesisParams := blockchain.GetDefaultSubnetEVMGenesis("EVM_ADDRESS") + evmGenesisBytes, _ := blockchain.CreateEvmGenesis(&evmGenesisParams) + blockchainName := "TestBlockchain" + vmID, err := blockchain.VMID(blockchainName) + if err != nil { + return fmt.Errorf("failed to get vmid: %w", err) + } + + createChainParams := &pchainTxs.CreateChainTxParams{ + SubnetAuthKeys: []string{"P-fujixxxxx"}, + SubnetID: subnetID, + VMID: vmID.String(), + ChainName: blockchainName, + Genesis: evmGenesisBytes, + } + buildTxParams := types.BuildTxParams{ + BaseParams: types.BaseParams{ + Account: *existingAccount, + Network: network, + }, + BuildTxInput: createChainParams, + } + buildTxResult, err := localWallet.BuildTx(ctx, buildTxParams) + if err != nil { + return fmt.Errorf("failed to BuildTx: %w", err) + } + + signTxParams := types.SignTxParams{ + BaseParams: types.BaseParams{ + Account: *existingAccount, + Network: network, + }, + BuildTxResult: &buildTxResult, + } + + signTxResult, err := localWallet.SignTx(ctx, signTxParams) + if err != nil { + return fmt.Errorf("failed to signTx: %w", err) + } + + sendTxParams := types.SendTxParams{ + BaseParams: types.BaseParams{ + Account: *existingAccount, + Network: network, + }, + SignTxResult: &signTxResult, + } + sendTxResult, err := localWallet.SendTx(ctx, sendTxParams) + if err != nil { + return fmt.Errorf("failed to sendTx: %w", err) + } + + if tx := sendTxResult.GetTx(); tx != nil { + if pChainTx, ok := tx.(*avagoTxs.Tx); ok { + fmt.Printf("sendTxResult %s \n", pChainTx.ID()) + } else { + fmt.Printf("sendTxResult %s transaction \n", sendTxResult.GetChainType()) + } + } + return nil +} + +func main() { + // Use a hardcoded subnet ID for this example + // In a real scenario, you would get this from creating a subnet first + subnetID := "SUBNET_ID" + if err := CreateChain(subnetID); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/example/wallet_example.go b/example/create_subnet.go similarity index 73% rename from example/wallet_example.go rename to example/create_subnet.go index 7bed152..6fddd02 100644 --- a/example/wallet_example.go +++ b/example/create_subnet.go @@ -1,3 +1,6 @@ +//go:build create_subnet +// +build create_subnet + // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. package main @@ -7,6 +10,8 @@ import ( "os" "time" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanche-tooling-sdk-go/network" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/local" @@ -16,23 +21,23 @@ import ( avagoTxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) -func CreateSubnet() error { +func CreateSubnet() (ids.ID, error) { ctx, cancel := utils.GetTimedContext(120 * time.Second) defer cancel() network := network.FujiNetwork() localWallet, err := local.NewLocalWallet() if err != nil { - return fmt.Errorf("failed to create wallet: %w", err) + return ids.Empty, fmt.Errorf("failed to create wallet: %w", err) } existingAccount, err := localWallet.ImportAccount("EXISTING_KEY_PATH") if err != nil { - return fmt.Errorf("failed to ImportAccount: %w", err) + return ids.Empty, fmt.Errorf("failed to ImportAccount: %w", err) } createSubnetParams := &pchainTxs.CreateSubnetTxParams{ - ControlKeys: []string{"P-fuji1377nx80rx3pzneup5qywgdgdsmzntql7trcqlg"}, + ControlKeys: []string{"P-fujixxxxx"}, Threshold: 1, } buildTxParams := types.BuildTxParams{ @@ -42,7 +47,7 @@ func CreateSubnet() error { } buildTxResult, err := localWallet.BuildTx(ctx, buildTxParams) if err != nil { - return fmt.Errorf("failed to BuildTx: %w", err) + return ids.Empty, fmt.Errorf("failed to BuildTx: %w", err) } signTxParams := types.SignTxParams{ @@ -52,7 +57,7 @@ func CreateSubnet() error { } signTxResult, err := localWallet.SignTx(ctx, signTxParams) if err != nil { - return fmt.Errorf("failed to signTx: %w", err) + return ids.Empty, fmt.Errorf("failed to signTx: %w", err) } sendTxParams := types.SendTxParams{ @@ -62,20 +67,19 @@ func CreateSubnet() error { } sendTxResult, err := localWallet.SendTx(ctx, sendTxParams) if err != nil { - return fmt.Errorf("failed to sendTx: %w", err) + return ids.Empty, fmt.Errorf("failed to sendTx: %w", err) } if tx := sendTxResult.GetTx(); tx != nil { if pChainTx, ok := tx.(*avagoTxs.Tx); ok { fmt.Printf("sendTxResult %s \n", pChainTx.ID()) - } else { - fmt.Printf("sendTxResult %s transaction \n", sendTxResult.GetChainType()) + return pChainTx.ID(), nil } } - return nil + return ids.Empty, fmt.Errorf("unable to get tx id") } func main() { - if err := CreateSubnet(); err != nil { + if _, err := CreateSubnet(); err != nil { fmt.Println(err) os.Exit(1) } diff --git a/wallet/chains/pchain/builder.go b/wallet/chains/pchain/builder.go index 644d690..78969ce 100644 --- a/wallet/chains/pchain/builder.go +++ b/wallet/chains/pchain/builder.go @@ -24,6 +24,8 @@ func BuildTx(wallet *primary.Wallet, account account.Account, params types.Build switch txType := params.BuildTxInput.(type) { case *pchainTxs.CreateSubnetTxParams: return buildCreateSubnetTx(wallet, txType) + case *pchainTxs.CreateChainTxParams: + return buildCreateChainTx(wallet, account, txType) case *pchainTxs.ConvertSubnetToL1TxParams: return buildConvertSubnetToL1Tx(wallet, account, txType) default: @@ -31,24 +33,6 @@ func BuildTx(wallet *primary.Wallet, account account.Account, params types.Build } } -// buildConvertSubnetToL1Tx provides a default implementation that can be used by any wallet -func buildConvertSubnetToL1Tx(wallet *primary.Wallet, account account.Account, params *pchainTxs.ConvertSubnetToL1TxParams) (types.BuildTxResult, error) { - options := getMultisigTxOptions(account, params.SubnetAuthKeys) - unsignedTx, err := wallet.P().Builder().NewConvertSubnetToL1Tx( - params.SubnetID, - params.ChainID, - params.Address, - params.Validators, - options..., - ) - if err != nil { - return types.BuildTxResult{}, fmt.Errorf("error building tx: %w", err) - } - builtTx := avagoTxs.Tx{Unsigned: unsignedTx} - pChainResult := types.NewPChainBuildTxResult(&builtTx) - return types.BuildTxResult{BuildTxOutput: pChainResult}, nil -} - // buildCreateSubnetTx provides a default implementation that can be used by any wallet func buildCreateSubnetTx(wallet *primary.Wallet, params *pchainTxs.CreateSubnetTxParams) (types.BuildTxResult, error) { addrs, err := address.ParseToIDs(params.ControlKeys) @@ -71,6 +55,69 @@ func buildCreateSubnetTx(wallet *primary.Wallet, params *pchainTxs.CreateSubnetT return types.BuildTxResult{BuildTxOutput: pChainResult}, nil } +// buildCreateChainTx provides a default implementation that can be used by any wallet +func buildCreateChainTx(wallet *primary.Wallet, account account.Account, params *pchainTxs.CreateChainTxParams) (types.BuildTxResult, error) { + subnetAuthKeys, err := convertSubnetAuthKeys(params.SubnetAuthKeys) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to convert subnet auth keys: %w", err) + } + options := getMultisigTxOptions(account, subnetAuthKeys) + fxIDs := make([]ids.ID, 0) + subnetID, err := ids.FromString(params.SubnetID) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to parse subnet ID: %w", err) + } + vmID, err := ids.FromString(params.VMID) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to parse VM ID: %w", err) + } + unsignedTx, err := wallet.P().Builder().NewCreateChainTx( + subnetID, + params.Genesis, + vmID, + fxIDs, + params.ChainName, + options..., + ) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("error building tx: %w", err) + } + builtTx := avagoTxs.Tx{Unsigned: unsignedTx} + pChainResult := types.NewPChainBuildTxResult(&builtTx) + return types.BuildTxResult{BuildTxOutput: pChainResult}, nil +} + +// buildConvertSubnetToL1Tx provides a default implementation that can be used by any wallet +func buildConvertSubnetToL1Tx(wallet *primary.Wallet, account account.Account, params *pchainTxs.ConvertSubnetToL1TxParams) (types.BuildTxResult, error) { + subnetAuthKeys, err := convertSubnetAuthKeys(params.SubnetAuthKeys) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to convert subnet auth keys: %w", err) + } + subnetID, err := ids.FromString(params.SubnetID) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to parse subnet ID: %w", err) + } + chainID, err := ids.FromString(params.ChainID) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("failed to parse chain ID: %w", err) + } + addressBytes := []byte(params.Address) + options := getMultisigTxOptions(account, subnetAuthKeys) + unsignedTx, err := wallet.P().Builder().NewConvertSubnetToL1Tx( + subnetID, + chainID, + addressBytes, + params.Validators, + options..., + ) + if err != nil { + return types.BuildTxResult{}, fmt.Errorf("error building tx: %w", err) + } + builtTx := avagoTxs.Tx{Unsigned: unsignedTx} + pChainResult := types.NewPChainBuildTxResult(&builtTx) + return types.BuildTxResult{BuildTxOutput: pChainResult}, nil +} + // getMultisigTxOptions is a helper function that can be shared func getMultisigTxOptions(account account.Account, subnetAuthKeys []ids.ShortID) []common.Option { options := []common.Option{} @@ -94,3 +141,12 @@ func getMultisigTxOptions(account account.Account, subnetAuthKeys []ids.ShortID) options = append(options, common.WithChangeOwner(changeOwner)) return options } + +// convertSubnetAuthKeys converts a slice of string addresses to a slice of ShortIDs +func convertSubnetAuthKeys(subnetAuthKeys []string) ([]ids.ShortID, error) { + subnetAuthKeyIDs, err := address.ParseToIDs(subnetAuthKeys) + if err != nil { + return nil, fmt.Errorf("failed to convert subnet auth key %s to ShortID: %w", subnetAuthKeys, err) + } + return subnetAuthKeyIDs, nil +} diff --git a/wallet/local/wallet.go b/wallet/local/wallet.go index 98bea53..b367b57 100644 --- a/wallet/local/wallet.go +++ b/wallet/local/wallet.go @@ -8,6 +8,7 @@ import ( "fmt" "time" + "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/wallet/subnet/primary" "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" @@ -18,6 +19,7 @@ import ( "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/types" + txs "github.com/ava-labs/avalanche-tooling-sdk-go/wallet/txs/p-chain" avagoTxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) @@ -40,17 +42,111 @@ func NewLocalWallet() (*LocalWallet, error) { }, nil } -func (w *LocalWallet) loadAccountIntoWallet(ctx context.Context, account account.Account, network network.Network) error { +// buildWalletConfig creates a WalletConfig with appropriate SubnetIDs based on transaction type +func buildWalletConfig(txInput interface{}) primary.WalletConfig { + config := primary.WalletConfig{} + + // Handle different input types + switch input := txInput.(type) { + case *types.SignTxParams: + // Extract subnet ID from SignTxParams by looking at the BuildTxResult + if input != nil && input.BuildTxResult != nil { + if subnetID, err := extractSubnetIDFromBuildTxResult(input.BuildTxResult); err == nil && subnetID != ids.Empty { + config.SubnetIDs = []ids.ID{subnetID} + } + } + case *types.SendTxParams: + // Extract subnet ID from SendTxParams by looking at the SignTxResult + if input != nil && input.SignTxResult != nil { + if subnetID, err := extractSubnetIDFromSignTxResult(input.SignTxResult); err == nil && subnetID != ids.Empty { + config.SubnetIDs = []ids.ID{subnetID} + } + } + case *types.BuildTxParams: + // Extract subnet ID from BuildTxParams by looking at the BuildTxInput + if input != nil && input.BuildTxInput != nil { + if subnetID, err := extractSubnetIDFromBuildTxInput(input.BuildTxInput); err == nil && subnetID != ids.Empty { + config.SubnetIDs = []ids.ID{subnetID} + } + } + } + + return config +} + +// extractSubnetIDFromBuildTxInput extracts subnet ID from BuildTxInput parameters +// This function handles the case where we have transaction parameters but not the built transaction yet +func extractSubnetIDFromBuildTxInput(input types.BuildTxInput) (ids.ID, error) { + // Handle different BuildTxInput types + switch params := input.(type) { + case *txs.CreateChainTxParams: + // For CreateChainTx, extract subnet ID from parameters + if params.SubnetID != "" { + return ids.FromString(params.SubnetID) + } + case *txs.ConvertSubnetToL1TxParams: + // For ConvertSubnetToL1Tx, extract subnet ID from parameters + if params.SubnetID != "" { + return ids.FromString(params.SubnetID) + } + case *txs.CreateSubnetTxParams: + // CreateSubnetTx doesn't have a subnet ID since it creates the subnet + return ids.Empty, fmt.Errorf("CreateSubnetTx doesn't have a subnet ID") + default: + // Unknown BuildTxInput type + } + return ids.Empty, fmt.Errorf("no subnet ID found in BuildTxInput") +} + +// extractSubnetIDFromBuildTxResult extracts subnet ID from a BuildTxResult +func extractSubnetIDFromBuildTxResult(result *types.BuildTxResult) (ids.ID, error) { + if result != nil && result.BuildTxOutput != nil { + if tx := result.BuildTxOutput.GetTx(); tx != nil { + return extractSubnetIDFromTx(tx) + } + } + return ids.Empty, fmt.Errorf("no transaction found in BuildTxResult") +} + +// extractSubnetIDFromSignTxResult extracts subnet ID from a SignTxResult +func extractSubnetIDFromSignTxResult(result *types.SignTxResult) (ids.ID, error) { + if result != nil && result.SignTxOutput != nil { + if tx := result.SignTxOutput.GetTx(); tx != nil { + return extractSubnetIDFromTx(tx) + } + } + return ids.Empty, fmt.Errorf("no transaction found in SignTxResult") +} + +// extractSubnetIDFromTx extracts subnet ID from a transaction object +func extractSubnetIDFromTx(tx interface{}) (ids.ID, error) { + // Handle P-Chain transactions + if pChainTx, ok := tx.(*avagoTxs.Tx); ok && pChainTx.Unsigned != nil { + switch unsignedTx := pChainTx.Unsigned.(type) { + case *avagoTxs.CreateChainTx: + // For CreateChainTx, the subnet ID field is SubnetID + return unsignedTx.SubnetID, nil + case *avagoTxs.ConvertSubnetToL1Tx: + // For CreateChainTx, the subnet ID field is SubnetID + return unsignedTx.Subnet, nil + } + } + return ids.Empty, fmt.Errorf("no subnet ID found in transaction") +} + +func (w *LocalWallet) loadAccountIntoWallet(ctx context.Context, account account.Account, network network.Network, txInput interface{}) error { keychain, err := account.GetKeychain() if err != nil { return err } + + walletConfig := buildWalletConfig(txInput) wallet, err := primary.MakeWallet( ctx, network.Endpoint, keychain, keychain, - primary.WalletConfig{}, + walletConfig, ) if err != nil { return err @@ -101,7 +197,7 @@ func (w *LocalWallet) ImportAccount(keyPath string) (*account.Account, error) { // BuildTx constructs a transaction for the specified operation func (w *LocalWallet) BuildTx(ctx context.Context, params types.BuildTxParams) (types.BuildTxResult, error) { - if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network); err != nil { + if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network, ¶ms); err != nil { return types.BuildTxResult{}, fmt.Errorf("error loading account into wallet: %w", err) } return wallet.BuildTx(w.Wallet, params) @@ -109,7 +205,7 @@ func (w *LocalWallet) BuildTx(ctx context.Context, params types.BuildTxParams) ( // SignTx signs a transaction func (w *LocalWallet) SignTx(ctx context.Context, params types.SignTxParams) (types.SignTxResult, error) { - if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network); err != nil { + if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network, ¶ms); err != nil { return types.SignTxResult{}, fmt.Errorf("error signing tx: %w", err) } @@ -118,7 +214,7 @@ func (w *LocalWallet) SignTx(ctx context.Context, params types.SignTxParams) (ty // SendTx submits a signed transaction to the Network func (w *LocalWallet) SendTx(ctx context.Context, params types.SendTxParams) (types.SendTxResult, error) { - if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network); err != nil { + if err := w.loadAccountIntoWallet(ctx, params.Account, params.Network, ¶ms); err != nil { return types.SendTxResult{}, fmt.Errorf("error loading account into wallet: %w", err) } diff --git a/wallet/txs/p-chain/convertSubnetToL1Tx.go b/wallet/txs/p-chain/convertSubnetToL1Tx.go index 0155176..5baa2bb 100644 --- a/wallet/txs/p-chain/convertSubnetToL1Tx.go +++ b/wallet/txs/p-chain/convertSubnetToL1Tx.go @@ -5,7 +5,7 @@ package txs import ( "fmt" - "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" avagoTxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" ) @@ -13,24 +13,27 @@ import ( // ConvertSubnetToL1TxParams contains all parameters needed to create a ConvertSubnetToL1Tx type ConvertSubnetToL1TxParams struct { // SubnetAuthKeys are the keys used to sign `ConvertSubnetToL1Tx` - SubnetAuthKeys []ids.ShortID + SubnetAuthKeys []string // SubnetID is Subnet ID of the subnet to convert to an L1. - SubnetID ids.ID + SubnetID string // ChainID is Blockchain ID of the L1 where the validator manager contract is deployed. - ChainID ids.ID + ChainID string // Address is address of the validator manager contract. - Address []byte + Address string // Validators are the initial set of L1 validators after the conversion. Validators []*avagoTxs.ConvertSubnetToL1Validator } // Validate validates the parameters func (p ConvertSubnetToL1TxParams) Validate() error { - if p.SubnetID == ids.Empty { - return fmt.Errorf("SubnetID cannot be empty") + if len(p.SubnetAuthKeys) == 0 { + return fmt.Errorf("subnet auth keys cannot be empty") } - if p.ChainID == ids.Empty { - return fmt.Errorf("ChainID cannot be empty") + if p.SubnetID == "" { + return fmt.Errorf("subnet ID cannot be empty") + } + if p.ChainID == "" { + return fmt.Errorf("chain ID cannot be empty") } if len(p.Address) == 0 { return fmt.Errorf("address cannot be empty") @@ -43,5 +46,5 @@ func (p ConvertSubnetToL1TxParams) Validate() error { // GetChainType returns which chain this transaction is for func (p ConvertSubnetToL1TxParams) GetChainType() string { - return "P-Chain" + return constants.ChainTypePChain } diff --git a/wallet/txs/p-chain/createChainTx.go b/wallet/txs/p-chain/createChainTx.go new file mode 100644 index 0000000..6d42e85 --- /dev/null +++ b/wallet/txs/p-chain/createChainTx.go @@ -0,0 +1,48 @@ +// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. +package txs + +import ( + "fmt" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" +) + +// CreateChainTxParams contains all parameters needed to create a ConvertSubnetToL1Tx +type CreateChainTxParams struct { + // SubnetAuthKeys are the keys used to sign `ConvertSubnetToL1Tx` + SubnetAuthKeys []string + // SubnetID specifies the subnet to launch the chain in + SubnetID string + // VMID specifies the vm that the new chain will run. + VMID string + // ChainName specifies a human-readable name for the chain + ChainName string + // Genesis specifies the initial state of the new chain + Genesis []byte +} + +// Validate validates the parameters +func (p CreateChainTxParams) Validate() error { + if p.SubnetAuthKeys == nil { + return fmt.Errorf("subnet auth keys cannot be empty") + } + if p.SubnetID == "" { + return fmt.Errorf("subnet ID cannot be empty") + } + if p.VMID == "" { + return fmt.Errorf("VMID cannot be empty") + } + if p.ChainName == "" { + return fmt.Errorf("chain name cannot be empty") + } + if len(p.Genesis) == 0 { + return fmt.Errorf("genesis cannot be empty") + } + return nil +} + +// GetChainType returns which chain this transaction is for +func (p CreateChainTxParams) GetChainType() string { + return constants.ChainTypePChain +} diff --git a/wallet/txs/p-chain/createSubnetTx.go b/wallet/txs/p-chain/createSubnetTx.go index 97d56d5..d246f30 100644 --- a/wallet/txs/p-chain/createSubnetTx.go +++ b/wallet/txs/p-chain/createSubnetTx.go @@ -2,7 +2,11 @@ // See the file LICENSE for licensing terms. package txs -import "fmt" +import ( + "fmt" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" +) // CreateSubnetTxParams contains all parameters needed to create a ConvertSubnetToL1Tx type CreateSubnetTxParams struct { @@ -23,5 +27,5 @@ func (p CreateSubnetTxParams) Validate() error { // GetChainType returns which chain this transaction is for func (p CreateSubnetTxParams) GetChainType() string { - return "P-Chain" + return constants.ChainTypePChain } diff --git a/wallet/types/builder.go b/wallet/types/builder.go index fd966bd..4c9c897 100644 --- a/wallet/types/builder.go +++ b/wallet/types/builder.go @@ -8,6 +8,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanche-tooling-sdk-go/account" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/network" ) @@ -69,7 +70,7 @@ type PChainBuildTxResult struct { } func (p *PChainBuildTxResult) GetChainType() string { - return ChainTypePChain + return constants.ChainTypePChain } func (p *PChainBuildTxResult) GetTx() interface{} { diff --git a/wallet/types/constants.go b/wallet/types/constants.go deleted file mode 100644 index 65fa101..0000000 --- a/wallet/types/constants.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2025, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. -package types - -// Transaction type constants for all chains - -// P-Chain transaction types -const ( - PChainCreateSubnetTx = "CreateSubnetTx" - PChainConvertSubnetToL1Tx = "ConvertSubnetToL1Tx" - PChainAddSubnetValidatorTx = "AddSubnetValidatorTx" - PChainRemoveSubnetValidatorTx = "RemoveSubnetValidatorTx" - PChainCreateChainTx = "CreateChainTx" - PChainTransformSubnetTx = "TransformSubnetTx" - PChainAddPermissionlessValidatorTx = "AddPermissionlessValidatorTx" - PChainTransferSubnetOwnershipTx = "TransferSubnetOwnershipTx" -) - -// C-Chain transaction types -const ( - CChainTransferTx = "TransferTx" - CChainContractCallTx = "ContractCallTx" - CChainDeployTx = "DeployTx" -) - -// X-Chain transaction types -const ( - XChainTransferTx = "TransferTx" - XChainExportTx = "ExportTx" - XChainImportTx = "ImportTx" -) - -// Chain type constants -const ( - ChainTypePChain = "P-Chain" - ChainTypeCChain = "C-Chain" - ChainTypeXChain = "X-Chain" -) - -// Transaction type constants -const ( - TxTypeUnknown = "Unknown" - TxTypeEVMTransaction = "EVMTransaction" - TxTypeAVMTransaction = "AVMTransaction" -) diff --git a/wallet/types/sender.go b/wallet/types/sender.go index 2516d76..ec18218 100644 --- a/wallet/types/sender.go +++ b/wallet/types/sender.go @@ -8,6 +8,7 @@ import ( "github.com/ava-labs/avalanchego/vms/platformvm/txs" "github.com/ava-labs/avalanche-tooling-sdk-go/account" + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/network" ) @@ -61,7 +62,7 @@ type PChainSendTxResult struct { } func (p *PChainSendTxResult) GetChainType() string { - return ChainTypePChain + return constants.ChainTypePChain } func (p *PChainSendTxResult) GetTx() interface{} {