Skip to main content

Pay Someone

An invoice is an asset you create, then send. The receiver takes it with a receive. Amounts are whole units. Decimals are only how a wallet displays them: with 8 decimals, a wallet shows 150000000 as 1.5. This invoice uses 0 decimals, so 100 is one hundred units.

Two directories. The issuer creates the asset and sends it. The payer is the contract that receives it. Alice may send. Bob may sign other commits on the issuer, and he may not send.

modal contract create --dir ./issuer
modal contract create --dir ./payer

In ./payer, post Bob and a rule that only he signs. The witness is the cookbook's Alice-must-sign shape, with Bob's path.

cd payer
modal checkout
modal set-named-id /parties/bob.id example/bob
modal add-rule --name authorized 'always([-signed_by(/parties/bob.id)] false)'
model Contract {
part flow {
q0 --> q1
q1 --> q1: +signed_by(/parties/bob.id)
}
}
modal commit --all -m "Payer"
modal status

Copy the payer's contract id. You will send to it.

In ./issuer:

cd ../issuer
modal checkout
modal set-named-id /parties/alice.id example/alice
modal set-named-id /parties/bob.id example/bob
modal add-rule --name authorized \
'always([-signed_by(/parties/alice.id) -signed_by(/parties/bob.id)] false)'
modal add-rule --name no_solo_send \
'always([+SEND -signed_by(/parties/alice.id)] false)'
model Contract {
part flow {
q0 --> q1
q1 --> q1: +signed_by(/parties/alice.id)
q1 --> q1: +signed_by(/parties/bob.id) -SEND
}
}
modal commit --all -m "Issuer"
modal commit \
--method create \
--asset-id invoice \
--quantity 1000 \
--divisibility 1 \
--decimals 0 \
--sign example/alice \
-m "Mint"

The issuer now holds 1000 of invoice. Bob tries to send 100 to the payer.

modal commit \
--method send \
--asset-id invoice \
--to-contract <payer contract id> \
--amount 100 \
--sign example/bob \
-m "Bob sends"
No valid transition for local commit from current states {"q1"}
Closest candidate transition: q1 -> q1 [+signed_by(/parties/bob.id) -SEND]; failed predicates: forbidden -SEND matched

Alice sends the same 100. modal log prints that commit's id. The payer receives it by stating that send.

modal commit \
--method send \
--asset-id invoice \
--to-contract <payer contract id> \
--amount 100 \
--sign example/alice \
-m "Alice sends"
cd ../payer
modal commit \
--method recv \
--send-commit-id <send commit id> \
--asset-contract <issuer contract id> \
--asset-id invoice \
--amount 100 \
--sign example/bob \
-m "Receive"

The receive lands locally when it is a step of the payer's witness. On a network, apply refuses a receive whose asset, amount, or source send differs from the send it names. See SEND and RECV.

The idea​

CREATE mints an asset into the contract that commits it. SEND moves whole units to another contract id. RECV is a later commit on the receiver, one per send, stating what it takes. The rule always([+SEND -signed_by(/parties/alice.id)] false) forbids a send Alice did not sign, and Bob's edge is marked -SEND so his signature cannot take one. Decimals never enter a rule.

Next: Too strong, then too weak.