github.com/ava-labs/avalanchego@v1.11.11/wallet/subnet/primary/api.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package primary 5 6 import ( 7 "context" 8 "fmt" 9 10 "github.com/ava-labs/coreth/ethclient" 11 "github.com/ava-labs/coreth/plugin/evm" 12 13 "github.com/ava-labs/avalanchego/api/info" 14 "github.com/ava-labs/avalanchego/codec" 15 "github.com/ava-labs/avalanchego/ids" 16 "github.com/ava-labs/avalanchego/utils/constants" 17 "github.com/ava-labs/avalanchego/utils/rpc" 18 "github.com/ava-labs/avalanchego/utils/set" 19 "github.com/ava-labs/avalanchego/vms/avm" 20 "github.com/ava-labs/avalanchego/vms/components/avax" 21 "github.com/ava-labs/avalanchego/vms/platformvm" 22 "github.com/ava-labs/avalanchego/vms/platformvm/txs" 23 "github.com/ava-labs/avalanchego/wallet/chain/c" 24 "github.com/ava-labs/avalanchego/wallet/chain/p" 25 "github.com/ava-labs/avalanchego/wallet/chain/x" 26 27 pbuilder "github.com/ava-labs/avalanchego/wallet/chain/p/builder" 28 xbuilder "github.com/ava-labs/avalanchego/wallet/chain/x/builder" 29 walletcommon "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" 30 ethcommon "github.com/ethereum/go-ethereum/common" 31 ) 32 33 const ( 34 MainnetAPIURI = "https://api.avax.network" 35 FujiAPIURI = "https://api.avax-test.network" 36 LocalAPIURI = "http://localhost:9650" 37 38 fetchLimit = 1024 39 ) 40 41 // TODO: Refactor UTXOClient definition to allow the client implementations to 42 // perform their own assertions. 43 var ( 44 _ UTXOClient = platformvm.Client(nil) 45 _ UTXOClient = avm.Client(nil) 46 ) 47 48 type UTXOClient interface { 49 GetAtomicUTXOs( 50 ctx context.Context, 51 addrs []ids.ShortID, 52 sourceChain string, 53 limit uint32, 54 startAddress ids.ShortID, 55 startUTXOID ids.ID, 56 options ...rpc.Option, 57 ) ([][]byte, ids.ShortID, ids.ID, error) 58 } 59 60 type AVAXState struct { 61 PClient platformvm.Client 62 PCTX *pbuilder.Context 63 XClient avm.Client 64 XCTX *xbuilder.Context 65 CClient evm.Client 66 CCTX *c.Context 67 UTXOs walletcommon.UTXOs 68 } 69 70 func FetchState( 71 ctx context.Context, 72 uri string, 73 addrs set.Set[ids.ShortID], 74 ) ( 75 *AVAXState, 76 error, 77 ) { 78 infoClient := info.NewClient(uri) 79 pClient := platformvm.NewClient(uri) 80 xClient := avm.NewClient(uri, "X") 81 cClient := evm.NewCChainClient(uri) 82 83 pCTX, err := p.NewContextFromClients(ctx, infoClient, xClient, pClient) 84 if err != nil { 85 return nil, err 86 } 87 88 xCTX, err := x.NewContextFromClients(ctx, infoClient, xClient) 89 if err != nil { 90 return nil, err 91 } 92 93 cCTX, err := c.NewContextFromClients(ctx, infoClient, xClient) 94 if err != nil { 95 return nil, err 96 } 97 98 utxos := walletcommon.NewUTXOs() 99 addrList := addrs.List() 100 chains := []struct { 101 id ids.ID 102 client UTXOClient 103 codec codec.Manager 104 }{ 105 { 106 id: constants.PlatformChainID, 107 client: pClient, 108 codec: txs.Codec, 109 }, 110 { 111 id: xCTX.BlockchainID, 112 client: xClient, 113 codec: xbuilder.Parser.Codec(), 114 }, 115 { 116 id: cCTX.BlockchainID, 117 client: cClient, 118 codec: evm.Codec, 119 }, 120 } 121 for _, destinationChain := range chains { 122 for _, sourceChain := range chains { 123 err = AddAllUTXOs( 124 ctx, 125 utxos, 126 destinationChain.client, 127 destinationChain.codec, 128 sourceChain.id, 129 destinationChain.id, 130 addrList, 131 ) 132 if err != nil { 133 return nil, err 134 } 135 } 136 } 137 return &AVAXState{ 138 PClient: pClient, 139 PCTX: pCTX, 140 XClient: xClient, 141 XCTX: xCTX, 142 CClient: cClient, 143 CCTX: cCTX, 144 UTXOs: utxos, 145 }, nil 146 } 147 148 type EthState struct { 149 Client ethclient.Client 150 Accounts map[ethcommon.Address]*c.Account 151 } 152 153 func FetchEthState( 154 ctx context.Context, 155 uri string, 156 addrs set.Set[ethcommon.Address], 157 ) (*EthState, error) { 158 path := fmt.Sprintf( 159 "%s/ext/%s/C/rpc", 160 uri, 161 constants.ChainAliasPrefix, 162 ) 163 client, err := ethclient.Dial(path) 164 if err != nil { 165 return nil, err 166 } 167 168 accounts := make(map[ethcommon.Address]*c.Account, addrs.Len()) 169 for addr := range addrs { 170 balance, err := client.BalanceAt(ctx, addr, nil) 171 if err != nil { 172 return nil, err 173 } 174 nonce, err := client.NonceAt(ctx, addr, nil) 175 if err != nil { 176 return nil, err 177 } 178 accounts[addr] = &c.Account{ 179 Balance: balance, 180 Nonce: nonce, 181 } 182 } 183 return &EthState{ 184 Client: client, 185 Accounts: accounts, 186 }, nil 187 } 188 189 // AddAllUTXOs fetches all the UTXOs referenced by [addresses] that were sent 190 // from [sourceChainID] to [destinationChainID] from the [client]. It then uses 191 // [codec] to parse the returned UTXOs and it adds them into [utxos]. If [ctx] 192 // expires, then the returned error will be immediately reported. 193 func AddAllUTXOs( 194 ctx context.Context, 195 utxos walletcommon.UTXOs, 196 client UTXOClient, 197 codec codec.Manager, 198 sourceChainID ids.ID, 199 destinationChainID ids.ID, 200 addrs []ids.ShortID, 201 ) error { 202 var ( 203 sourceChainIDStr = sourceChainID.String() 204 startAddr ids.ShortID 205 startUTXO ids.ID 206 ) 207 for { 208 utxosBytes, endAddr, endUTXO, err := client.GetAtomicUTXOs( 209 ctx, 210 addrs, 211 sourceChainIDStr, 212 fetchLimit, 213 startAddr, 214 startUTXO, 215 ) 216 if err != nil { 217 return err 218 } 219 220 for _, utxoBytes := range utxosBytes { 221 var utxo avax.UTXO 222 _, err := codec.Unmarshal(utxoBytes, &utxo) 223 if err != nil { 224 return err 225 } 226 227 if err := utxos.AddUTXO(ctx, sourceChainID, destinationChainID, &utxo); err != nil { 228 return err 229 } 230 } 231 232 if len(utxosBytes) < fetchLimit { 233 break 234 } 235 236 // Update the vars to query the next page of UTXOs. 237 startAddr = endAddr 238 startUTXO = endUTXO 239 } 240 return nil 241 }