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

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package c
     5  
     6  import (
     7  	"context"
     8  	"math/big"
     9  	"time"
    10  
    11  	"github.com/ava-labs/coreth/ethclient"
    12  	"github.com/ava-labs/coreth/plugin/evm"
    13  
    14  	"github.com/ava-labs/avalanchego/ids"
    15  	"github.com/ava-labs/avalanchego/utils/rpc"
    16  	"github.com/ava-labs/avalanchego/vms/secp256k1fx"
    17  	"github.com/ava-labs/avalanchego/wallet/subnet/primary/common"
    18  
    19  	ethcommon "github.com/ethereum/go-ethereum/common"
    20  )
    21  
    22  var _ Wallet = (*wallet)(nil)
    23  
    24  type Wallet interface {
    25  	// Builder returns the builder that will be used to create the transactions.
    26  	Builder() Builder
    27  
    28  	// Signer returns the signer that will be used to sign the transactions.
    29  	Signer() Signer
    30  
    31  	// IssueImportTx creates, signs, and issues an import transaction that
    32  	// attempts to consume all the available UTXOs and import the funds to [to].
    33  	//
    34  	// - [chainID] specifies the chain to be importing funds from.
    35  	// - [to] specifies where to send the imported funds to.
    36  	IssueImportTx(
    37  		chainID ids.ID,
    38  		to ethcommon.Address,
    39  		options ...common.Option,
    40  	) (*evm.Tx, error)
    41  
    42  	// IssueExportTx creates, signs, and issues an export transaction that
    43  	// attempts to send all the provided [outputs] to the requested [chainID].
    44  	//
    45  	// - [chainID] specifies the chain to be exporting the funds to.
    46  	// - [outputs] specifies the outputs to send to the [chainID].
    47  	IssueExportTx(
    48  		chainID ids.ID,
    49  		outputs []*secp256k1fx.TransferOutput,
    50  		options ...common.Option,
    51  	) (*evm.Tx, error)
    52  
    53  	// IssueUnsignedAtomicTx signs and issues the unsigned tx.
    54  	IssueUnsignedAtomicTx(
    55  		utx evm.UnsignedAtomicTx,
    56  		options ...common.Option,
    57  	) (*evm.Tx, error)
    58  
    59  	// IssueAtomicTx issues the signed tx.
    60  	IssueAtomicTx(
    61  		tx *evm.Tx,
    62  		options ...common.Option,
    63  	) error
    64  }
    65  
    66  func NewWallet(
    67  	builder Builder,
    68  	signer Signer,
    69  	avaxClient evm.Client,
    70  	ethClient ethclient.Client,
    71  	backend Backend,
    72  ) Wallet {
    73  	return &wallet{
    74  		Backend:    backend,
    75  		builder:    builder,
    76  		signer:     signer,
    77  		avaxClient: avaxClient,
    78  		ethClient:  ethClient,
    79  	}
    80  }
    81  
    82  type wallet struct {
    83  	Backend
    84  	builder    Builder
    85  	signer     Signer
    86  	avaxClient evm.Client
    87  	ethClient  ethclient.Client
    88  }
    89  
    90  func (w *wallet) Builder() Builder {
    91  	return w.builder
    92  }
    93  
    94  func (w *wallet) Signer() Signer {
    95  	return w.signer
    96  }
    97  
    98  func (w *wallet) IssueImportTx(
    99  	chainID ids.ID,
   100  	to ethcommon.Address,
   101  	options ...common.Option,
   102  ) (*evm.Tx, error) {
   103  	baseFee, err := w.baseFee(options)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	utx, err := w.builder.NewImportTx(chainID, to, baseFee, options...)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return w.IssueUnsignedAtomicTx(utx, options...)
   113  }
   114  
   115  func (w *wallet) IssueExportTx(
   116  	chainID ids.ID,
   117  	outputs []*secp256k1fx.TransferOutput,
   118  	options ...common.Option,
   119  ) (*evm.Tx, error) {
   120  	baseFee, err := w.baseFee(options)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	utx, err := w.builder.NewExportTx(chainID, outputs, baseFee, options...)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	return w.IssueUnsignedAtomicTx(utx, options...)
   130  }
   131  
   132  func (w *wallet) IssueUnsignedAtomicTx(
   133  	utx evm.UnsignedAtomicTx,
   134  	options ...common.Option,
   135  ) (*evm.Tx, error) {
   136  	ops := common.NewOptions(options)
   137  	ctx := ops.Context()
   138  	tx, err := SignUnsignedAtomic(ctx, w.signer, utx)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	return tx, w.IssueAtomicTx(tx, options...)
   144  }
   145  
   146  func (w *wallet) IssueAtomicTx(
   147  	tx *evm.Tx,
   148  	options ...common.Option,
   149  ) error {
   150  	ops := common.NewOptions(options)
   151  	ctx := ops.Context()
   152  	txID, err := w.avaxClient.IssueTx(ctx, tx.SignedBytes())
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	if f := ops.PostIssuanceFunc(); f != nil {
   158  		f(txID)
   159  	}
   160  
   161  	if ops.AssumeDecided() {
   162  		return w.Backend.AcceptAtomicTx(ctx, tx)
   163  	}
   164  
   165  	if err := awaitTxAccepted(w.avaxClient, ctx, txID, ops.PollFrequency()); err != nil {
   166  		return err
   167  	}
   168  
   169  	return w.Backend.AcceptAtomicTx(ctx, tx)
   170  }
   171  
   172  func (w *wallet) baseFee(options []common.Option) (*big.Int, error) {
   173  	ops := common.NewOptions(options)
   174  	baseFee := ops.BaseFee(nil)
   175  	if baseFee != nil {
   176  		return baseFee, nil
   177  	}
   178  
   179  	ctx := ops.Context()
   180  	return w.ethClient.EstimateBaseFee(ctx)
   181  }
   182  
   183  // TODO: Upstream this function into coreth.
   184  func awaitTxAccepted(
   185  	c evm.Client,
   186  	ctx context.Context,
   187  	txID ids.ID,
   188  	freq time.Duration,
   189  	options ...rpc.Option,
   190  ) error {
   191  	ticker := time.NewTicker(freq)
   192  	defer ticker.Stop()
   193  
   194  	for {
   195  		status, err := c.GetAtomicTxStatus(ctx, txID, options...)
   196  		if err != nil {
   197  			return err
   198  		}
   199  
   200  		if status == evm.Accepted {
   201  			return nil
   202  		}
   203  
   204  		select {
   205  		case <-ticker.C:
   206  		case <-ctx.Done():
   207  			return ctx.Err()
   208  		}
   209  	}
   210  }