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  }