package invoker_test import ( "context" "errors" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "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/invoker" "github.com/nspcc-dev/neo-go/pkg/rpcclient/neo" "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" ) func ExampleInvoker() { // No error checking done at all, intentionally. c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) // A simple invoker with no signers, perfectly fine for reads from safe methods. inv := invoker.New(c, nil) // Get the NEO token supply (notice that unwrap is used to get the result). supply, _ := unwrap.BigInt(inv.Call(neo.Hash, "totalSupply")) _ = supply acc, _ := address.StringToUint160("NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq") // Get the NEO balance for account NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq. balance, _ := unwrap.BigInt(inv.Call(neo.Hash, "balanceOf", acc)) _ = balance // Test-invoke transfer call. res, _ := inv.Call(neo.Hash, "transfer", acc, util.Uint160{1, 2, 3}, 1, nil) if res.State == vmstate.Halt.String() { panic("NEO is broken!") // inv has no signers and transfer requires a witness to be performed. } else { // nolint:revive // superfluous-else: if block ends with call to panic function, so drop this else and outdent its block (revive) println("ok") // this actually should fail } // A historic invoker with no signers at block 1000000. inv = invoker.NewHistoricAtHeight(1000000, c, nil) // It's the same call as above, but the data is for a state at block 1000000. balance, _ = unwrap.BigInt(inv.Call(neo.Hash, "balanceOf", acc)) _ = balance // This invoker has a signer for NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq account with // CalledByEntry scope, which is sufficient for most operation. It uses current // state which is exactly what you need if you want to then create a transaction // with the same action. inv = invoker.New(c, []transaction.Signer{{Account: acc, Scopes: transaction.CalledByEntry}}) // Now test invocation should be fine (if NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq has 1 NEO of course). res, _ = inv.Call(neo.Hash, "transfer", acc, util.Uint160{1, 2, 3}, 1, nil) if res.State == vmstate.Halt.String() { // transfer actually returns a value, so check it too. ok, _ := unwrap.Bool(res, nil) if ok { // OK, as expected. // res.Script contains the corresponding script. _ = res.Script // res.GasConsumed has an appropriate system fee required for a transaction. _ = res.GasConsumed } } // Now let's try working with iterators. nep11Contract := util.Uint160{1, 2, 3} var tokens [][]byte // Try doing it the right way, by traversing the iterator. sess, iter, err := unwrap.SessionIterator(inv.Call(nep11Contract, "tokensOf", acc)) // The server doesn't support sessions and doesn't perform iterator expansion, // iterators can't be used. if err != nil { if errors.Is(err, unwrap.ErrNoSessionID) { // But if we expect some low number of elements, CallAndExpandIterator // can help us in this case. If the account has more than 10 elements, // some of them will be missing from the response. tokens, _ = unwrap.ArrayOfBytes(inv.CallAndExpandIterator(nep11Contract, "tokensOf", 10, acc)) } else { panic("some error") } } else { items, err := inv.TraverseIterator(sess, &iter, 100) // Keep going until there are no more elements for err == nil && len(items) != 0 { for _, itm := range items { tokenID, _ := itm.TryBytes() tokens = append(tokens, tokenID) } items, err = inv.TraverseIterator(sess, &iter, 100) } // Let the server release the session. _ = inv.TerminateSession(sess) } _ = tokens }