github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/actor/doc_test.go (about)

     1  package actor_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os"
     7  
     8  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
     9  	"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
    10  	"github.com/nspcc-dev/neo-go/pkg/rpcclient"
    11  	"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
    12  	"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
    13  	"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"
    14  	sccontext "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
    15  	"github.com/nspcc-dev/neo-go/pkg/util"
    16  	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
    17  	"github.com/nspcc-dev/neo-go/pkg/wallet"
    18  )
    19  
    20  func ExampleActor() {
    21  	// No error checking done at all, intentionally.
    22  	w, _ := wallet.NewWalletFromFile("somewhere")
    23  	defer w.Close()
    24  
    25  	c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{})
    26  
    27  	// Create a simple CalledByEntry-scoped actor (assuming there are accounts
    28  	// inside the wallet).
    29  	a, _ := actor.NewSimple(c, w.Accounts[0])
    30  
    31  	customContract := util.Uint160{9, 8, 7}
    32  	// Actor has an Invoker inside, so we can perform test invocations, it will
    33  	// have a signer with the first wallet account and CalledByEntry scope.
    34  	res, _ := a.Call(customContract, "method", 1, 2, 3)
    35  	if res.State != vmstate.Halt.String() {
    36  		panic("failed")
    37  	}
    38  	// All of the side-effects in res can be analyzed.
    39  
    40  	// Now we want to send the same invocation in a transaction, but we already
    41  	// have the script and a proper system fee for it, therefore SendUncheckedRun
    42  	// can be used.
    43  	txid, vub, _ := a.SendUncheckedRun(res.Script, res.GasConsumed, nil, nil)
    44  	_ = txid
    45  	_ = vub
    46  	// You need to wait for it to persist and then check the on-chain result of it.
    47  
    48  	// Now we want to send some transaction, but give it a priority by increasing
    49  	// its network fee, this can be done with Tuned APIs.
    50  	txid, vub, _ = a.SendTunedCall(customContract, "method", nil, func(r *result.Invoke, t *transaction.Transaction) error {
    51  		// This code is run after the test-invocation done by *Call methods.
    52  		// Reuse the default function to check for HALT execution state.
    53  		err := actor.DefaultCheckerModifier(r, t)
    54  		if err != nil {
    55  			return err
    56  		}
    57  		// Some additional checks can be performed right here, but we only
    58  		// want to raise the network fee by ~20%.
    59  		t.NetworkFee += (t.NetworkFee / 5)
    60  		return nil
    61  	}, 1, 2, 3)
    62  	_ = txid
    63  	_ = vub
    64  
    65  	// Actor can be used for higher-level wrappers as well, if we want to interact with
    66  	// NEO then [neo] package can accept our Actor and allow to easily use NEO methods.
    67  	neoContract := neo.New(a)
    68  	balance, _ := neoContract.BalanceOf(a.Sender())
    69  	_ = balance
    70  
    71  	// Now suppose the second wallet account is a committee account. We want to
    72  	// create and sign transactions for committee, but use the first account as
    73  	// a sender (because committee account has no GAS). We at the same time want
    74  	// to make all transactions using this actor high-priority ones, because
    75  	// committee can use this attribute.
    76  
    77  	// Get the default options to have CheckerModifier/Modifier set up correctly.
    78  	opts := actor.NewDefaultOptions()
    79  	// And override attributes.
    80  	opts.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}
    81  
    82  	// Create an Actor.
    83  	a, _ = actor.NewTuned(c, []actor.SignerAccount{{
    84  		// Sender, regular account with None scope.
    85  		Signer: transaction.Signer{
    86  			Account: w.Accounts[0].ScriptHash(),
    87  			Scopes:  transaction.None,
    88  		},
    89  		Account: w.Accounts[0],
    90  	}, {
    91  		// Commmitee.
    92  		Signer: transaction.Signer{
    93  			Account: w.Accounts[1].ScriptHash(),
    94  			Scopes:  transaction.CalledByEntry,
    95  		},
    96  		Account: w.Accounts[1],
    97  	}}, opts)
    98  
    99  	// Use policy contract wrapper to simplify things. All changes in the
   100  	// Policy contract are made by the committee.
   101  	policyContract := policy.New(a)
   102  
   103  	// Create a transaction to set storage price, it'll be high-priority and have two
   104  	// signers from above. Committee is a multisignature account, so we can't sign/send
   105  	// it right away, w.Accounts[1] has only one public key. Therefore, we need to
   106  	// create a partially signed transaction and save it, then collect other signatures
   107  	// and send.
   108  	tx, _ := policyContract.SetStoragePriceUnsigned(10)
   109  
   110  	net := a.GetNetwork()
   111  	scCtx := sccontext.NewParameterContext(sccontext.TransactionType, net, tx)
   112  	sign := w.Accounts[0].SignHashable(net, tx)
   113  	_ = scCtx.AddSignature(w.Accounts[0].ScriptHash(), w.Accounts[0].Contract, w.Accounts[0].PublicKey(), sign)
   114  
   115  	sign = w.Accounts[1].SignHashable(net, tx)
   116  	_ = scCtx.AddSignature(w.Accounts[1].ScriptHash(), w.Accounts[1].Contract, w.Accounts[1].PublicKey(), sign)
   117  
   118  	data, _ := json.Marshal(scCtx)
   119  	_ = os.WriteFile("tx.json", data, 0644)
   120  
   121  	// Signature collection is out of scope, usually it's manual for cases like this.
   122  }