github.com/MetalBlockchain/metalgo@v1.11.9/vms/avm/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 avm 5 6 import ( 7 "context" 8 "errors" 9 "fmt" 10 "time" 11 12 "github.com/MetalBlockchain/metalgo/api" 13 "github.com/MetalBlockchain/metalgo/ids" 14 "github.com/MetalBlockchain/metalgo/snow/choices" 15 "github.com/MetalBlockchain/metalgo/utils/constants" 16 "github.com/MetalBlockchain/metalgo/utils/crypto/secp256k1" 17 "github.com/MetalBlockchain/metalgo/utils/formatting" 18 "github.com/MetalBlockchain/metalgo/utils/formatting/address" 19 "github.com/MetalBlockchain/metalgo/utils/json" 20 "github.com/MetalBlockchain/metalgo/utils/rpc" 21 ) 22 23 var ( 24 _ Client = (*client)(nil) 25 26 ErrRejected = errors.New("rejected") 27 ) 28 29 // Client for interacting with an AVM (X-Chain) instance 30 type Client interface { 31 WalletClient 32 // GetBlock returns the block with the given id. 33 GetBlock(ctx context.Context, blkID ids.ID, options ...rpc.Option) ([]byte, error) 34 // GetBlockByHeight returns the block at the given [height]. 35 GetBlockByHeight(ctx context.Context, height uint64, options ...rpc.Option) ([]byte, error) 36 // GetHeight returns the height of the last accepted block. 37 GetHeight(ctx context.Context, options ...rpc.Option) (uint64, error) 38 // GetTxStatus returns the status of [txID] 39 // 40 // Deprecated: GetTxStatus only returns Accepted or Unknown, GetTx should be 41 // used instead to determine if the tx was accepted. 42 GetTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (choices.Status, error) 43 // GetTx returns the byte representation of [txID] 44 GetTx(ctx context.Context, txID ids.ID, options ...rpc.Option) ([]byte, error) 45 // GetUTXOs returns the byte representation of the UTXOs controlled by [addrs] 46 GetUTXOs( 47 ctx context.Context, 48 addrs []ids.ShortID, 49 limit uint32, 50 startAddress ids.ShortID, 51 startUTXOID ids.ID, 52 options ...rpc.Option, 53 ) ([][]byte, ids.ShortID, ids.ID, error) 54 // GetAtomicUTXOs returns the byte representation of the atomic UTXOs controlled by [addrs] 55 // from [sourceChain] 56 GetAtomicUTXOs( 57 ctx context.Context, 58 addrs []ids.ShortID, 59 sourceChain string, 60 limit uint32, 61 startAddress ids.ShortID, 62 startUTXOID ids.ID, 63 options ...rpc.Option, 64 ) ([][]byte, ids.ShortID, ids.ID, error) 65 // GetAssetDescription returns a description of [assetID] 66 GetAssetDescription(ctx context.Context, assetID string, options ...rpc.Option) (*GetAssetDescriptionReply, error) 67 // GetBalance returns the balance of [assetID] held by [addr]. 68 // If [includePartial], balance includes partial owned (i.e. in a multisig) funds. 69 // 70 // Deprecated: GetUTXOs should be used instead. 71 GetBalance(ctx context.Context, addr ids.ShortID, assetID string, includePartial bool, options ...rpc.Option) (*GetBalanceReply, error) 72 // GetAllBalances returns all asset balances for [addr] 73 // 74 // Deprecated: GetUTXOs should be used instead. 75 GetAllBalances(ctx context.Context, addr ids.ShortID, includePartial bool, options ...rpc.Option) ([]Balance, error) 76 // CreateAsset creates a new asset and returns its assetID 77 // 78 // Deprecated: Transactions should be issued using the 79 // `avalanchego/wallet/chain/x.Wallet` utility. 80 CreateAsset( 81 ctx context.Context, 82 user api.UserPass, 83 from []ids.ShortID, 84 changeAddr ids.ShortID, 85 name string, 86 symbol string, 87 denomination byte, 88 holders []*ClientHolder, 89 minters []ClientOwners, 90 options ...rpc.Option, 91 ) (ids.ID, error) 92 // CreateFixedCapAsset creates a new fixed cap asset and returns its assetID 93 // 94 // Deprecated: Transactions should be issued using the 95 // `avalanchego/wallet/chain/x.Wallet` utility. 96 CreateFixedCapAsset( 97 ctx context.Context, 98 user api.UserPass, 99 from []ids.ShortID, 100 changeAddr ids.ShortID, 101 name string, 102 symbol string, 103 denomination byte, 104 holders []*ClientHolder, 105 options ...rpc.Option, 106 ) (ids.ID, error) 107 // CreateVariableCapAsset creates a new variable cap asset and returns its assetID 108 // 109 // Deprecated: Transactions should be issued using the 110 // `avalanchego/wallet/chain/x.Wallet` utility. 111 CreateVariableCapAsset( 112 ctx context.Context, 113 user api.UserPass, 114 from []ids.ShortID, 115 changeAddr ids.ShortID, 116 name string, 117 symbol string, 118 denomination byte, 119 minters []ClientOwners, 120 options ...rpc.Option, 121 ) (ids.ID, error) 122 // CreateNFTAsset creates a new NFT asset and returns its assetID 123 // 124 // Deprecated: Transactions should be issued using the 125 // `avalanchego/wallet/chain/x.Wallet` utility. 126 CreateNFTAsset( 127 ctx context.Context, 128 user api.UserPass, 129 from []ids.ShortID, 130 changeAddr ids.ShortID, 131 name string, 132 symbol string, 133 minters []ClientOwners, 134 options ...rpc.Option, 135 ) (ids.ID, error) 136 // CreateAddress creates a new address controlled by [user] 137 // 138 // Deprecated: Keys should no longer be stored on the node. 139 CreateAddress(ctx context.Context, user api.UserPass, options ...rpc.Option) (ids.ShortID, error) 140 // ListAddresses returns all addresses on this chain controlled by [user] 141 // 142 // Deprecated: Keys should no longer be stored on the node. 143 ListAddresses(ctx context.Context, user api.UserPass, options ...rpc.Option) ([]ids.ShortID, error) 144 // ExportKey returns the private key corresponding to [addr] controlled by [user] 145 // 146 // Deprecated: Keys should no longer be stored on the node. 147 ExportKey(ctx context.Context, user api.UserPass, addr ids.ShortID, options ...rpc.Option) (*secp256k1.PrivateKey, error) 148 // ImportKey imports [privateKey] to [user] 149 // 150 // Deprecated: Keys should no longer be stored on the node. 151 ImportKey(ctx context.Context, user api.UserPass, privateKey *secp256k1.PrivateKey, options ...rpc.Option) (ids.ShortID, error) 152 // Mint [amount] of [assetID] to be owned by [to] 153 // 154 // Deprecated: Transactions should be issued using the 155 // `avalanchego/wallet/chain/x.Wallet` utility. 156 Mint( 157 ctx context.Context, 158 user api.UserPass, 159 from []ids.ShortID, 160 changeAddr ids.ShortID, 161 amount uint64, 162 assetID string, 163 to ids.ShortID, 164 options ...rpc.Option, 165 ) (ids.ID, error) 166 // SendNFT sends an NFT and returns the ID of the newly created transaction 167 // 168 // Deprecated: Transactions should be issued using the 169 // `avalanchego/wallet/chain/x.Wallet` utility. 170 SendNFT( 171 ctx context.Context, 172 user api.UserPass, 173 from []ids.ShortID, 174 changeAddr ids.ShortID, 175 assetID string, 176 groupID uint32, 177 to ids.ShortID, 178 options ...rpc.Option, 179 ) (ids.ID, error) 180 // MintNFT issues a MintNFT transaction and returns the ID of the newly created transaction 181 // 182 // Deprecated: Transactions should be issued using the 183 // `avalanchego/wallet/chain/x.Wallet` utility. 184 MintNFT( 185 ctx context.Context, 186 user api.UserPass, 187 from []ids.ShortID, 188 changeAddr ids.ShortID, 189 assetID string, 190 payload []byte, 191 to ids.ShortID, 192 options ...rpc.Option, 193 ) (ids.ID, error) 194 // Import sends an import transaction to import funds from [sourceChain] and 195 // returns the ID of the newly created transaction 196 // 197 // Deprecated: Transactions should be issued using the 198 // `avalanchego/wallet/chain/x.Wallet` utility. 199 Import(ctx context.Context, user api.UserPass, to ids.ShortID, sourceChain string, options ...rpc.Option) (ids.ID, error) // Export sends an asset from this chain to the P/C-Chain. 200 // After this tx is accepted, the AVAX must be imported to the P/C-chain with an importTx. 201 // Returns the ID of the newly created atomic transaction 202 // 203 // Deprecated: Transactions should be issued using the 204 // `avalanchego/wallet/chain/x.Wallet` utility. 205 Export( 206 ctx context.Context, 207 user api.UserPass, 208 from []ids.ShortID, 209 changeAddr ids.ShortID, 210 amount uint64, 211 to ids.ShortID, 212 toChainIDAlias string, 213 assetID string, 214 options ...rpc.Option, 215 ) (ids.ID, error) 216 } 217 218 // implementation for an AVM client for interacting with avm [chain] 219 type client struct { 220 requester rpc.EndpointRequester 221 } 222 223 // NewClient returns an AVM client for interacting with avm [chain] 224 func NewClient(uri, chain string) Client { 225 path := fmt.Sprintf( 226 "%s/ext/%s/%s", 227 uri, 228 constants.ChainAliasPrefix, 229 chain, 230 ) 231 return &client{ 232 requester: rpc.NewEndpointRequester(path), 233 } 234 } 235 236 func (c *client) GetBlock(ctx context.Context, blkID ids.ID, options ...rpc.Option) ([]byte, error) { 237 res := &api.FormattedBlock{} 238 err := c.requester.SendRequest(ctx, "avm.getBlock", &api.GetBlockArgs{ 239 BlockID: blkID, 240 Encoding: formatting.HexNC, 241 }, res, options...) 242 if err != nil { 243 return nil, err 244 } 245 return formatting.Decode(res.Encoding, res.Block) 246 } 247 248 func (c *client) GetBlockByHeight(ctx context.Context, height uint64, options ...rpc.Option) ([]byte, error) { 249 res := &api.FormattedBlock{} 250 err := c.requester.SendRequest(ctx, "avm.getBlockByHeight", &api.GetBlockByHeightArgs{ 251 Height: json.Uint64(height), 252 Encoding: formatting.HexNC, 253 }, res, options...) 254 if err != nil { 255 return nil, err 256 } 257 return formatting.Decode(res.Encoding, res.Block) 258 } 259 260 func (c *client) GetHeight(ctx context.Context, options ...rpc.Option) (uint64, error) { 261 res := &api.GetHeightResponse{} 262 err := c.requester.SendRequest(ctx, "avm.getHeight", struct{}{}, res, options...) 263 return uint64(res.Height), err 264 } 265 266 func (c *client) IssueTx(ctx context.Context, txBytes []byte, options ...rpc.Option) (ids.ID, error) { 267 txStr, err := formatting.Encode(formatting.Hex, txBytes) 268 if err != nil { 269 return ids.Empty, err 270 } 271 res := &api.JSONTxID{} 272 err = c.requester.SendRequest(ctx, "avm.issueTx", &api.FormattedTx{ 273 Tx: txStr, 274 Encoding: formatting.Hex, 275 }, res, options...) 276 return res.TxID, err 277 } 278 279 func (c *client) GetTxStatus(ctx context.Context, txID ids.ID, options ...rpc.Option) (choices.Status, error) { 280 res := &GetTxStatusReply{} 281 err := c.requester.SendRequest(ctx, "avm.getTxStatus", &api.JSONTxID{ 282 TxID: txID, 283 }, res, options...) 284 return res.Status, err 285 } 286 287 func (c *client) GetTx(ctx context.Context, txID ids.ID, options ...rpc.Option) ([]byte, error) { 288 res := &api.FormattedTx{} 289 err := c.requester.SendRequest(ctx, "avm.getTx", &api.GetTxArgs{ 290 TxID: txID, 291 Encoding: formatting.Hex, 292 }, res, options...) 293 if err != nil { 294 return nil, err 295 } 296 return formatting.Decode(res.Encoding, res.Tx) 297 } 298 299 func (c *client) GetUTXOs( 300 ctx context.Context, 301 addrs []ids.ShortID, 302 limit uint32, 303 startAddress ids.ShortID, 304 startUTXOID ids.ID, 305 options ...rpc.Option, 306 ) ([][]byte, ids.ShortID, ids.ID, error) { 307 return c.GetAtomicUTXOs(ctx, addrs, "", limit, startAddress, startUTXOID, options...) 308 } 309 310 func (c *client) GetAtomicUTXOs( 311 ctx context.Context, 312 addrs []ids.ShortID, 313 sourceChain string, 314 limit uint32, 315 startAddress ids.ShortID, 316 startUTXOID ids.ID, 317 options ...rpc.Option, 318 ) ([][]byte, ids.ShortID, ids.ID, error) { 319 res := &api.GetUTXOsReply{} 320 err := c.requester.SendRequest(ctx, "avm.getUTXOs", &api.GetUTXOsArgs{ 321 Addresses: ids.ShortIDsToStrings(addrs), 322 SourceChain: sourceChain, 323 Limit: json.Uint32(limit), 324 StartIndex: api.Index{ 325 Address: startAddress.String(), 326 UTXO: startUTXOID.String(), 327 }, 328 Encoding: formatting.Hex, 329 }, res, options...) 330 if err != nil { 331 return nil, ids.ShortID{}, ids.Empty, err 332 } 333 334 utxos := make([][]byte, len(res.UTXOs)) 335 for i, utxo := range res.UTXOs { 336 utxoBytes, err := formatting.Decode(res.Encoding, utxo) 337 if err != nil { 338 return nil, ids.ShortID{}, ids.Empty, err 339 } 340 utxos[i] = utxoBytes 341 } 342 endAddr, err := address.ParseToID(res.EndIndex.Address) 343 if err != nil { 344 return nil, ids.ShortID{}, ids.Empty, err 345 } 346 endUTXOID, err := ids.FromString(res.EndIndex.UTXO) 347 return utxos, endAddr, endUTXOID, err 348 } 349 350 func (c *client) GetAssetDescription(ctx context.Context, assetID string, options ...rpc.Option) (*GetAssetDescriptionReply, error) { 351 res := &GetAssetDescriptionReply{} 352 err := c.requester.SendRequest(ctx, "avm.getAssetDescription", &GetAssetDescriptionArgs{ 353 AssetID: assetID, 354 }, res, options...) 355 return res, err 356 } 357 358 func (c *client) GetBalance( 359 ctx context.Context, 360 addr ids.ShortID, 361 assetID string, 362 includePartial bool, 363 options ...rpc.Option, 364 ) (*GetBalanceReply, error) { 365 res := &GetBalanceReply{} 366 err := c.requester.SendRequest(ctx, "avm.getBalance", &GetBalanceArgs{ 367 Address: addr.String(), 368 AssetID: assetID, 369 IncludePartial: includePartial, 370 }, res, options...) 371 return res, err 372 } 373 374 func (c *client) GetAllBalances( 375 ctx context.Context, 376 addr ids.ShortID, 377 includePartial bool, 378 options ...rpc.Option, 379 ) ([]Balance, error) { 380 res := &GetAllBalancesReply{} 381 err := c.requester.SendRequest(ctx, "avm.getAllBalances", &GetAllBalancesArgs{ 382 JSONAddress: api.JSONAddress{Address: addr.String()}, 383 IncludePartial: includePartial, 384 }, res, options...) 385 return res.Balances, err 386 } 387 388 // ClientHolder describes how much an address owns of an asset 389 type ClientHolder struct { 390 Amount uint64 391 Address ids.ShortID 392 } 393 394 // ClientOwners describes who can perform an action 395 type ClientOwners struct { 396 Threshold uint32 397 Minters []ids.ShortID 398 } 399 400 func (c *client) CreateAsset( 401 ctx context.Context, 402 user api.UserPass, 403 from []ids.ShortID, 404 changeAddr ids.ShortID, 405 name string, 406 symbol string, 407 denomination byte, 408 clientHolders []*ClientHolder, 409 clientMinters []ClientOwners, 410 options ...rpc.Option, 411 ) (ids.ID, error) { 412 res := &FormattedAssetID{} 413 holders := make([]*Holder, len(clientHolders)) 414 for i, clientHolder := range clientHolders { 415 holders[i] = &Holder{ 416 Amount: json.Uint64(clientHolder.Amount), 417 Address: clientHolder.Address.String(), 418 } 419 } 420 minters := make([]Owners, len(clientMinters)) 421 for i, clientMinter := range clientMinters { 422 minters[i] = Owners{ 423 Threshold: json.Uint32(clientMinter.Threshold), 424 Minters: ids.ShortIDsToStrings(clientMinter.Minters), 425 } 426 } 427 err := c.requester.SendRequest(ctx, "avm.createAsset", &CreateAssetArgs{ 428 JSONSpendHeader: api.JSONSpendHeader{ 429 UserPass: user, 430 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 431 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 432 }, 433 Name: name, 434 Symbol: symbol, 435 Denomination: denomination, 436 InitialHolders: holders, 437 MinterSets: minters, 438 }, res, options...) 439 return res.AssetID, err 440 } 441 442 func (c *client) CreateFixedCapAsset( 443 ctx context.Context, 444 user api.UserPass, 445 from []ids.ShortID, 446 changeAddr ids.ShortID, 447 name string, 448 symbol string, 449 denomination byte, 450 clientHolders []*ClientHolder, 451 options ...rpc.Option, 452 ) (ids.ID, error) { 453 res := &FormattedAssetID{} 454 holders := make([]*Holder, len(clientHolders)) 455 for i, clientHolder := range clientHolders { 456 holders[i] = &Holder{ 457 Amount: json.Uint64(clientHolder.Amount), 458 Address: clientHolder.Address.String(), 459 } 460 } 461 err := c.requester.SendRequest(ctx, "avm.createAsset", &CreateAssetArgs{ 462 JSONSpendHeader: api.JSONSpendHeader{ 463 UserPass: user, 464 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 465 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 466 }, 467 Name: name, 468 Symbol: symbol, 469 Denomination: denomination, 470 InitialHolders: holders, 471 }, res, options...) 472 return res.AssetID, err 473 } 474 475 func (c *client) CreateVariableCapAsset( 476 ctx context.Context, 477 user api.UserPass, 478 from []ids.ShortID, 479 changeAddr ids.ShortID, 480 name string, 481 symbol string, 482 denomination byte, 483 clientMinters []ClientOwners, 484 options ...rpc.Option, 485 ) (ids.ID, error) { 486 res := &FormattedAssetID{} 487 minters := make([]Owners, len(clientMinters)) 488 for i, clientMinter := range clientMinters { 489 minters[i] = Owners{ 490 Threshold: json.Uint32(clientMinter.Threshold), 491 Minters: ids.ShortIDsToStrings(clientMinter.Minters), 492 } 493 } 494 err := c.requester.SendRequest(ctx, "avm.createAsset", &CreateAssetArgs{ 495 JSONSpendHeader: api.JSONSpendHeader{ 496 UserPass: user, 497 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 498 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 499 }, 500 Name: name, 501 Symbol: symbol, 502 Denomination: denomination, 503 MinterSets: minters, 504 }, res, options...) 505 return res.AssetID, err 506 } 507 508 func (c *client) CreateNFTAsset( 509 ctx context.Context, 510 user api.UserPass, 511 from []ids.ShortID, 512 changeAddr ids.ShortID, 513 name string, 514 symbol string, 515 clientMinters []ClientOwners, 516 options ...rpc.Option, 517 ) (ids.ID, error) { 518 res := &FormattedAssetID{} 519 minters := make([]Owners, len(clientMinters)) 520 for i, clientMinter := range clientMinters { 521 minters[i] = Owners{ 522 Threshold: json.Uint32(clientMinter.Threshold), 523 Minters: ids.ShortIDsToStrings(clientMinter.Minters), 524 } 525 } 526 err := c.requester.SendRequest(ctx, "avm.createNFTAsset", &CreateNFTAssetArgs{ 527 JSONSpendHeader: api.JSONSpendHeader{ 528 UserPass: user, 529 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 530 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 531 }, 532 Name: name, 533 Symbol: symbol, 534 MinterSets: minters, 535 }, res, options...) 536 return res.AssetID, err 537 } 538 539 func (c *client) CreateAddress(ctx context.Context, user api.UserPass, options ...rpc.Option) (ids.ShortID, error) { 540 res := &api.JSONAddress{} 541 err := c.requester.SendRequest(ctx, "avm.createAddress", &user, res, options...) 542 if err != nil { 543 return ids.ShortID{}, err 544 } 545 return address.ParseToID(res.Address) 546 } 547 548 func (c *client) ListAddresses(ctx context.Context, user api.UserPass, options ...rpc.Option) ([]ids.ShortID, error) { 549 res := &api.JSONAddresses{} 550 err := c.requester.SendRequest(ctx, "avm.listAddresses", &user, res, options...) 551 if err != nil { 552 return nil, err 553 } 554 return address.ParseToIDs(res.Addresses) 555 } 556 557 func (c *client) ExportKey(ctx context.Context, user api.UserPass, addr ids.ShortID, options ...rpc.Option) (*secp256k1.PrivateKey, error) { 558 res := &ExportKeyReply{} 559 err := c.requester.SendRequest(ctx, "avm.exportKey", &ExportKeyArgs{ 560 UserPass: user, 561 Address: addr.String(), 562 }, res, options...) 563 return res.PrivateKey, err 564 } 565 566 func (c *client) ImportKey(ctx context.Context, user api.UserPass, privateKey *secp256k1.PrivateKey, options ...rpc.Option) (ids.ShortID, error) { 567 res := &api.JSONAddress{} 568 err := c.requester.SendRequest(ctx, "avm.importKey", &ImportKeyArgs{ 569 UserPass: user, 570 PrivateKey: privateKey, 571 }, res, options...) 572 if err != nil { 573 return ids.ShortID{}, err 574 } 575 return address.ParseToID(res.Address) 576 } 577 578 func (c *client) Send( 579 ctx context.Context, 580 user api.UserPass, 581 from []ids.ShortID, 582 changeAddr ids.ShortID, 583 amount uint64, 584 assetID string, 585 to ids.ShortID, 586 memo string, 587 options ...rpc.Option, 588 ) (ids.ID, error) { 589 res := &api.JSONTxID{} 590 err := c.requester.SendRequest(ctx, "avm.send", &SendArgs{ 591 JSONSpendHeader: api.JSONSpendHeader{ 592 UserPass: user, 593 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 594 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 595 }, 596 SendOutput: SendOutput{ 597 Amount: json.Uint64(amount), 598 AssetID: assetID, 599 To: to.String(), 600 }, 601 Memo: memo, 602 }, res, options...) 603 return res.TxID, err 604 } 605 606 func (c *client) SendMultiple( 607 ctx context.Context, 608 user api.UserPass, 609 from []ids.ShortID, 610 changeAddr ids.ShortID, 611 clientOutputs []ClientSendOutput, 612 memo string, 613 options ...rpc.Option, 614 ) (ids.ID, error) { 615 res := &api.JSONTxID{} 616 outputs := make([]SendOutput, len(clientOutputs)) 617 for i, clientOutput := range clientOutputs { 618 outputs[i] = SendOutput{ 619 Amount: json.Uint64(clientOutput.Amount), 620 AssetID: clientOutput.AssetID, 621 To: clientOutput.To.String(), 622 } 623 } 624 err := c.requester.SendRequest(ctx, "avm.sendMultiple", &SendMultipleArgs{ 625 JSONSpendHeader: api.JSONSpendHeader{ 626 UserPass: user, 627 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 628 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 629 }, 630 Outputs: outputs, 631 Memo: memo, 632 }, res, options...) 633 return res.TxID, err 634 } 635 636 func (c *client) Mint( 637 ctx context.Context, 638 user api.UserPass, 639 from []ids.ShortID, 640 changeAddr ids.ShortID, 641 amount uint64, 642 assetID string, 643 to ids.ShortID, 644 options ...rpc.Option, 645 ) (ids.ID, error) { 646 res := &api.JSONTxID{} 647 err := c.requester.SendRequest(ctx, "avm.mint", &MintArgs{ 648 JSONSpendHeader: api.JSONSpendHeader{ 649 UserPass: user, 650 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 651 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 652 }, 653 Amount: json.Uint64(amount), 654 AssetID: assetID, 655 To: to.String(), 656 }, res, options...) 657 return res.TxID, err 658 } 659 660 func (c *client) SendNFT( 661 ctx context.Context, 662 user api.UserPass, 663 from []ids.ShortID, 664 changeAddr ids.ShortID, 665 assetID string, 666 groupID uint32, 667 to ids.ShortID, 668 options ...rpc.Option, 669 ) (ids.ID, error) { 670 res := &api.JSONTxID{} 671 err := c.requester.SendRequest(ctx, "avm.sendNFT", &SendNFTArgs{ 672 JSONSpendHeader: api.JSONSpendHeader{ 673 UserPass: user, 674 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 675 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 676 }, 677 AssetID: assetID, 678 GroupID: json.Uint32(groupID), 679 To: to.String(), 680 }, res, options...) 681 return res.TxID, err 682 } 683 684 func (c *client) MintNFT( 685 ctx context.Context, 686 user api.UserPass, 687 from []ids.ShortID, 688 changeAddr ids.ShortID, 689 assetID string, 690 payload []byte, 691 to ids.ShortID, 692 options ...rpc.Option, 693 ) (ids.ID, error) { 694 payloadStr, err := formatting.Encode(formatting.Hex, payload) 695 if err != nil { 696 return ids.Empty, err 697 } 698 res := &api.JSONTxID{} 699 err = c.requester.SendRequest(ctx, "avm.mintNFT", &MintNFTArgs{ 700 JSONSpendHeader: api.JSONSpendHeader{ 701 UserPass: user, 702 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 703 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 704 }, 705 AssetID: assetID, 706 Payload: payloadStr, 707 To: to.String(), 708 Encoding: formatting.Hex, 709 }, res, options...) 710 return res.TxID, err 711 } 712 713 func (c *client) Import(ctx context.Context, user api.UserPass, to ids.ShortID, sourceChain string, options ...rpc.Option) (ids.ID, error) { 714 res := &api.JSONTxID{} 715 err := c.requester.SendRequest(ctx, "avm.import", &ImportArgs{ 716 UserPass: user, 717 To: to.String(), 718 SourceChain: sourceChain, 719 }, res, options...) 720 return res.TxID, err 721 } 722 723 func (c *client) Export( 724 ctx context.Context, 725 user api.UserPass, 726 from []ids.ShortID, 727 changeAddr ids.ShortID, 728 amount uint64, 729 to ids.ShortID, 730 targetChain string, 731 assetID string, 732 options ...rpc.Option, 733 ) (ids.ID, error) { 734 res := &api.JSONTxID{} 735 err := c.requester.SendRequest(ctx, "avm.export", &ExportArgs{ 736 JSONSpendHeader: api.JSONSpendHeader{ 737 UserPass: user, 738 JSONFromAddrs: api.JSONFromAddrs{From: ids.ShortIDsToStrings(from)}, 739 JSONChangeAddr: api.JSONChangeAddr{ChangeAddr: changeAddr.String()}, 740 }, 741 Amount: json.Uint64(amount), 742 TargetChain: targetChain, 743 To: to.String(), 744 AssetID: assetID, 745 }, res, options...) 746 return res.TxID, err 747 } 748 749 func AwaitTxAccepted( 750 c Client, 751 ctx context.Context, 752 txID ids.ID, 753 freq time.Duration, 754 options ...rpc.Option, 755 ) error { 756 ticker := time.NewTicker(freq) 757 defer ticker.Stop() 758 759 for { 760 status, err := c.GetTxStatus(ctx, txID, options...) 761 if err != nil { 762 return err 763 } 764 765 switch status { 766 case choices.Accepted: 767 return nil 768 case choices.Rejected: 769 return ErrRejected 770 } 771 772 select { 773 case <-ticker.C: 774 case <-ctx.Done(): 775 return ctx.Err() 776 } 777 } 778 }