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 }