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