Invoking Smart Contracts
Invoking a Smart Contract's entry point in Solo looks like the following:
req := solo.NewCallParams("example1", "storeString", "paramString", "Hello, world!").WithIotas(1)
_, err = chain.PostRequestSync(req, nil)
In the example TestTutorial3
we invoke the storeString
entry point of the
example1
smart contract by posting it as a request. The parameter
named paramString
is passed with the string value "Hello, world!". The Solo
test code itself is separate from the chain where the smart contract is invoked and is not executed "on-chain".
NewCallParams
creates a call object named req
which wraps all call
parameters into a single object. This is syntactic sugar just for convenience.
In this case, the call object only wraps the target smart contract name, target
entry point name, and one named parameter paramString
. In other cases it could
contain many parameters.
WithIotas
attaches a single iota with the request. All on-ledger
requests
are implemented as value transactions with additional data, and therefore we
need to transfer at least a single token for the request to be valid.
PostRequestSync
sends the request to the chain. Let’s describe in detail what
is going on here.
The diagram above depicts the generic process of posting an on-ledger
request to the smart
contract. The same picture is valid for the Solo environment and for any other
requester which sends an on-ledger
request to the smart contract, for example, the IOTA Smart Contracts
wallet or another chain.
Posting the request always consists of the steps below. Note that in Solo all 7
steps are carried out by the single call to PostRequestSync
.
- Creating the smart contract transaction which wraps the request with metadata
and moves tokens. Each request transaction is a value transaction, it always
moves at least one token. Therefore, each request transaction must be signed
by the private key of the owner of the tokens: the requester. That securely
identifies each requester in IOTA Smart Contracts. In Solo, the transaction is signed by the
private key provided in the second parameter of the
PostRequestSync
call (see below). - Posting the request transaction to the Tangle and confirming it. In Solo it
is just adding the transaction to the
UTXODB ledger
, the emulated UTXO Ledger, so it is confirmed immediately and synchronously. The confirmed transaction on the ledger becomes part of the backlog of requests to the chain. In the real UTXO Ledger the sender would have to wait until the ledger confirms the transaction. - The chain picks the request from the backlog and runs the request on the VM.
- The VM calls the target entry point of the smart contract program. The program updates the state.
- The VM produces a state update transaction (the
anchor
). - The chain signs the transaction with its private key. In the Solo
environment is the
ChainSigScheme
property of the chain. In the real Wasp environment it is the threshold signature of the committee of validator nodes. - The chain posts the resulting transaction to the Tangle and, after confirmation, solidifies the corresponding state. In the Solo environment it adds the transaction to the UTXODB ledger.
The following lines in the log correspond to step 7:
54:43.809 INFO TestTutorial3.ex3 vmcontext/runreq.go:311 eventlog -> '[req] [0]CHvU6BUDgt9MZJTxsYMZ1p1veg591mvwKGQBJd2KYdaB: Ok'
54:43.809 INFO TestTutorial3 solo/clock.go:35 AdvanceClockBy: logical clock advanced by 2ns
54:43.809 INFO TestTutorial3.ex3.m mempool/mempool.go:119 OUT MEMPOOL [0]CHvU6BUDgt9MZJTxsYMZ1p1veg591mvwKGQBJd2KYdaB
54:43.809 INFO TestTutorial3.ex3 solo/run.go:86 state transition #2 --> #3. Requests in the block: 1. Outputs: 1
The chain adds a record about any successfully processed request
[0]CHvU6BUDgt9MZJTxsYMZ1p1veg591mvwKGQBJd2KYdaB
to the immutable on-chain log.
The statement _, err = chain.PostRequestSync(req, nil)
in the Solo test uses nil
for the default signature scheme of the requester. The OriginatorSigScheme
,
the one which deployed the chain, is used as the default requester. In the
Solo environment you can create other identities for requesters (“wallets”)
with NewKeyPairWithFunds
.
Off-ledger Requests
Alternatively, in the example above, we could send an off-ledger request by using chain.PostRequestOffLedger
instead of PostRequestSync
.
However, to be able to submit off-ledger request, the account sending the request must deposit funds to the chain beforehand.
wallet, address := env.NewKeyPairWithFunds()
AgentID := iscp.NewAgentID(ownerAddr, 0)
// deposit into the account
req := solo.NewCallParams(accounts.Contract.Name, accounts.FuncDeposit.Name).WithIotas(100)
_, err := chain.PostRequestSync(req, owner)
require.NoError(t, err)
//no .WithIotas() needed, fees will be deducted from the sender on-chain account
req := solo.NewCallParams("example1", "storeString", "paramString", "Hello, world!")
_, err = chain.PostRequestOffLedger(req, wallet)