github.com/MetalBlockchain/metalgo@v1.11.9/vms/example/xsvm/api/client.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package api 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 "github.com/MetalBlockchain/metalgo/ids" 12 "github.com/MetalBlockchain/metalgo/utils/constants" 13 "github.com/MetalBlockchain/metalgo/utils/rpc" 14 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/block" 15 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/genesis" 16 "github.com/MetalBlockchain/metalgo/vms/example/xsvm/tx" 17 "github.com/MetalBlockchain/metalgo/vms/platformvm/warp" 18 ) 19 20 const DefaultPollingInterval = 50 * time.Millisecond 21 22 // Client defines the xsvm API client. 23 type Client interface { 24 Network( 25 ctx context.Context, 26 options ...rpc.Option, 27 ) (uint32, ids.ID, ids.ID, error) 28 Genesis( 29 ctx context.Context, 30 options ...rpc.Option, 31 ) (*genesis.Genesis, error) 32 Nonce( 33 ctx context.Context, 34 address ids.ShortID, 35 options ...rpc.Option, 36 ) (uint64, error) 37 Balance( 38 ctx context.Context, 39 address ids.ShortID, 40 assetID ids.ID, 41 options ...rpc.Option, 42 ) (uint64, error) 43 Loan( 44 ctx context.Context, 45 chainID ids.ID, 46 options ...rpc.Option, 47 ) (uint64, error) 48 IssueTx( 49 ctx context.Context, 50 tx *tx.Tx, 51 options ...rpc.Option, 52 ) (ids.ID, error) 53 LastAccepted( 54 ctx context.Context, 55 options ...rpc.Option, 56 ) (ids.ID, *block.Stateless, error) 57 Block( 58 ctx context.Context, 59 blkID ids.ID, 60 options ...rpc.Option, 61 ) (*block.Stateless, error) 62 Message( 63 ctx context.Context, 64 txID ids.ID, 65 options ...rpc.Option, 66 ) (*warp.UnsignedMessage, []byte, error) 67 } 68 69 func NewClient(uri, chain string) Client { 70 path := fmt.Sprintf( 71 "%s/ext/%s/%s", 72 uri, 73 constants.ChainAliasPrefix, 74 chain, 75 ) 76 return &client{ 77 req: rpc.NewEndpointRequester(path), 78 } 79 } 80 81 type client struct { 82 req rpc.EndpointRequester 83 } 84 85 func (c *client) Network( 86 ctx context.Context, 87 options ...rpc.Option, 88 ) (uint32, ids.ID, ids.ID, error) { 89 resp := new(NetworkReply) 90 err := c.req.SendRequest( 91 ctx, 92 "xsvm.network", 93 nil, 94 resp, 95 options..., 96 ) 97 return resp.NetworkID, resp.SubnetID, resp.ChainID, err 98 } 99 100 func (c *client) Genesis( 101 ctx context.Context, 102 options ...rpc.Option, 103 ) (*genesis.Genesis, error) { 104 resp := new(GenesisReply) 105 err := c.req.SendRequest( 106 ctx, 107 "xsvm.genesis", 108 nil, 109 resp, 110 options..., 111 ) 112 return resp.Genesis, err 113 } 114 115 func (c *client) Nonce( 116 ctx context.Context, 117 address ids.ShortID, 118 options ...rpc.Option, 119 ) (uint64, error) { 120 resp := new(NonceReply) 121 err := c.req.SendRequest( 122 ctx, 123 "xsvm.nonce", 124 &NonceArgs{ 125 Address: address, 126 }, 127 resp, 128 options..., 129 ) 130 return resp.Nonce, err 131 } 132 133 func (c *client) Balance( 134 ctx context.Context, 135 address ids.ShortID, 136 assetID ids.ID, 137 options ...rpc.Option, 138 ) (uint64, error) { 139 resp := new(BalanceReply) 140 err := c.req.SendRequest( 141 ctx, 142 "xsvm.balance", 143 &BalanceArgs{ 144 Address: address, 145 AssetID: assetID, 146 }, 147 resp, 148 options..., 149 ) 150 return resp.Balance, err 151 } 152 153 func (c *client) Loan( 154 ctx context.Context, 155 chainID ids.ID, 156 options ...rpc.Option, 157 ) (uint64, error) { 158 resp := new(LoanReply) 159 err := c.req.SendRequest( 160 ctx, 161 "xsvm.loan", 162 &LoanArgs{ 163 ChainID: chainID, 164 }, 165 resp, 166 options..., 167 ) 168 return resp.Amount, err 169 } 170 171 func (c *client) IssueTx( 172 ctx context.Context, 173 newTx *tx.Tx, 174 options ...rpc.Option, 175 ) (ids.ID, error) { 176 txBytes, err := tx.Codec.Marshal(tx.CodecVersion, newTx) 177 if err != nil { 178 return ids.Empty, err 179 } 180 181 resp := new(IssueTxReply) 182 err = c.req.SendRequest( 183 ctx, 184 "xsvm.issueTx", 185 &IssueTxArgs{ 186 Tx: txBytes, 187 }, 188 resp, 189 options..., 190 ) 191 return resp.TxID, err 192 } 193 194 func (c *client) LastAccepted( 195 ctx context.Context, 196 options ...rpc.Option, 197 ) (ids.ID, *block.Stateless, error) { 198 resp := new(LastAcceptedReply) 199 err := c.req.SendRequest( 200 ctx, 201 "xsvm.lastAccepted", 202 nil, 203 resp, 204 options..., 205 ) 206 return resp.BlockID, resp.Block, err 207 } 208 209 func (c *client) Block( 210 ctx context.Context, 211 blkID ids.ID, 212 options ...rpc.Option, 213 ) (*block.Stateless, error) { 214 resp := new(BlockReply) 215 err := c.req.SendRequest( 216 ctx, 217 "xsvm.lastAccepted", 218 &BlockArgs{ 219 BlockID: blkID, 220 }, 221 resp, 222 options..., 223 ) 224 return resp.Block, err 225 } 226 227 func (c *client) Message( 228 ctx context.Context, 229 txID ids.ID, 230 options ...rpc.Option, 231 ) (*warp.UnsignedMessage, []byte, error) { 232 resp := new(MessageReply) 233 err := c.req.SendRequest( 234 ctx, 235 "xsvm.message", 236 &MessageArgs{ 237 TxID: txID, 238 }, 239 resp, 240 options..., 241 ) 242 if err != nil { 243 return nil, nil, err 244 } 245 return resp.Message, resp.Signature, resp.Message.Initialize() 246 } 247 248 func AwaitTxAccepted( 249 ctx context.Context, 250 c Client, 251 address ids.ShortID, 252 nonce uint64, 253 freq time.Duration, 254 options ...rpc.Option, 255 ) error { 256 ticker := time.NewTicker(freq) 257 defer ticker.Stop() 258 259 for { 260 currentNonce, err := c.Nonce(ctx, address, options...) 261 if err != nil { 262 return err 263 } 264 265 if currentNonce > nonce { 266 // The nonce increasing indicates the acceptance of a transaction 267 // issued with the specified nonce. 268 return nil 269 } 270 271 select { 272 case <-ctx.Done(): 273 return ctx.Err() 274 case <-ticker.C: 275 } 276 } 277 }