github.com/ava-labs/avalanchego@v1.11.11/wallet/chain/p/builder/builder.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package builder
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/ava-labs/avalanchego/ids"
    13  	"github.com/ava-labs/avalanchego/utils"
    14  	"github.com/ava-labs/avalanchego/utils/constants"
    15  	"github.com/ava-labs/avalanchego/utils/math"
    16  	"github.com/ava-labs/avalanchego/utils/set"
    17  	"github.com/ava-labs/avalanchego/vms/components/avax"
    18  	"github.com/ava-labs/avalanchego/vms/components/gas"
    19  	"github.com/ava-labs/avalanchego/vms/components/verify"
    20  	"github.com/ava-labs/avalanchego/vms/platformvm/fx"
    21  	"github.com/ava-labs/avalanchego/vms/platformvm/signer"
    22  	"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
    23  	"github.com/ava-labs/avalanchego/vms/platformvm/txs"
    24  	"github.com/ava-labs/avalanchego/vms/platformvm/txs/fee"
    25  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    26  	"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
    27  )
    28  
    29  var (
    30  	ErrNoChangeAddress           = errors.New("no possible change address")
    31  	ErrUnknownOutputType         = errors.New("unknown output type")
    32  	ErrUnknownOwnerType          = errors.New("unknown owner type")
    33  	ErrInsufficientAuthorization = errors.New("insufficient authorization")
    34  	ErrInsufficientFunds         = errors.New("insufficient funds")
    35  
    36  	_ Builder = (*builder)(nil)
    37  )
    38  
    39  // Builder provides a convenient interface for building unsigned P-chain
    40  // transactions.
    41  type Builder interface {
    42  	// Context returns the configuration of the chain that this builder uses to
    43  	// create transactions.
    44  	Context() *Context
    45  
    46  	// GetBalance calculates the amount of each asset that this builder has
    47  	// control over.
    48  	GetBalance(
    49  		options ...common.Option,
    50  	) (map[ids.ID]uint64, error)
    51  
    52  	// GetImportableBalance calculates the amount of each asset that this
    53  	// builder could import from the provided chain.
    54  	//
    55  	// - [chainID] specifies the chain the funds are from.
    56  	GetImportableBalance(
    57  		chainID ids.ID,
    58  		options ...common.Option,
    59  	) (map[ids.ID]uint64, error)
    60  
    61  	// NewBaseTx creates a new simple value transfer.
    62  	//
    63  	// - [outputs] specifies all the recipients and amounts that should be sent
    64  	//   from this transaction.
    65  	NewBaseTx(
    66  		outputs []*avax.TransferableOutput,
    67  		options ...common.Option,
    68  	) (*txs.BaseTx, error)
    69  
    70  	// NewAddValidatorTx creates a new validator of the primary network.
    71  	//
    72  	// - [vdr] specifies all the details of the validation period such as the
    73  	//   startTime, endTime, stake weight, and nodeID.
    74  	// - [rewardsOwner] specifies the owner of all the rewards this validator
    75  	//   may accrue during its validation period.
    76  	// - [shares] specifies the fraction (out of 1,000,000) that this validator
    77  	//   will take from delegation rewards. If 1,000,000 is provided, 100% of
    78  	//   the delegation reward will be sent to the validator's [rewardsOwner].
    79  	NewAddValidatorTx(
    80  		vdr *txs.Validator,
    81  		rewardsOwner *secp256k1fx.OutputOwners,
    82  		shares uint32,
    83  		options ...common.Option,
    84  	) (*txs.AddValidatorTx, error)
    85  
    86  	// NewAddSubnetValidatorTx creates a new validator of a subnet.
    87  	//
    88  	// - [vdr] specifies all the details of the validation period such as the
    89  	//   startTime, endTime, sampling weight, nodeID, and subnetID.
    90  	NewAddSubnetValidatorTx(
    91  		vdr *txs.SubnetValidator,
    92  		options ...common.Option,
    93  	) (*txs.AddSubnetValidatorTx, error)
    94  
    95  	// NewRemoveSubnetValidatorTx removes [nodeID] from the validator
    96  	// set [subnetID].
    97  	NewRemoveSubnetValidatorTx(
    98  		nodeID ids.NodeID,
    99  		subnetID ids.ID,
   100  		options ...common.Option,
   101  	) (*txs.RemoveSubnetValidatorTx, error)
   102  
   103  	// NewAddDelegatorTx creates a new delegator to a validator on the primary
   104  	// network.
   105  	//
   106  	// - [vdr] specifies all the details of the delegation period such as the
   107  	//   startTime, endTime, stake weight, and validator's nodeID.
   108  	// - [rewardsOwner] specifies the owner of all the rewards this delegator
   109  	//   may accrue at the end of its delegation period.
   110  	NewAddDelegatorTx(
   111  		vdr *txs.Validator,
   112  		rewardsOwner *secp256k1fx.OutputOwners,
   113  		options ...common.Option,
   114  	) (*txs.AddDelegatorTx, error)
   115  
   116  	// NewCreateChainTx creates a new chain in the named subnet.
   117  	//
   118  	// - [subnetID] specifies the subnet to launch the chain in.
   119  	// - [genesis] specifies the initial state of the new chain.
   120  	// - [vmID] specifies the vm that the new chain will run.
   121  	// - [fxIDs] specifies all the feature extensions that the vm should be
   122  	//   running with.
   123  	// - [chainName] specifies a human readable name for the chain.
   124  	NewCreateChainTx(
   125  		subnetID ids.ID,
   126  		genesis []byte,
   127  		vmID ids.ID,
   128  		fxIDs []ids.ID,
   129  		chainName string,
   130  		options ...common.Option,
   131  	) (*txs.CreateChainTx, error)
   132  
   133  	// NewCreateSubnetTx creates a new subnet with the specified owner.
   134  	//
   135  	// - [owner] specifies who has the ability to create new chains and add new
   136  	//   validators to the subnet.
   137  	NewCreateSubnetTx(
   138  		owner *secp256k1fx.OutputOwners,
   139  		options ...common.Option,
   140  	) (*txs.CreateSubnetTx, error)
   141  
   142  	// NewTransferSubnetOwnershipTx changes the owner of the named subnet.
   143  	//
   144  	// - [subnetID] specifies the subnet to be modified
   145  	// - [owner] specifies who has the ability to create new chains and add new
   146  	//   validators to the subnet.
   147  	NewTransferSubnetOwnershipTx(
   148  		subnetID ids.ID,
   149  		owner *secp256k1fx.OutputOwners,
   150  		options ...common.Option,
   151  	) (*txs.TransferSubnetOwnershipTx, error)
   152  
   153  	// NewImportTx creates an import transaction that attempts to consume all
   154  	// the available UTXOs and import the funds to [to].
   155  	//
   156  	// - [chainID] specifies the chain to be importing funds from.
   157  	// - [to] specifies where to send the imported funds to.
   158  	NewImportTx(
   159  		chainID ids.ID,
   160  		to *secp256k1fx.OutputOwners,
   161  		options ...common.Option,
   162  	) (*txs.ImportTx, error)
   163  
   164  	// NewExportTx creates an export transaction that attempts to send all the
   165  	// provided [outputs] to the requested [chainID].
   166  	//
   167  	// - [chainID] specifies the chain to be exporting the funds to.
   168  	// - [outputs] specifies the outputs to send to the [chainID].
   169  	NewExportTx(
   170  		chainID ids.ID,
   171  		outputs []*avax.TransferableOutput,
   172  		options ...common.Option,
   173  	) (*txs.ExportTx, error)
   174  
   175  	// NewTransformSubnetTx creates a transform subnet transaction that attempts
   176  	// to convert the provided [subnetID] from a permissioned subnet to a
   177  	// permissionless subnet. This transaction will convert
   178  	// [maxSupply] - [initialSupply] of [assetID] to staking rewards.
   179  	//
   180  	// - [subnetID] specifies the subnet to transform.
   181  	// - [assetID] specifies the asset to use to reward stakers on the subnet.
   182  	// - [initialSupply] is the amount of [assetID] that will be in circulation
   183  	//   after this transaction is accepted.
   184  	// - [maxSupply] is the maximum total amount of [assetID] that should ever
   185  	//   exist.
   186  	// - [minConsumptionRate] is the rate that a staker will receive rewards
   187  	//   if they stake with a duration of 0.
   188  	// - [maxConsumptionRate] is the maximum rate that staking rewards should be
   189  	//   consumed from the reward pool per year.
   190  	// - [minValidatorStake] is the minimum amount of funds required to become a
   191  	//   validator.
   192  	// - [maxValidatorStake] is the maximum amount of funds a single validator
   193  	//   can be allocated, including delegated funds.
   194  	// - [minStakeDuration] is the minimum number of seconds a staker can stake
   195  	//   for.
   196  	// - [maxStakeDuration] is the maximum number of seconds a staker can stake
   197  	//   for.
   198  	// - [minValidatorStake] is the minimum amount of funds required to become a
   199  	//   delegator.
   200  	// - [maxValidatorWeightFactor] is the factor which calculates the maximum
   201  	//   amount of delegation a validator can receive. A value of 1 effectively
   202  	//   disables delegation.
   203  	// - [uptimeRequirement] is the minimum percentage a validator must be
   204  	//   online and responsive to receive a reward.
   205  	NewTransformSubnetTx(
   206  		subnetID ids.ID,
   207  		assetID ids.ID,
   208  		initialSupply uint64,
   209  		maxSupply uint64,
   210  		minConsumptionRate uint64,
   211  		maxConsumptionRate uint64,
   212  		minValidatorStake uint64,
   213  		maxValidatorStake uint64,
   214  		minStakeDuration time.Duration,
   215  		maxStakeDuration time.Duration,
   216  		minDelegationFee uint32,
   217  		minDelegatorStake uint64,
   218  		maxValidatorWeightFactor byte,
   219  		uptimeRequirement uint32,
   220  		options ...common.Option,
   221  	) (*txs.TransformSubnetTx, error)
   222  
   223  	// NewAddPermissionlessValidatorTx creates a new validator of the specified
   224  	// subnet.
   225  	//
   226  	// - [vdr] specifies all the details of the validation period such as the
   227  	//   subnetID, startTime, endTime, stake weight, and nodeID.
   228  	// - [signer] if the subnetID is the primary network, this is the BLS key
   229  	//   for this validator. Otherwise, this value should be the empty signer.
   230  	// - [assetID] specifies the asset to stake.
   231  	// - [validationRewardsOwner] specifies the owner of all the rewards this
   232  	//   validator earns for its validation period.
   233  	// - [delegationRewardsOwner] specifies the owner of all the rewards this
   234  	//   validator earns for delegations during its validation period.
   235  	// - [shares] specifies the fraction (out of 1,000,000) that this validator
   236  	//   will take from delegation rewards. If 1,000,000 is provided, 100% of
   237  	//   the delegation reward will be sent to the validator's [rewardsOwner].
   238  	NewAddPermissionlessValidatorTx(
   239  		vdr *txs.SubnetValidator,
   240  		signer signer.Signer,
   241  		assetID ids.ID,
   242  		validationRewardsOwner *secp256k1fx.OutputOwners,
   243  		delegationRewardsOwner *secp256k1fx.OutputOwners,
   244  		shares uint32,
   245  		options ...common.Option,
   246  	) (*txs.AddPermissionlessValidatorTx, error)
   247  
   248  	// NewAddPermissionlessDelegatorTx creates a new delegator of the specified
   249  	// subnet on the specified nodeID.
   250  	//
   251  	// - [vdr] specifies all the details of the delegation period such as the
   252  	//   subnetID, startTime, endTime, stake weight, and nodeID.
   253  	// - [assetID] specifies the asset to stake.
   254  	// - [rewardsOwner] specifies the owner of all the rewards this delegator
   255  	//   earns during its delegation period.
   256  	NewAddPermissionlessDelegatorTx(
   257  		vdr *txs.SubnetValidator,
   258  		assetID ids.ID,
   259  		rewardsOwner *secp256k1fx.OutputOwners,
   260  		options ...common.Option,
   261  	) (*txs.AddPermissionlessDelegatorTx, error)
   262  }
   263  
   264  type Backend interface {
   265  	UTXOs(ctx context.Context, sourceChainID ids.ID) ([]*avax.UTXO, error)
   266  	GetSubnetOwner(ctx context.Context, subnetID ids.ID) (fx.Owner, error)
   267  }
   268  
   269  type builder struct {
   270  	addrs   set.Set[ids.ShortID]
   271  	context *Context
   272  	backend Backend
   273  }
   274  
   275  // New returns a new transaction builder.
   276  //
   277  //   - [addrs] is the set of addresses that the builder assumes can be used when
   278  //     signing the transactions in the future.
   279  //   - [context] provides the chain's configuration.
   280  //   - [backend] provides the chain's state.
   281  func New(
   282  	addrs set.Set[ids.ShortID],
   283  	context *Context,
   284  	backend Backend,
   285  ) Builder {
   286  	return &builder{
   287  		addrs:   addrs,
   288  		context: context,
   289  		backend: backend,
   290  	}
   291  }
   292  
   293  func (b *builder) Context() *Context {
   294  	return b.context
   295  }
   296  
   297  func (b *builder) GetBalance(
   298  	options ...common.Option,
   299  ) (map[ids.ID]uint64, error) {
   300  	ops := common.NewOptions(options)
   301  	return b.getBalance(constants.PlatformChainID, ops)
   302  }
   303  
   304  func (b *builder) GetImportableBalance(
   305  	chainID ids.ID,
   306  	options ...common.Option,
   307  ) (map[ids.ID]uint64, error) {
   308  	ops := common.NewOptions(options)
   309  	return b.getBalance(chainID, ops)
   310  }
   311  
   312  func (b *builder) NewBaseTx(
   313  	outputs []*avax.TransferableOutput,
   314  	options ...common.Option,
   315  ) (*txs.BaseTx, error) {
   316  	toBurn := map[ids.ID]uint64{
   317  		b.context.AVAXAssetID: b.context.StaticFeeConfig.TxFee,
   318  	}
   319  	for _, out := range outputs {
   320  		assetID := out.AssetID()
   321  		amountToBurn, err := math.Add(toBurn[assetID], out.Out.Amount())
   322  		if err != nil {
   323  			return nil, err
   324  		}
   325  		toBurn[assetID] = amountToBurn
   326  	}
   327  	toStake := map[ids.ID]uint64{}
   328  
   329  	ops := common.NewOptions(options)
   330  	memo := ops.Memo()
   331  	memoComplexity := gas.Dimensions{
   332  		gas.Bandwidth: uint64(len(memo)),
   333  	}
   334  	outputComplexity, err := fee.OutputComplexity(outputs...)
   335  	if err != nil {
   336  		return nil, err
   337  	}
   338  	complexity, err := fee.IntrinsicBaseTxComplexities.Add(
   339  		&memoComplexity,
   340  		&outputComplexity,
   341  	)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  
   346  	inputs, changeOutputs, _, err := b.spend(
   347  		toBurn,
   348  		toStake,
   349  		0,
   350  		complexity,
   351  		nil,
   352  		ops,
   353  	)
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	outputs = append(outputs, changeOutputs...)
   358  	avax.SortTransferableOutputs(outputs, txs.Codec) // sort the outputs
   359  
   360  	tx := &txs.BaseTx{BaseTx: avax.BaseTx{
   361  		NetworkID:    b.context.NetworkID,
   362  		BlockchainID: constants.PlatformChainID,
   363  		Ins:          inputs,
   364  		Outs:         outputs,
   365  		Memo:         memo,
   366  	}}
   367  	return tx, b.initCtx(tx)
   368  }
   369  
   370  func (b *builder) NewAddValidatorTx(
   371  	vdr *txs.Validator,
   372  	rewardsOwner *secp256k1fx.OutputOwners,
   373  	shares uint32,
   374  	options ...common.Option,
   375  ) (*txs.AddValidatorTx, error) {
   376  	avaxAssetID := b.context.AVAXAssetID
   377  	toBurn := map[ids.ID]uint64{
   378  		avaxAssetID: b.context.StaticFeeConfig.AddPrimaryNetworkValidatorFee,
   379  	}
   380  	toStake := map[ids.ID]uint64{
   381  		avaxAssetID: vdr.Wght,
   382  	}
   383  	ops := common.NewOptions(options)
   384  	inputs, baseOutputs, stakeOutputs, err := b.spend(
   385  		toBurn,
   386  		toStake,
   387  		0,
   388  		gas.Dimensions{},
   389  		nil,
   390  		ops,
   391  	)
   392  	if err != nil {
   393  		return nil, err
   394  	}
   395  
   396  	utils.Sort(rewardsOwner.Addrs)
   397  	tx := &txs.AddValidatorTx{
   398  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   399  			NetworkID:    b.context.NetworkID,
   400  			BlockchainID: constants.PlatformChainID,
   401  			Ins:          inputs,
   402  			Outs:         baseOutputs,
   403  			Memo:         ops.Memo(),
   404  		}},
   405  		Validator:        *vdr,
   406  		StakeOuts:        stakeOutputs,
   407  		RewardsOwner:     rewardsOwner,
   408  		DelegationShares: shares,
   409  	}
   410  	return tx, b.initCtx(tx)
   411  }
   412  
   413  func (b *builder) NewAddSubnetValidatorTx(
   414  	vdr *txs.SubnetValidator,
   415  	options ...common.Option,
   416  ) (*txs.AddSubnetValidatorTx, error) {
   417  	toBurn := map[ids.ID]uint64{
   418  		b.context.AVAXAssetID: b.context.StaticFeeConfig.AddSubnetValidatorFee,
   419  	}
   420  	toStake := map[ids.ID]uint64{}
   421  
   422  	ops := common.NewOptions(options)
   423  	subnetAuth, err := b.authorizeSubnet(vdr.Subnet, ops)
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  
   428  	memo := ops.Memo()
   429  	memoComplexity := gas.Dimensions{
   430  		gas.Bandwidth: uint64(len(memo)),
   431  	}
   432  	authComplexity, err := fee.AuthComplexity(subnetAuth)
   433  	if err != nil {
   434  		return nil, err
   435  	}
   436  	complexity, err := fee.IntrinsicAddSubnetValidatorTxComplexities.Add(
   437  		&memoComplexity,
   438  		&authComplexity,
   439  	)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  
   444  	inputs, outputs, _, err := b.spend(
   445  		toBurn,
   446  		toStake,
   447  		0,
   448  		complexity,
   449  		nil,
   450  		ops,
   451  	)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  
   456  	tx := &txs.AddSubnetValidatorTx{
   457  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   458  			NetworkID:    b.context.NetworkID,
   459  			BlockchainID: constants.PlatformChainID,
   460  			Ins:          inputs,
   461  			Outs:         outputs,
   462  			Memo:         memo,
   463  		}},
   464  		SubnetValidator: *vdr,
   465  		SubnetAuth:      subnetAuth,
   466  	}
   467  	return tx, b.initCtx(tx)
   468  }
   469  
   470  func (b *builder) NewRemoveSubnetValidatorTx(
   471  	nodeID ids.NodeID,
   472  	subnetID ids.ID,
   473  	options ...common.Option,
   474  ) (*txs.RemoveSubnetValidatorTx, error) {
   475  	toBurn := map[ids.ID]uint64{
   476  		b.context.AVAXAssetID: b.context.StaticFeeConfig.TxFee,
   477  	}
   478  	toStake := map[ids.ID]uint64{}
   479  
   480  	ops := common.NewOptions(options)
   481  	subnetAuth, err := b.authorizeSubnet(subnetID, ops)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	memo := ops.Memo()
   487  	memoComplexity := gas.Dimensions{
   488  		gas.Bandwidth: uint64(len(memo)),
   489  	}
   490  	authComplexity, err := fee.AuthComplexity(subnetAuth)
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  	complexity, err := fee.IntrinsicRemoveSubnetValidatorTxComplexities.Add(
   495  		&memoComplexity,
   496  		&authComplexity,
   497  	)
   498  	if err != nil {
   499  		return nil, err
   500  	}
   501  
   502  	inputs, outputs, _, err := b.spend(
   503  		toBurn,
   504  		toStake,
   505  		0,
   506  		complexity,
   507  		nil,
   508  		ops,
   509  	)
   510  	if err != nil {
   511  		return nil, err
   512  	}
   513  
   514  	tx := &txs.RemoveSubnetValidatorTx{
   515  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   516  			NetworkID:    b.context.NetworkID,
   517  			BlockchainID: constants.PlatformChainID,
   518  			Ins:          inputs,
   519  			Outs:         outputs,
   520  			Memo:         ops.Memo(),
   521  		}},
   522  		Subnet:     subnetID,
   523  		NodeID:     nodeID,
   524  		SubnetAuth: subnetAuth,
   525  	}
   526  	return tx, b.initCtx(tx)
   527  }
   528  
   529  func (b *builder) NewAddDelegatorTx(
   530  	vdr *txs.Validator,
   531  	rewardsOwner *secp256k1fx.OutputOwners,
   532  	options ...common.Option,
   533  ) (*txs.AddDelegatorTx, error) {
   534  	avaxAssetID := b.context.AVAXAssetID
   535  	toBurn := map[ids.ID]uint64{
   536  		avaxAssetID: b.context.StaticFeeConfig.AddPrimaryNetworkDelegatorFee,
   537  	}
   538  	toStake := map[ids.ID]uint64{
   539  		avaxAssetID: vdr.Wght,
   540  	}
   541  	ops := common.NewOptions(options)
   542  	inputs, baseOutputs, stakeOutputs, err := b.spend(
   543  		toBurn,
   544  		toStake,
   545  		0,
   546  		gas.Dimensions{},
   547  		nil,
   548  		ops,
   549  	)
   550  	if err != nil {
   551  		return nil, err
   552  	}
   553  
   554  	utils.Sort(rewardsOwner.Addrs)
   555  	tx := &txs.AddDelegatorTx{
   556  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   557  			NetworkID:    b.context.NetworkID,
   558  			BlockchainID: constants.PlatformChainID,
   559  			Ins:          inputs,
   560  			Outs:         baseOutputs,
   561  			Memo:         ops.Memo(),
   562  		}},
   563  		Validator:              *vdr,
   564  		StakeOuts:              stakeOutputs,
   565  		DelegationRewardsOwner: rewardsOwner,
   566  	}
   567  	return tx, b.initCtx(tx)
   568  }
   569  
   570  func (b *builder) NewCreateChainTx(
   571  	subnetID ids.ID,
   572  	genesis []byte,
   573  	vmID ids.ID,
   574  	fxIDs []ids.ID,
   575  	chainName string,
   576  	options ...common.Option,
   577  ) (*txs.CreateChainTx, error) {
   578  	toBurn := map[ids.ID]uint64{
   579  		b.context.AVAXAssetID: b.context.StaticFeeConfig.CreateBlockchainTxFee,
   580  	}
   581  	toStake := map[ids.ID]uint64{}
   582  
   583  	ops := common.NewOptions(options)
   584  	subnetAuth, err := b.authorizeSubnet(subnetID, ops)
   585  	if err != nil {
   586  		return nil, err
   587  	}
   588  
   589  	memo := ops.Memo()
   590  	bandwidth, err := math.Mul(uint64(len(fxIDs)), ids.IDLen)
   591  	if err != nil {
   592  		return nil, err
   593  	}
   594  	bandwidth, err = math.Add(bandwidth, uint64(len(chainName)))
   595  	if err != nil {
   596  		return nil, err
   597  	}
   598  	bandwidth, err = math.Add(bandwidth, uint64(len(genesis)))
   599  	if err != nil {
   600  		return nil, err
   601  	}
   602  	bandwidth, err = math.Add(bandwidth, uint64(len(memo)))
   603  	if err != nil {
   604  		return nil, err
   605  	}
   606  	dynamicComplexity := gas.Dimensions{
   607  		gas.Bandwidth: bandwidth,
   608  	}
   609  	authComplexity, err := fee.AuthComplexity(subnetAuth)
   610  	if err != nil {
   611  		return nil, err
   612  	}
   613  	complexity, err := fee.IntrinsicCreateChainTxComplexities.Add(
   614  		&dynamicComplexity,
   615  		&authComplexity,
   616  	)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  
   621  	inputs, outputs, _, err := b.spend(
   622  		toBurn,
   623  		toStake,
   624  		0,
   625  		complexity,
   626  		nil,
   627  		ops,
   628  	)
   629  	if err != nil {
   630  		return nil, err
   631  	}
   632  
   633  	utils.Sort(fxIDs)
   634  	tx := &txs.CreateChainTx{
   635  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   636  			NetworkID:    b.context.NetworkID,
   637  			BlockchainID: constants.PlatformChainID,
   638  			Ins:          inputs,
   639  			Outs:         outputs,
   640  			Memo:         memo,
   641  		}},
   642  		SubnetID:    subnetID,
   643  		ChainName:   chainName,
   644  		VMID:        vmID,
   645  		FxIDs:       fxIDs,
   646  		GenesisData: genesis,
   647  		SubnetAuth:  subnetAuth,
   648  	}
   649  	return tx, b.initCtx(tx)
   650  }
   651  
   652  func (b *builder) NewCreateSubnetTx(
   653  	owner *secp256k1fx.OutputOwners,
   654  	options ...common.Option,
   655  ) (*txs.CreateSubnetTx, error) {
   656  	toBurn := map[ids.ID]uint64{
   657  		b.context.AVAXAssetID: b.context.StaticFeeConfig.CreateSubnetTxFee,
   658  	}
   659  	toStake := map[ids.ID]uint64{}
   660  
   661  	ops := common.NewOptions(options)
   662  	memo := ops.Memo()
   663  	memoComplexity := gas.Dimensions{
   664  		gas.Bandwidth: uint64(len(memo)),
   665  	}
   666  	ownerComplexity, err := fee.OwnerComplexity(owner)
   667  	if err != nil {
   668  		return nil, err
   669  	}
   670  	complexity, err := fee.IntrinsicCreateSubnetTxComplexities.Add(
   671  		&memoComplexity,
   672  		&ownerComplexity,
   673  	)
   674  	if err != nil {
   675  		return nil, err
   676  	}
   677  
   678  	inputs, outputs, _, err := b.spend(
   679  		toBurn,
   680  		toStake,
   681  		0,
   682  		complexity,
   683  		nil,
   684  		ops,
   685  	)
   686  	if err != nil {
   687  		return nil, err
   688  	}
   689  
   690  	utils.Sort(owner.Addrs)
   691  	tx := &txs.CreateSubnetTx{
   692  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   693  			NetworkID:    b.context.NetworkID,
   694  			BlockchainID: constants.PlatformChainID,
   695  			Ins:          inputs,
   696  			Outs:         outputs,
   697  			Memo:         memo,
   698  		}},
   699  		Owner: owner,
   700  	}
   701  	return tx, b.initCtx(tx)
   702  }
   703  
   704  func (b *builder) NewTransferSubnetOwnershipTx(
   705  	subnetID ids.ID,
   706  	owner *secp256k1fx.OutputOwners,
   707  	options ...common.Option,
   708  ) (*txs.TransferSubnetOwnershipTx, error) {
   709  	toBurn := map[ids.ID]uint64{
   710  		b.context.AVAXAssetID: b.context.StaticFeeConfig.TxFee,
   711  	}
   712  	toStake := map[ids.ID]uint64{}
   713  
   714  	ops := common.NewOptions(options)
   715  	subnetAuth, err := b.authorizeSubnet(subnetID, ops)
   716  	if err != nil {
   717  		return nil, err
   718  	}
   719  
   720  	memo := ops.Memo()
   721  	memoComplexity := gas.Dimensions{
   722  		gas.Bandwidth: uint64(len(memo)),
   723  	}
   724  	authComplexity, err := fee.AuthComplexity(subnetAuth)
   725  	if err != nil {
   726  		return nil, err
   727  	}
   728  	ownerComplexity, err := fee.OwnerComplexity(owner)
   729  	if err != nil {
   730  		return nil, err
   731  	}
   732  	complexity, err := fee.IntrinsicTransferSubnetOwnershipTxComplexities.Add(
   733  		&memoComplexity,
   734  		&authComplexity,
   735  		&ownerComplexity,
   736  	)
   737  	if err != nil {
   738  		return nil, err
   739  	}
   740  
   741  	inputs, outputs, _, err := b.spend(
   742  		toBurn,
   743  		toStake,
   744  		0,
   745  		complexity,
   746  		nil,
   747  		ops,
   748  	)
   749  	if err != nil {
   750  		return nil, err
   751  	}
   752  
   753  	utils.Sort(owner.Addrs)
   754  	tx := &txs.TransferSubnetOwnershipTx{
   755  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   756  			NetworkID:    b.context.NetworkID,
   757  			BlockchainID: constants.PlatformChainID,
   758  			Ins:          inputs,
   759  			Outs:         outputs,
   760  			Memo:         memo,
   761  		}},
   762  		Subnet:     subnetID,
   763  		Owner:      owner,
   764  		SubnetAuth: subnetAuth,
   765  	}
   766  	return tx, b.initCtx(tx)
   767  }
   768  
   769  func (b *builder) NewImportTx(
   770  	sourceChainID ids.ID,
   771  	to *secp256k1fx.OutputOwners,
   772  	options ...common.Option,
   773  ) (*txs.ImportTx, error) {
   774  	ops := common.NewOptions(options)
   775  	utxos, err := b.backend.UTXOs(ops.Context(), sourceChainID)
   776  	if err != nil {
   777  		return nil, err
   778  	}
   779  
   780  	var (
   781  		addrs           = ops.Addresses(b.addrs)
   782  		minIssuanceTime = ops.MinIssuanceTime()
   783  		avaxAssetID     = b.context.AVAXAssetID
   784  		txFee           = b.context.StaticFeeConfig.TxFee
   785  
   786  		importedInputs  = make([]*avax.TransferableInput, 0, len(utxos))
   787  		importedAmounts = make(map[ids.ID]uint64)
   788  	)
   789  	// Iterate over the unlocked UTXOs
   790  	for _, utxo := range utxos {
   791  		out, ok := utxo.Out.(*secp256k1fx.TransferOutput)
   792  		if !ok {
   793  			continue
   794  		}
   795  
   796  		inputSigIndices, ok := common.MatchOwners(&out.OutputOwners, addrs, minIssuanceTime)
   797  		if !ok {
   798  			// We couldn't spend this UTXO, so we skip to the next one
   799  			continue
   800  		}
   801  
   802  		importedInputs = append(importedInputs, &avax.TransferableInput{
   803  			UTXOID: utxo.UTXOID,
   804  			Asset:  utxo.Asset,
   805  			In: &secp256k1fx.TransferInput{
   806  				Amt: out.Amt,
   807  				Input: secp256k1fx.Input{
   808  					SigIndices: inputSigIndices,
   809  				},
   810  			},
   811  		})
   812  
   813  		assetID := utxo.AssetID()
   814  		newImportedAmount, err := math.Add(importedAmounts[assetID], out.Amt)
   815  		if err != nil {
   816  			return nil, err
   817  		}
   818  		importedAmounts[assetID] = newImportedAmount
   819  	}
   820  	utils.Sort(importedInputs) // sort imported inputs
   821  
   822  	if len(importedInputs) == 0 {
   823  		return nil, fmt.Errorf(
   824  			"%w: no UTXOs available to import",
   825  			ErrInsufficientFunds,
   826  		)
   827  	}
   828  
   829  	outputs := make([]*avax.TransferableOutput, 0, len(importedAmounts))
   830  	for assetID, amount := range importedAmounts {
   831  		if assetID == avaxAssetID {
   832  			continue
   833  		}
   834  
   835  		outputs = append(outputs, &avax.TransferableOutput{
   836  			Asset: avax.Asset{ID: assetID},
   837  			Out: &secp256k1fx.TransferOutput{
   838  				Amt:          amount,
   839  				OutputOwners: *to,
   840  			},
   841  		})
   842  	}
   843  
   844  	memo := ops.Memo()
   845  	memoComplexity := gas.Dimensions{
   846  		gas.Bandwidth: uint64(len(memo)),
   847  	}
   848  	inputComplexity, err := fee.InputComplexity(importedInputs...)
   849  	if err != nil {
   850  		return nil, err
   851  	}
   852  	outputComplexity, err := fee.OutputComplexity(outputs...)
   853  	if err != nil {
   854  		return nil, err
   855  	}
   856  	complexity, err := fee.IntrinsicImportTxComplexities.Add(
   857  		&memoComplexity,
   858  		&inputComplexity,
   859  		&outputComplexity,
   860  	)
   861  	if err != nil {
   862  		return nil, err
   863  	}
   864  
   865  	var (
   866  		toBurn     = map[ids.ID]uint64{}
   867  		toStake    = map[ids.ID]uint64{}
   868  		excessAVAX uint64
   869  	)
   870  	if importedAVAX := importedAmounts[avaxAssetID]; importedAVAX < txFee {
   871  		toBurn[avaxAssetID] = txFee - importedAVAX
   872  	} else {
   873  		excessAVAX = importedAVAX - txFee
   874  	}
   875  
   876  	inputs, changeOutputs, _, err := b.spend(
   877  		toBurn,
   878  		toStake,
   879  		excessAVAX,
   880  		complexity,
   881  		to,
   882  		ops,
   883  	)
   884  	if err != nil {
   885  		return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
   886  	}
   887  	outputs = append(outputs, changeOutputs...)
   888  
   889  	avax.SortTransferableOutputs(outputs, txs.Codec) // sort imported outputs
   890  	tx := &txs.ImportTx{
   891  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   892  			NetworkID:    b.context.NetworkID,
   893  			BlockchainID: constants.PlatformChainID,
   894  			Ins:          inputs,
   895  			Outs:         outputs,
   896  			Memo:         memo,
   897  		}},
   898  		SourceChain:    sourceChainID,
   899  		ImportedInputs: importedInputs,
   900  	}
   901  	return tx, b.initCtx(tx)
   902  }
   903  
   904  func (b *builder) NewExportTx(
   905  	chainID ids.ID,
   906  	outputs []*avax.TransferableOutput,
   907  	options ...common.Option,
   908  ) (*txs.ExportTx, error) {
   909  	toBurn := map[ids.ID]uint64{
   910  		b.context.AVAXAssetID: b.context.StaticFeeConfig.TxFee,
   911  	}
   912  	for _, out := range outputs {
   913  		assetID := out.AssetID()
   914  		amountToBurn, err := math.Add(toBurn[assetID], out.Out.Amount())
   915  		if err != nil {
   916  			return nil, err
   917  		}
   918  		toBurn[assetID] = amountToBurn
   919  	}
   920  
   921  	toStake := map[ids.ID]uint64{}
   922  	ops := common.NewOptions(options)
   923  	memo := ops.Memo()
   924  	memoComplexity := gas.Dimensions{
   925  		gas.Bandwidth: uint64(len(memo)),
   926  	}
   927  	outputComplexity, err := fee.OutputComplexity(outputs...)
   928  	if err != nil {
   929  		return nil, err
   930  	}
   931  	complexity, err := fee.IntrinsicExportTxComplexities.Add(
   932  		&memoComplexity,
   933  		&outputComplexity,
   934  	)
   935  	if err != nil {
   936  		return nil, err
   937  	}
   938  
   939  	inputs, changeOutputs, _, err := b.spend(
   940  		toBurn,
   941  		toStake,
   942  		0,
   943  		complexity,
   944  		nil,
   945  		ops,
   946  	)
   947  	if err != nil {
   948  		return nil, err
   949  	}
   950  
   951  	avax.SortTransferableOutputs(outputs, txs.Codec) // sort exported outputs
   952  	tx := &txs.ExportTx{
   953  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
   954  			NetworkID:    b.context.NetworkID,
   955  			BlockchainID: constants.PlatformChainID,
   956  			Ins:          inputs,
   957  			Outs:         changeOutputs,
   958  			Memo:         memo,
   959  		}},
   960  		DestinationChain: chainID,
   961  		ExportedOutputs:  outputs,
   962  	}
   963  	return tx, b.initCtx(tx)
   964  }
   965  
   966  func (b *builder) NewTransformSubnetTx(
   967  	subnetID ids.ID,
   968  	assetID ids.ID,
   969  	initialSupply uint64,
   970  	maxSupply uint64,
   971  	minConsumptionRate uint64,
   972  	maxConsumptionRate uint64,
   973  	minValidatorStake uint64,
   974  	maxValidatorStake uint64,
   975  	minStakeDuration time.Duration,
   976  	maxStakeDuration time.Duration,
   977  	minDelegationFee uint32,
   978  	minDelegatorStake uint64,
   979  	maxValidatorWeightFactor byte,
   980  	uptimeRequirement uint32,
   981  	options ...common.Option,
   982  ) (*txs.TransformSubnetTx, error) {
   983  	toBurn := map[ids.ID]uint64{
   984  		b.context.AVAXAssetID: b.context.StaticFeeConfig.TransformSubnetTxFee,
   985  		assetID:               maxSupply - initialSupply,
   986  	}
   987  	toStake := map[ids.ID]uint64{}
   988  
   989  	ops := common.NewOptions(options)
   990  	subnetAuth, err := b.authorizeSubnet(subnetID, ops)
   991  	if err != nil {
   992  		return nil, err
   993  	}
   994  
   995  	inputs, outputs, _, err := b.spend(
   996  		toBurn,
   997  		toStake,
   998  		0,
   999  		gas.Dimensions{},
  1000  		nil,
  1001  		ops,
  1002  	)
  1003  	if err != nil {
  1004  		return nil, err
  1005  	}
  1006  
  1007  	tx := &txs.TransformSubnetTx{
  1008  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
  1009  			NetworkID:    b.context.NetworkID,
  1010  			BlockchainID: constants.PlatformChainID,
  1011  			Ins:          inputs,
  1012  			Outs:         outputs,
  1013  			Memo:         ops.Memo(),
  1014  		}},
  1015  		Subnet:                   subnetID,
  1016  		AssetID:                  assetID,
  1017  		InitialSupply:            initialSupply,
  1018  		MaximumSupply:            maxSupply,
  1019  		MinConsumptionRate:       minConsumptionRate,
  1020  		MaxConsumptionRate:       maxConsumptionRate,
  1021  		MinValidatorStake:        minValidatorStake,
  1022  		MaxValidatorStake:        maxValidatorStake,
  1023  		MinStakeDuration:         uint32(minStakeDuration / time.Second),
  1024  		MaxStakeDuration:         uint32(maxStakeDuration / time.Second),
  1025  		MinDelegationFee:         minDelegationFee,
  1026  		MinDelegatorStake:        minDelegatorStake,
  1027  		MaxValidatorWeightFactor: maxValidatorWeightFactor,
  1028  		UptimeRequirement:        uptimeRequirement,
  1029  		SubnetAuth:               subnetAuth,
  1030  	}
  1031  	return tx, b.initCtx(tx)
  1032  }
  1033  
  1034  func (b *builder) NewAddPermissionlessValidatorTx(
  1035  	vdr *txs.SubnetValidator,
  1036  	signer signer.Signer,
  1037  	assetID ids.ID,
  1038  	validationRewardsOwner *secp256k1fx.OutputOwners,
  1039  	delegationRewardsOwner *secp256k1fx.OutputOwners,
  1040  	shares uint32,
  1041  	options ...common.Option,
  1042  ) (*txs.AddPermissionlessValidatorTx, error) {
  1043  	avaxAssetID := b.context.AVAXAssetID
  1044  	toBurn := map[ids.ID]uint64{}
  1045  	if vdr.Subnet == constants.PrimaryNetworkID {
  1046  		toBurn[avaxAssetID] = b.context.StaticFeeConfig.AddPrimaryNetworkValidatorFee
  1047  	} else {
  1048  		toBurn[avaxAssetID] = b.context.StaticFeeConfig.AddSubnetValidatorFee
  1049  	}
  1050  	toStake := map[ids.ID]uint64{
  1051  		assetID: vdr.Wght,
  1052  	}
  1053  
  1054  	ops := common.NewOptions(options)
  1055  	memo := ops.Memo()
  1056  	memoComplexity := gas.Dimensions{
  1057  		gas.Bandwidth: uint64(len(memo)),
  1058  	}
  1059  	signerComplexity, err := fee.SignerComplexity(signer)
  1060  	if err != nil {
  1061  		return nil, err
  1062  	}
  1063  	validatorOwnerComplexity, err := fee.OwnerComplexity(validationRewardsOwner)
  1064  	if err != nil {
  1065  		return nil, err
  1066  	}
  1067  	delegatorOwnerComplexity, err := fee.OwnerComplexity(delegationRewardsOwner)
  1068  	if err != nil {
  1069  		return nil, err
  1070  	}
  1071  	complexity, err := fee.IntrinsicAddPermissionlessValidatorTxComplexities.Add(
  1072  		&memoComplexity,
  1073  		&signerComplexity,
  1074  		&validatorOwnerComplexity,
  1075  		&delegatorOwnerComplexity,
  1076  	)
  1077  	if err != nil {
  1078  		return nil, err
  1079  	}
  1080  
  1081  	inputs, baseOutputs, stakeOutputs, err := b.spend(
  1082  		toBurn,
  1083  		toStake,
  1084  		0,
  1085  		complexity,
  1086  		nil,
  1087  		ops,
  1088  	)
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  
  1093  	utils.Sort(validationRewardsOwner.Addrs)
  1094  	utils.Sort(delegationRewardsOwner.Addrs)
  1095  	tx := &txs.AddPermissionlessValidatorTx{
  1096  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
  1097  			NetworkID:    b.context.NetworkID,
  1098  			BlockchainID: constants.PlatformChainID,
  1099  			Ins:          inputs,
  1100  			Outs:         baseOutputs,
  1101  			Memo:         memo,
  1102  		}},
  1103  		Validator:             vdr.Validator,
  1104  		Subnet:                vdr.Subnet,
  1105  		Signer:                signer,
  1106  		StakeOuts:             stakeOutputs,
  1107  		ValidatorRewardsOwner: validationRewardsOwner,
  1108  		DelegatorRewardsOwner: delegationRewardsOwner,
  1109  		DelegationShares:      shares,
  1110  	}
  1111  	return tx, b.initCtx(tx)
  1112  }
  1113  
  1114  func (b *builder) NewAddPermissionlessDelegatorTx(
  1115  	vdr *txs.SubnetValidator,
  1116  	assetID ids.ID,
  1117  	rewardsOwner *secp256k1fx.OutputOwners,
  1118  	options ...common.Option,
  1119  ) (*txs.AddPermissionlessDelegatorTx, error) {
  1120  	avaxAssetID := b.context.AVAXAssetID
  1121  	toBurn := map[ids.ID]uint64{}
  1122  	if vdr.Subnet == constants.PrimaryNetworkID {
  1123  		toBurn[avaxAssetID] = b.context.StaticFeeConfig.AddPrimaryNetworkDelegatorFee
  1124  	} else {
  1125  		toBurn[avaxAssetID] = b.context.StaticFeeConfig.AddSubnetDelegatorFee
  1126  	}
  1127  	toStake := map[ids.ID]uint64{
  1128  		assetID: vdr.Wght,
  1129  	}
  1130  
  1131  	ops := common.NewOptions(options)
  1132  	memo := ops.Memo()
  1133  	memoComplexity := gas.Dimensions{
  1134  		gas.Bandwidth: uint64(len(memo)),
  1135  	}
  1136  	ownerComplexity, err := fee.OwnerComplexity(rewardsOwner)
  1137  	if err != nil {
  1138  		return nil, err
  1139  	}
  1140  	complexity, err := fee.IntrinsicAddPermissionlessDelegatorTxComplexities.Add(
  1141  		&memoComplexity,
  1142  		&ownerComplexity,
  1143  	)
  1144  	if err != nil {
  1145  		return nil, err
  1146  	}
  1147  
  1148  	inputs, baseOutputs, stakeOutputs, err := b.spend(
  1149  		toBurn,
  1150  		toStake,
  1151  		0,
  1152  		complexity,
  1153  		nil,
  1154  		ops,
  1155  	)
  1156  	if err != nil {
  1157  		return nil, err
  1158  	}
  1159  
  1160  	utils.Sort(rewardsOwner.Addrs)
  1161  	tx := &txs.AddPermissionlessDelegatorTx{
  1162  		BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
  1163  			NetworkID:    b.context.NetworkID,
  1164  			BlockchainID: constants.PlatformChainID,
  1165  			Ins:          inputs,
  1166  			Outs:         baseOutputs,
  1167  			Memo:         memo,
  1168  		}},
  1169  		Validator:              vdr.Validator,
  1170  		Subnet:                 vdr.Subnet,
  1171  		StakeOuts:              stakeOutputs,
  1172  		DelegationRewardsOwner: rewardsOwner,
  1173  	}
  1174  	return tx, b.initCtx(tx)
  1175  }
  1176  
  1177  func (b *builder) getBalance(
  1178  	chainID ids.ID,
  1179  	options *common.Options,
  1180  ) (
  1181  	balance map[ids.ID]uint64,
  1182  	err error,
  1183  ) {
  1184  	utxos, err := b.backend.UTXOs(options.Context(), chainID)
  1185  	if err != nil {
  1186  		return nil, err
  1187  	}
  1188  
  1189  	addrs := options.Addresses(b.addrs)
  1190  	minIssuanceTime := options.MinIssuanceTime()
  1191  	balance = make(map[ids.ID]uint64)
  1192  
  1193  	// Iterate over the UTXOs
  1194  	for _, utxo := range utxos {
  1195  		outIntf := utxo.Out
  1196  		if lockedOut, ok := outIntf.(*stakeable.LockOut); ok {
  1197  			if !options.AllowStakeableLocked() && lockedOut.Locktime > minIssuanceTime {
  1198  				// This output is currently locked, so this output can't be
  1199  				// burned.
  1200  				continue
  1201  			}
  1202  			outIntf = lockedOut.TransferableOut
  1203  		}
  1204  
  1205  		out, ok := outIntf.(*secp256k1fx.TransferOutput)
  1206  		if !ok {
  1207  			return nil, ErrUnknownOutputType
  1208  		}
  1209  
  1210  		_, ok = common.MatchOwners(&out.OutputOwners, addrs, minIssuanceTime)
  1211  		if !ok {
  1212  			// We couldn't spend this UTXO, so we skip to the next one
  1213  			continue
  1214  		}
  1215  
  1216  		assetID := utxo.AssetID()
  1217  		balance[assetID], err = math.Add(balance[assetID], out.Amt)
  1218  		if err != nil {
  1219  			return nil, err
  1220  		}
  1221  	}
  1222  	return balance, nil
  1223  }
  1224  
  1225  // spend takes in the requested burn amounts and the requested stake amounts.
  1226  //
  1227  //   - [toBurn] maps assetID to the amount of the asset to spend without
  1228  //     producing an output. This is typically used for fees. However, it can
  1229  //     also be used to consume some of an asset that will be produced in
  1230  //     separate outputs, such as ExportedOutputs. Only unlocked UTXOs are able
  1231  //     to be burned here.
  1232  //   - [toStake] maps assetID to the amount of the asset to spend and place into
  1233  //     the staked outputs. First locked UTXOs are attempted to be used for these
  1234  //     funds, and then unlocked UTXOs will be attempted to be used. There is no
  1235  //     preferential ordering on the unlock times.
  1236  //   - [excessAVAX] contains the amount of extra AVAX that spend can produce in
  1237  //     the change outputs in addition to the consumed and not burned AVAX.
  1238  //   - [complexity] contains the currently accrued transaction complexity that
  1239  //     will be used to calculate the required fees to be burned.
  1240  //   - [ownerOverride] optionally specifies the output owners to use for the
  1241  //     unlocked AVAX change output if no additional AVAX was needed to be
  1242  //     burned. If this value is nil, the default change owner is used.
  1243  func (b *builder) spend(
  1244  	toBurn map[ids.ID]uint64,
  1245  	toStake map[ids.ID]uint64,
  1246  	excessAVAX uint64,
  1247  	complexity gas.Dimensions,
  1248  	ownerOverride *secp256k1fx.OutputOwners,
  1249  	options *common.Options,
  1250  ) (
  1251  	inputs []*avax.TransferableInput,
  1252  	changeOutputs []*avax.TransferableOutput,
  1253  	stakeOutputs []*avax.TransferableOutput,
  1254  	err error,
  1255  ) {
  1256  	utxos, err := b.backend.UTXOs(options.Context(), constants.PlatformChainID)
  1257  	if err != nil {
  1258  		return nil, nil, nil, err
  1259  	}
  1260  
  1261  	addrs := options.Addresses(b.addrs)
  1262  	minIssuanceTime := options.MinIssuanceTime()
  1263  
  1264  	addr, ok := addrs.Peek()
  1265  	if !ok {
  1266  		return nil, nil, nil, ErrNoChangeAddress
  1267  	}
  1268  	changeOwner := options.ChangeOwner(&secp256k1fx.OutputOwners{
  1269  		Threshold: 1,
  1270  		Addrs:     []ids.ShortID{addr},
  1271  	})
  1272  	if ownerOverride == nil {
  1273  		ownerOverride = changeOwner
  1274  	}
  1275  
  1276  	s := spendHelper{
  1277  		weights:  b.context.ComplexityWeights,
  1278  		gasPrice: b.context.GasPrice,
  1279  
  1280  		toBurn:     toBurn,
  1281  		toStake:    toStake,
  1282  		complexity: complexity,
  1283  
  1284  		// Initialize the return values with empty slices to preserve backward
  1285  		// compatibility of the json representation of transactions with no
  1286  		// inputs or outputs.
  1287  		inputs:        make([]*avax.TransferableInput, 0),
  1288  		changeOutputs: make([]*avax.TransferableOutput, 0),
  1289  		stakeOutputs:  make([]*avax.TransferableOutput, 0),
  1290  	}
  1291  
  1292  	utxosByLocktime := splitByLocktime(utxos, minIssuanceTime)
  1293  	for _, utxo := range utxosByLocktime.locked {
  1294  		assetID := utxo.AssetID()
  1295  		if !s.shouldConsumeLockedAsset(assetID) {
  1296  			continue
  1297  		}
  1298  
  1299  		out, locktime, err := unwrapOutput(utxo.Out)
  1300  		if err != nil {
  1301  			return nil, nil, nil, err
  1302  		}
  1303  
  1304  		inputSigIndices, ok := common.MatchOwners(&out.OutputOwners, addrs, minIssuanceTime)
  1305  		if !ok {
  1306  			// We couldn't spend this UTXO, so we skip to the next one
  1307  			continue
  1308  		}
  1309  
  1310  		err = s.addInput(&avax.TransferableInput{
  1311  			UTXOID: utxo.UTXOID,
  1312  			Asset:  utxo.Asset,
  1313  			In: &stakeable.LockIn{
  1314  				Locktime: locktime,
  1315  				TransferableIn: &secp256k1fx.TransferInput{
  1316  					Amt: out.Amt,
  1317  					Input: secp256k1fx.Input{
  1318  						SigIndices: inputSigIndices,
  1319  					},
  1320  				},
  1321  			},
  1322  		})
  1323  		if err != nil {
  1324  			return nil, nil, nil, err
  1325  		}
  1326  
  1327  		excess := s.consumeLockedAsset(assetID, out.Amt)
  1328  		err = s.addStakedOutput(&avax.TransferableOutput{
  1329  			Asset: utxo.Asset,
  1330  			Out: &stakeable.LockOut{
  1331  				Locktime: locktime,
  1332  				TransferableOut: &secp256k1fx.TransferOutput{
  1333  					Amt:          out.Amt - excess,
  1334  					OutputOwners: out.OutputOwners,
  1335  				},
  1336  			},
  1337  		})
  1338  		if err != nil {
  1339  			return nil, nil, nil, err
  1340  		}
  1341  
  1342  		if excess == 0 {
  1343  			continue
  1344  		}
  1345  
  1346  		// This input had extra value, so some of it must be returned
  1347  		err = s.addChangeOutput(&avax.TransferableOutput{
  1348  			Asset: utxo.Asset,
  1349  			Out: &stakeable.LockOut{
  1350  				Locktime: locktime,
  1351  				TransferableOut: &secp256k1fx.TransferOutput{
  1352  					Amt:          excess,
  1353  					OutputOwners: out.OutputOwners,
  1354  				},
  1355  			},
  1356  		})
  1357  		if err != nil {
  1358  			return nil, nil, nil, err
  1359  		}
  1360  	}
  1361  
  1362  	// Add all the remaining stake amounts assuming unlocked UTXOs.
  1363  	for assetID, amount := range s.toStake {
  1364  		if amount == 0 {
  1365  			continue
  1366  		}
  1367  
  1368  		err = s.addStakedOutput(&avax.TransferableOutput{
  1369  			Asset: avax.Asset{
  1370  				ID: assetID,
  1371  			},
  1372  			Out: &secp256k1fx.TransferOutput{
  1373  				Amt:          amount,
  1374  				OutputOwners: *changeOwner,
  1375  			},
  1376  		})
  1377  		if err != nil {
  1378  			return nil, nil, nil, err
  1379  		}
  1380  	}
  1381  
  1382  	// AVAX is handled last to account for fees.
  1383  	utxosByAVAXAssetID := splitByAssetID(utxosByLocktime.unlocked, b.context.AVAXAssetID)
  1384  	for _, utxo := range utxosByAVAXAssetID.other {
  1385  		assetID := utxo.AssetID()
  1386  		if !s.shouldConsumeAsset(assetID) {
  1387  			continue
  1388  		}
  1389  
  1390  		out, _, err := unwrapOutput(utxo.Out)
  1391  		if err != nil {
  1392  			return nil, nil, nil, err
  1393  		}
  1394  
  1395  		inputSigIndices, ok := common.MatchOwners(&out.OutputOwners, addrs, minIssuanceTime)
  1396  		if !ok {
  1397  			// We couldn't spend this UTXO, so we skip to the next one
  1398  			continue
  1399  		}
  1400  
  1401  		err = s.addInput(&avax.TransferableInput{
  1402  			UTXOID: utxo.UTXOID,
  1403  			Asset:  utxo.Asset,
  1404  			In: &secp256k1fx.TransferInput{
  1405  				Amt: out.Amt,
  1406  				Input: secp256k1fx.Input{
  1407  					SigIndices: inputSigIndices,
  1408  				},
  1409  			},
  1410  		})
  1411  		if err != nil {
  1412  			return nil, nil, nil, err
  1413  		}
  1414  
  1415  		excess := s.consumeAsset(assetID, out.Amt)
  1416  		if excess == 0 {
  1417  			continue
  1418  		}
  1419  
  1420  		// This input had extra value, so some of it must be returned
  1421  		err = s.addChangeOutput(&avax.TransferableOutput{
  1422  			Asset: utxo.Asset,
  1423  			Out: &secp256k1fx.TransferOutput{
  1424  				Amt:          excess,
  1425  				OutputOwners: *changeOwner,
  1426  			},
  1427  		})
  1428  		if err != nil {
  1429  			return nil, nil, nil, err
  1430  		}
  1431  	}
  1432  
  1433  	for _, utxo := range utxosByAVAXAssetID.requested {
  1434  		requiredFee, err := s.calculateFee()
  1435  		if err != nil {
  1436  			return nil, nil, nil, err
  1437  		}
  1438  
  1439  		// If we don't need to burn or stake additional AVAX and we have
  1440  		// consumed enough AVAX to pay the required fee, we should stop
  1441  		// consuming UTXOs.
  1442  		if !s.shouldConsumeAsset(b.context.AVAXAssetID) && excessAVAX >= requiredFee {
  1443  			break
  1444  		}
  1445  
  1446  		out, _, err := unwrapOutput(utxo.Out)
  1447  		if err != nil {
  1448  			return nil, nil, nil, err
  1449  		}
  1450  
  1451  		inputSigIndices, ok := common.MatchOwners(&out.OutputOwners, addrs, minIssuanceTime)
  1452  		if !ok {
  1453  			// We couldn't spend this UTXO, so we skip to the next one
  1454  			continue
  1455  		}
  1456  
  1457  		err = s.addInput(&avax.TransferableInput{
  1458  			UTXOID: utxo.UTXOID,
  1459  			Asset:  utxo.Asset,
  1460  			In: &secp256k1fx.TransferInput{
  1461  				Amt: out.Amt,
  1462  				Input: secp256k1fx.Input{
  1463  					SigIndices: inputSigIndices,
  1464  				},
  1465  			},
  1466  		})
  1467  		if err != nil {
  1468  			return nil, nil, nil, err
  1469  		}
  1470  
  1471  		excess := s.consumeAsset(b.context.AVAXAssetID, out.Amt)
  1472  		excessAVAX, err = math.Add(excessAVAX, excess)
  1473  		if err != nil {
  1474  			return nil, nil, nil, err
  1475  		}
  1476  
  1477  		// If we need to consume additional AVAX, we should be returning the
  1478  		// change to the change address.
  1479  		ownerOverride = changeOwner
  1480  	}
  1481  
  1482  	if err := s.verifyAssetsConsumed(); err != nil {
  1483  		return nil, nil, nil, err
  1484  	}
  1485  
  1486  	requiredFee, err := s.calculateFee()
  1487  	if err != nil {
  1488  		return nil, nil, nil, err
  1489  	}
  1490  	if excessAVAX < requiredFee {
  1491  		return nil, nil, nil, fmt.Errorf(
  1492  			"%w: provided UTXOs needed %d more nAVAX (%q)",
  1493  			ErrInsufficientFunds,
  1494  			requiredFee-excessAVAX,
  1495  			b.context.AVAXAssetID,
  1496  		)
  1497  	}
  1498  
  1499  	secpExcessAVAXOutput := &secp256k1fx.TransferOutput{
  1500  		Amt:          0, // Populated later if used
  1501  		OutputOwners: *ownerOverride,
  1502  	}
  1503  	excessAVAXOutput := &avax.TransferableOutput{
  1504  		Asset: avax.Asset{
  1505  			ID: b.context.AVAXAssetID,
  1506  		},
  1507  		Out: secpExcessAVAXOutput,
  1508  	}
  1509  	if err := s.addOutputComplexity(excessAVAXOutput); err != nil {
  1510  		return nil, nil, nil, err
  1511  	}
  1512  
  1513  	requiredFeeWithChange, err := s.calculateFee()
  1514  	if err != nil {
  1515  		return nil, nil, nil, err
  1516  	}
  1517  	if excessAVAX > requiredFeeWithChange {
  1518  		// It is worth adding the change output
  1519  		secpExcessAVAXOutput.Amt = excessAVAX - requiredFeeWithChange
  1520  		s.changeOutputs = append(s.changeOutputs, excessAVAXOutput)
  1521  	}
  1522  
  1523  	utils.Sort(s.inputs)                                     // sort inputs
  1524  	avax.SortTransferableOutputs(s.changeOutputs, txs.Codec) // sort the change outputs
  1525  	avax.SortTransferableOutputs(s.stakeOutputs, txs.Codec)  // sort stake outputs
  1526  	return s.inputs, s.changeOutputs, s.stakeOutputs, nil
  1527  }
  1528  
  1529  func (b *builder) authorizeSubnet(subnetID ids.ID, options *common.Options) (*secp256k1fx.Input, error) {
  1530  	ownerIntf, err := b.backend.GetSubnetOwner(options.Context(), subnetID)
  1531  	if err != nil {
  1532  		return nil, fmt.Errorf(
  1533  			"failed to fetch subnet owner for %q: %w",
  1534  			subnetID,
  1535  			err,
  1536  		)
  1537  	}
  1538  	owner, ok := ownerIntf.(*secp256k1fx.OutputOwners)
  1539  	if !ok {
  1540  		return nil, ErrUnknownOwnerType
  1541  	}
  1542  
  1543  	addrs := options.Addresses(b.addrs)
  1544  	minIssuanceTime := options.MinIssuanceTime()
  1545  	inputSigIndices, ok := common.MatchOwners(owner, addrs, minIssuanceTime)
  1546  	if !ok {
  1547  		// We can't authorize the subnet
  1548  		return nil, ErrInsufficientAuthorization
  1549  	}
  1550  	return &secp256k1fx.Input{
  1551  		SigIndices: inputSigIndices,
  1552  	}, nil
  1553  }
  1554  
  1555  func (b *builder) initCtx(tx txs.UnsignedTx) error {
  1556  	ctx, err := NewSnowContext(b.context.NetworkID, b.context.AVAXAssetID)
  1557  	if err != nil {
  1558  		return err
  1559  	}
  1560  
  1561  	tx.InitCtx(ctx)
  1562  	return nil
  1563  }
  1564  
  1565  type spendHelper struct {
  1566  	weights  gas.Dimensions
  1567  	gasPrice gas.Price
  1568  
  1569  	toBurn     map[ids.ID]uint64
  1570  	toStake    map[ids.ID]uint64
  1571  	complexity gas.Dimensions
  1572  
  1573  	inputs        []*avax.TransferableInput
  1574  	changeOutputs []*avax.TransferableOutput
  1575  	stakeOutputs  []*avax.TransferableOutput
  1576  }
  1577  
  1578  func (s *spendHelper) addInput(input *avax.TransferableInput) error {
  1579  	newInputComplexity, err := fee.InputComplexity(input)
  1580  	if err != nil {
  1581  		return err
  1582  	}
  1583  	s.complexity, err = s.complexity.Add(&newInputComplexity)
  1584  	if err != nil {
  1585  		return err
  1586  	}
  1587  
  1588  	s.inputs = append(s.inputs, input)
  1589  	return nil
  1590  }
  1591  
  1592  func (s *spendHelper) addChangeOutput(output *avax.TransferableOutput) error {
  1593  	s.changeOutputs = append(s.changeOutputs, output)
  1594  	return s.addOutputComplexity(output)
  1595  }
  1596  
  1597  func (s *spendHelper) addStakedOutput(output *avax.TransferableOutput) error {
  1598  	s.stakeOutputs = append(s.stakeOutputs, output)
  1599  	return s.addOutputComplexity(output)
  1600  }
  1601  
  1602  func (s *spendHelper) addOutputComplexity(output *avax.TransferableOutput) error {
  1603  	newOutputComplexity, err := fee.OutputComplexity(output)
  1604  	if err != nil {
  1605  		return err
  1606  	}
  1607  	s.complexity, err = s.complexity.Add(&newOutputComplexity)
  1608  	return err
  1609  }
  1610  
  1611  func (s *spendHelper) shouldConsumeLockedAsset(assetID ids.ID) bool {
  1612  	return s.toStake[assetID] != 0
  1613  }
  1614  
  1615  func (s *spendHelper) shouldConsumeAsset(assetID ids.ID) bool {
  1616  	return s.toBurn[assetID] != 0 || s.shouldConsumeLockedAsset(assetID)
  1617  }
  1618  
  1619  func (s *spendHelper) consumeLockedAsset(assetID ids.ID, amount uint64) uint64 {
  1620  	// Stake any value that should be staked
  1621  	toStake := min(
  1622  		s.toStake[assetID], // Amount we still need to stake
  1623  		amount,             // Amount available to stake
  1624  	)
  1625  	s.toStake[assetID] -= toStake
  1626  	return amount - toStake
  1627  }
  1628  
  1629  func (s *spendHelper) consumeAsset(assetID ids.ID, amount uint64) uint64 {
  1630  	// Burn any value that should be burned
  1631  	toBurn := min(
  1632  		s.toBurn[assetID], // Amount we still need to burn
  1633  		amount,            // Amount available to burn
  1634  	)
  1635  	s.toBurn[assetID] -= toBurn
  1636  
  1637  	// Stake any remaining value that should be staked
  1638  	return s.consumeLockedAsset(assetID, amount-toBurn)
  1639  }
  1640  
  1641  func (s *spendHelper) calculateFee() (uint64, error) {
  1642  	gas, err := s.complexity.ToGas(s.weights)
  1643  	if err != nil {
  1644  		return 0, err
  1645  	}
  1646  	return gas.Cost(s.gasPrice)
  1647  }
  1648  
  1649  func (s *spendHelper) verifyAssetsConsumed() error {
  1650  	for assetID, amount := range s.toStake {
  1651  		if amount == 0 {
  1652  			continue
  1653  		}
  1654  
  1655  		return fmt.Errorf(
  1656  			"%w: provided UTXOs need %d more units of asset %q to stake",
  1657  			ErrInsufficientFunds,
  1658  			amount,
  1659  			assetID,
  1660  		)
  1661  	}
  1662  	for assetID, amount := range s.toBurn {
  1663  		if amount == 0 {
  1664  			continue
  1665  		}
  1666  
  1667  		return fmt.Errorf(
  1668  			"%w: provided UTXOs need %d more units of asset %q",
  1669  			ErrInsufficientFunds,
  1670  			amount,
  1671  			assetID,
  1672  		)
  1673  	}
  1674  	return nil
  1675  }
  1676  
  1677  type utxosByLocktime struct {
  1678  	unlocked []*avax.UTXO
  1679  	locked   []*avax.UTXO
  1680  }
  1681  
  1682  // splitByLocktime separates the provided UTXOs into two slices:
  1683  // 1. UTXOs that are unlocked with the provided issuance time
  1684  // 2. UTXOs that are locked with the provided issuance time
  1685  func splitByLocktime(utxos []*avax.UTXO, minIssuanceTime uint64) utxosByLocktime {
  1686  	split := utxosByLocktime{
  1687  		unlocked: make([]*avax.UTXO, 0, len(utxos)),
  1688  		locked:   make([]*avax.UTXO, 0, len(utxos)),
  1689  	}
  1690  	for _, utxo := range utxos {
  1691  		if lockedOut, ok := utxo.Out.(*stakeable.LockOut); ok && minIssuanceTime < lockedOut.Locktime {
  1692  			split.locked = append(split.locked, utxo)
  1693  		} else {
  1694  			split.unlocked = append(split.unlocked, utxo)
  1695  		}
  1696  	}
  1697  	return split
  1698  }
  1699  
  1700  type utxosByAssetID struct {
  1701  	requested []*avax.UTXO
  1702  	other     []*avax.UTXO
  1703  }
  1704  
  1705  // splitByAssetID separates the provided UTXOs into two slices:
  1706  // 1. UTXOs with the provided assetID
  1707  // 2. UTXOs with a different assetID
  1708  func splitByAssetID(utxos []*avax.UTXO, assetID ids.ID) utxosByAssetID {
  1709  	split := utxosByAssetID{
  1710  		requested: make([]*avax.UTXO, 0, len(utxos)),
  1711  		other:     make([]*avax.UTXO, 0, len(utxos)),
  1712  	}
  1713  	for _, utxo := range utxos {
  1714  		if utxo.AssetID() == assetID {
  1715  			split.requested = append(split.requested, utxo)
  1716  		} else {
  1717  			split.other = append(split.other, utxo)
  1718  		}
  1719  	}
  1720  	return split
  1721  }
  1722  
  1723  // unwrapOutput returns the *secp256k1fx.TransferOutput that was, potentially,
  1724  // wrapped by a *stakeable.LockOut.
  1725  //
  1726  // If the output was stakeable and locked, the locktime is returned. Otherwise,
  1727  // the locktime returned will be 0.
  1728  //
  1729  // If the output is not a, potentially wrapped, *secp256k1fx.TransferOutput, an
  1730  // error is returned.
  1731  func unwrapOutput(output verify.State) (*secp256k1fx.TransferOutput, uint64, error) {
  1732  	var locktime uint64
  1733  	if lockedOut, ok := output.(*stakeable.LockOut); ok {
  1734  		output = lockedOut.TransferableOut
  1735  		locktime = lockedOut.Locktime
  1736  	}
  1737  
  1738  	unwrappedOutput, ok := output.(*secp256k1fx.TransferOutput)
  1739  	if !ok {
  1740  		return nil, 0, ErrUnknownOutputType
  1741  	}
  1742  	return unwrappedOutput, locktime, nil
  1743  }