How to implement a state machine with Move?

Hello there. I am new to Move and I am about to grasp the basics. Now I am wondering if it would be possible to enforce a state machine with it?

Something similar to this Ethereum Solidity contract:

contract Negotiation {
    enum State {IN_WORK, OFFERED, ACCEPTED};
    
    address creator;
    address counterparty;
    bytes32 record;
    State state = State.IN_WORK;

    constructor(addresss _counterparty) {
        creator = msg.sender;
        counterparty = _counterparty;
    }

    function modify(bytes32 newRecord) {
        require(state == State.IN_WORK);
        require(msg.sender == creator);
        record = newRecord;
    }

    function offer() {
        require(state == State.IN_WORK);
        require(msg.sender == creator);
        state = State.OFFERED;
    }

    function accept() {
        require(state == State.OFFERED);
        require(msg.sender == counterparty);
        state = State.ACCEPTED;
    }
}
3 Likes

Yes. Here is an example. Note that this won’t look particularly nice because Move IR does not have enums–we’ll use ints to achieve the same effect:

module Negotiation {

  resource T {
    counterparty: address,
    record: bytearray,
    // Move IR doesn't have enums, so we'll use IN_WORK = 0, OFFERED = 1, ACCEPTED = 2
    state: u64
  }

  // publishes a Negotiation.T resource under the sender's account
  public create(counterparty: address, record: bytearray) {
    let t: R#Self.T;

    // start in IN_WORK state
    t = T { counterparty: move(counterparty), record:move(record), state: 0 };
    move_to_sender<T>(move(t));

    return;
  }

   // modify the Negotiation.T published at addr
  public modify(new_record: bytearray, addr: address) {
    let t_ref: &mut R#Self.T;
    let sender: address;

    t_ref = borrow_global<T>(copy(addr));
    sender = get_txn_sender();

    // similar to require(state == State.IN_WORK)
    assert(*(&copy(t_ref).state) == 0, 77);
    // similar require(msg.sender == creator)
    assert(move(sender) == move(addr), 78);

    // similar to record = newRecord
    *(&mut move(t_ref).record) = move(new_record);

    return;
  }

  // ... offer and accept look similar

}
4 Likes

Thanks a lot for the example @sam. Just to complete the picture:

To which address is the module code deployed? How would the transaction script look like for deploying the module code?

How would transaction script look like for triggering the create function for instance?

2 Likes