Stateful Contract

Bitcoin/sCrypt contract uses Unspent Transaction Output (UTXO) model: a contract is inside a UTXO, dictating how the bitcoins in the UTXO can be spent. When a UTXO is spent (i.e., an sCrypt contract public function is called successfully), the contract is terminated. For a contract to keep state and able to be called multiple times while carrying the mutable state, these steps have to be followed.

State Decorator

Declare any property that is part of the state with a decorator @state. The state property can be used the same way as a regular property.

contract Counter {
    @state
    int counter;

    constructor(int counter) {
        this.counter = counter;
    }
}

Propagate the State

A contract can keep state across chained transactions by storing it in the locking script. In the following example, a contract goes from state0 to state1, and then to state2. Input in transaction 1 tx1 is spending UTXO in tx0, and tx2 spending tx1.

keep state

When you are ready to pass the new state into the output[s] in the current spending transaction, simply call a built-in function this.getStateScript() to get the locking script containing the latest stateful properties. It is automatically generated for every stateful contract, i.e., a contract that has at least one property decorated with @state.

Finally, use OP_PUSH_TX to ensure the output[s] containing the state go into the current spending transaction. The following is an example contract that records the number of times mutate() have been called.

contract Counter {
    @state
    int counter;

    constructor(int counter) {
        this.counter = counter;
    }

    public function increment(SigHashPreimage txPreimage, int amount) {
        require(Tx.checkPreimage(txPreimage));

        // mutate state
        this.counter++;

        // get the locking script containing the latest stateful properties
        bytes outputScript = this.getStateScript();

        // construct an output from its locking script and satoshi amount
        bytes output = Util.buildOutput(outputScript, amount);
        // only 1 input here
        require(hash256(output) == Util.hashOutputs(txPreimage));
    }
}