From ee55e95c282f77d5d4bac0b05c7d406dd066a27b Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 7 Sep 2022 18:26:57 +0300 Subject: [PATCH] rpcclient: add examples for nep11/nep17/neo GAS doesn't need any, so just mention nep17 package there. --- pkg/rpcclient/gas/gas.go | 3 +- pkg/rpcclient/neo/doc_test.go | 91 +++++++++++++++++ pkg/rpcclient/nep11/doc_test.go | 167 ++++++++++++++++++++++++++++++++ pkg/rpcclient/nep17/doc_test.go | 67 +++++++++++++ 4 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 pkg/rpcclient/neo/doc_test.go create mode 100644 pkg/rpcclient/nep11/doc_test.go create mode 100644 pkg/rpcclient/nep17/doc_test.go diff --git a/pkg/rpcclient/gas/gas.go b/pkg/rpcclient/gas/gas.go index a19dde6bc..0ea2ca5d4 100644 --- a/pkg/rpcclient/gas/gas.go +++ b/pkg/rpcclient/gas/gas.go @@ -2,7 +2,8 @@ Package gas provides a convenience wrapper for GAS contract to use it via RPC. GAS itself only has standard NEP-17 methods, so this package only contains its -hash and allows to create NEP-17 structures in an easier way. +hash and allows to create NEP-17 structures in an easier way. Refer to [nep17] +package for more details on NEP-17 interface. */ package gas diff --git a/pkg/rpcclient/neo/doc_test.go b/pkg/rpcclient/neo/doc_test.go new file mode 100644 index 000000000..46fa77990 --- /dev/null +++ b/pkg/rpcclient/neo/doc_test.go @@ -0,0 +1,91 @@ +package neo_test + +import ( + "context" + "math/big" + "sort" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +func ExampleContractReader() { + // No error checking done at all, intentionally. + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Safe methods are reachable with just an invoker, no need for an account there. + inv := invoker.New(c, nil) + + // Create a reader interface. + neoToken := neo.NewReader(inv) + + // Account hash we're interested in. + accHash, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Get the account balance. + balance, _ := neoToken.BalanceOf(accHash) + _ = balance + + // Get the extended NEO-specific balance data. + bNeo, _ := neoToken.GetAccountState(accHash) + + // Account can have no associated vote. + if bNeo.VoteTo == nil { + return + } + // Committee keys. + comm, _ := neoToken.GetCommittee() + + // Check if the vote is made for a committee member. + var votedForCommitteeMember bool + for i := range comm { + if bNeo.VoteTo.Equal(comm[i]) { + votedForCommitteeMember = true + break + } + } + _ = votedForCommitteeMember +} + +func ExampleContract() { + // No error checking done at all, intentionally. + w, _ := wallet.NewWalletFromFile("somewhere") + defer w.Close() + + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Create a simple CalledByEntry-scoped actor (assuming there is an account + // inside the wallet). + a, _ := actor.NewSimple(c, w.Accounts[0]) + + // Create a complete contract representation. + neoToken := neo.New(a) + + tgtAcc, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Send a transaction that transfers one token to another account. + txid, vub, _ := neoToken.Transfer(a.Sender(), tgtAcc, big.NewInt(1), nil) + _ = txid + _ = vub + + // Get a list of candidates (it's limited, but should be sufficient in most cases). + cands, _ := neoToken.GetCandidates() + + // Sort by votes. + sort.Slice(cands, func(i, j int) bool { return cands[i].Votes < cands[j].Votes }) + + // Get the extended NEO-specific balance data. + bNeo, _ := neoToken.GetAccountState(a.Sender()) + + // If not yet voted, or voted for suboptimal candidate (we want the one with the least votes), + // send a new voting transaction + if bNeo.VoteTo == nil || !bNeo.VoteTo.Equal(&cands[0].PublicKey) { + txid, vub, _ = neoToken.Vote(a.Sender(), &cands[0].PublicKey) + _ = txid + _ = vub + } +} diff --git a/pkg/rpcclient/nep11/doc_test.go b/pkg/rpcclient/nep11/doc_test.go new file mode 100644 index 000000000..9c8581fde --- /dev/null +++ b/pkg/rpcclient/nep11/doc_test.go @@ -0,0 +1,167 @@ +package nep11_test + +import ( + "context" + "math/big" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +func ExampleNonDivisibleReader() { + // No error checking done at all, intentionally. + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Safe methods are reachable with just an invoker, no need for an account there. + inv := invoker.New(c, nil) + + // NEP-11 contract hash. + nep11Hash := util.Uint160{9, 8, 7} + + // Most of the time contracts are non-divisible, create a reader for nep11Hash. + n11 := nep11.NewNonDivisibleReader(inv, nep11Hash) + + // Get the metadata. Even though these methods are implemented in neptoken package, + // they're available for NEP-11 wrappers. + symbol, _ := n11.Symbol() + supply, _ := n11.TotalSupply() + _ = symbol + _ = supply + + // Account hash we're interested in. + accHash, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Get account balance. + balance, _ := n11.BalanceOf(accHash) + if balance.Sign() > 0 { + // There are some tokens there, let's look at them. + tokIter, _ := n11.TokensOf(accHash) + + for toks, err := tokIter.Next(10); err == nil && len(toks) > 0; toks, err = tokIter.Next(10) { + for i := range toks { + // We know the owner of the token, but let's check internal contract consistency. + owner, _ := n11.OwnerOf(toks[i]) + if !owner.Equals(accHash) { + panic("NEP-11 contract is broken!") + } + } + } + } +} + +func ExampleDivisibleReader() { + // No error checking done at all, intentionally. + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Safe methods are reachable with just an invoker, no need for an account there. + inv := invoker.New(c, nil) + + // NEP-11 contract hash. + nep11Hash := util.Uint160{9, 8, 7} + + // Divisible contract are more rare, but we can handle them too. + n11 := nep11.NewDivisibleReader(inv, nep11Hash) + + // Get the metadata. Even though these methods are implemented in neptoken package, + // they're available for NEP-11 wrappers. + symbol, _ := n11.Symbol() + supply, _ := n11.TotalSupply() + _ = symbol + _ = supply + + // Account hash we're interested in. + accHash, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Get account balance. + balance, _ := n11.BalanceOf(accHash) + if balance.Sign() > 0 && balance.Cmp(big.NewInt(10)) < 0 { + // We know we have a low number of tokens, so we can use a simple API to get them. + toks, _ := n11.TokensOfExpanded(accHash, 10) + + // We can build a list of all owners of account's tokens. + var owners = make([]util.Uint160, 0) + for i := range toks { + ownIter, _ := n11.OwnerOf(toks[i]) + for ows, err := ownIter.Next(10); err == nil && len(ows) > 0; ows, err = ownIter.Next(10) { + // Notice that it includes accHash too. + owners = append(owners, ows...) + } + } + // The list can be sorted/deduplicated if needed. + _ = owners + } +} + +func ExampleNonDivisible() { + // No error checking done at all, intentionally. + w, _ := wallet.NewWalletFromFile("somewhere") + defer w.Close() + + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Create a simple CalledByEntry-scoped actor (assuming there is an account + // inside the wallet). + a, _ := actor.NewSimple(c, w.Accounts[0]) + + // NEP-11 contract hash. + nep11Hash := util.Uint160{9, 8, 7} + + // Create a complete non-divisible contract representation. + n11 := nep11.NewNonDivisible(a, nep11Hash) + + tgtAcc, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Let's tranfer all of account's tokens to some other account. + tokIter, _ := n11.TokensOf(a.Sender()) + for toks, err := tokIter.Next(10); err == nil && len(toks) > 0; toks, err = tokIter.Next(10) { + for i := range toks { + // This creates a transaction for every token, but you can + // create a script that will move multiple tokens in one + // transaction with Builder from smartcontract package. + txid, vub, _ := n11.Transfer(tgtAcc, toks[i], nil) + _ = txid + _ = vub + } + } +} + +func ExampleDivisible() { + // No error checking done at all, intentionally. + w, _ := wallet.NewWalletFromFile("somewhere") + defer w.Close() + + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Create a simple CalledByEntry-scoped actor (assuming there is an account + // inside the wallet). + a, _ := actor.NewSimple(c, w.Accounts[0]) + + // NEP-11 contract hash. + nep11Hash := util.Uint160{9, 8, 7} + + // Create a complete divisible contract representation. + n11 := nep11.NewDivisible(a, nep11Hash) + + tgtAcc, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Let's tranfer all of account's tokens to some other account. + tokIter, _ := n11.TokensOf(a.Sender()) + for toks, err := tokIter.Next(10); err == nil && len(toks) > 0; toks, err = tokIter.Next(10) { + for i := range toks { + // It's a divisible token, so balance data is required in general case. + balance, _ := n11.BalanceOfD(a.Sender(), toks[i]) + + // This creates a transaction for every token, but you can + // create a script that will move multiple tokens in one + // transaction with Builder from smartcontract package. + txid, vub, _ := n11.TransferD(a.Sender(), tgtAcc, balance, toks[i], nil) + _ = txid + _ = vub + } + } +} diff --git a/pkg/rpcclient/nep17/doc_test.go b/pkg/rpcclient/nep17/doc_test.go new file mode 100644 index 000000000..4bbb2e836 --- /dev/null +++ b/pkg/rpcclient/nep17/doc_test.go @@ -0,0 +1,67 @@ +package nep17_test + +import ( + "context" + "math/big" + + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +func ExampleTokenReader() { + // No error checking done at all, intentionally. + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Safe methods are reachable with just an invoker, no need for an account there. + inv := invoker.New(c, nil) + + // NEP-17 contract hash. + nep17Hash := util.Uint160{9, 8, 7} + + // And a reader interface. + n17 := nep17.NewReader(inv, nep17Hash) + + // Get the metadata. Even though these methods are implemented in neptoken package, + // they're available for NEP-17 wrappers. + symbol, _ := n17.Symbol() + supply, _ := n17.TotalSupply() + _ = symbol + _ = supply + + // Account hash we're interested in. + accHash, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Get account balance. + balance, _ := n17.BalanceOf(accHash) + _ = balance +} + +func ExampleToken() { + // No error checking done at all, intentionally. + w, _ := wallet.NewWalletFromFile("somewhere") + defer w.Close() + + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // Create a simple CalledByEntry-scoped actor (assuming there is an account + // inside the wallet). + a, _ := actor.NewSimple(c, w.Accounts[0]) + + // NEP-17 contract hash. + nep17Hash := util.Uint160{9, 8, 7} + + // Create a complete NEP-17 contract representation. + n17 := nep17.New(a, nep17Hash) + + tgtAcc, _ := address.StringToUint160("NdypBhqkz2CMMnwxBgvoC9X2XjKF5axgKo") + + // Send a transaction that transfers one token to another account. + txid, vub, _ := n17.Transfer(a.Sender(), tgtAcc, big.NewInt(1), nil) + _ = txid + _ = vub +}