Stateful Contracts
sCrypt contracts use the Unspent Transaction Output (UTXO) model: contracts reside within a UTXO and define how the Bitcoin within the UTXO can be spent. When a UTXO is spent (i.e., a public function of the sCrypt contract is successfully called), the contract is terminated. To maintain the contract's state and allow it to be called multiple times with variable states, follow these steps.
State Modifiers
Use the modifier @state
to declare any attribute as part of the state. State attributes can be used like regular attributes.
Updating State
By storing the state in the locking script, the contract can maintain state across chained transactions. In the following example, the contract transitions from state0 to state1, and then to state2. Transaction tx1's input spends the UTXO from tx0, and tx2 spends tx1.
Maintaining State
When you are ready to pass the new state to the next UTXO, simply call the built-in function this.updateState()
with two parameters:
txPreimage
: The preimage of the current spending transaction. It must have a single output that contains the new state.amount
: The number of satoshis in that single output.
This function is automatically generated for every stateful contract (i.e., a contract with at least one property decorated with @state
). If you need to customize the sighash type (different from the default SigHash.ALL | SigHash.FORKID
), you can use updateStateSigHashType
.
Here is an example contract that records the number of increment()
calls:
Advanced
If you need more granular control over the state, such as having multiple outputs in the spending transaction, you can call another built-in function this.getStateScript()
to get the locking script containing the latest state properties.
Next, use OP_PUSH_TX
to ensure the output containing the state is included in the current spending transaction. This is equivalent to using this.updateState()
in the contract above.
Limitations
For any public function accessing stateful properties, a SigHashPreimage parameter verified by Tx.checkPreimage()
must be included, using OP_PUSH_TX
. This does not apply to any non-public functions, including constructors.