How it works
The usual example: Elena buys from a shop she doesn't know. She pays 300 EURC, which stay locked until the courier marks the parcel delivered. If the parcel hasn't arrived after ten days, the money goes back to her.
The terms
A Scontract is made of six things:
| Field | What it is |
|---|---|
payer |
The party who pays in. In the example, Elena. |
payee |
The party who receives. In the example, the shop. |
money |
The sum, in EURC. Amounts are in the smallest units, with 6 decimals: 300 EURC is 300000000. |
condition |
The fact that executes the contract. |
deadline |
The deadline, in Unix seconds. |
onExpiry |
What happens at the deadline if the fact hasn't occurred: refund, the money goes back to the payer, or execute, the contract executes anyway. |
Each party has an address, a display name and the way it was identified. Today that is wallet; later it will be kyc or cie.
The condition
| Type | Who certifies it | In the pilot |
|---|---|---|
parcel-delivered |
the courier, through AfterShip | yes |
manual-acceptance |
the designated party accepts the delivery | in the core, without an oracle |
registry-fact |
a public registry: PRA, Registro Imprese, SDI | in the core, without an oracle |
A condition can be observed only if there is an oracle that knows how to read it. The pilot has the one for shipments. The other types exist in the model, and the first one coming is acceptance with tacit execution at the deadline: the freelancer case.
The condition enters the contract as a hash: the keccak256 of its canonical form, with the keys sorted. The oracle attests that specific condition, not just any parcel.
The mandate
The terms become an EIP-712 document, the mandate: payer, payee, token, amount, condition hash, deadline, what to do at the deadline, and a random nonce. Its hash is the Scontract's id, the same in the API and in the contract.
The two parties sign it at two different moments:
- The payee signs first, off-chain, from their wallet. They accept those terms and no others.
- The payer signs by paying in. They open the Scontract on-chain with the payee's signature, then fund it. The contract checks that the signature really is the payee's, and that it covers those terms.
The signing domain is { name: "Scontratto", version: "1", chainId, verifyingContract }: a signature is valid for one escrow on one chain only.
From signature to payment
- Preparation. The terms and the mandate to be signed are prepared.
- Payee signature. The Scontract is created in
draftstatus. - Opening and funding. The payer authorizes the escrow to withdraw the EURC, opens the Scontract and funds it. The status moves to
funded. - Observation. Every five minutes, and immediately when AfterShip sends an update, the oracle checks the tracking.
- Attestation. If the courier marked the parcel delivered before the deadline, the attestor signs what the oracle saw: the Scontract id, the condition hash and the timestamp.
- Execution. With that signature, the contract pays the payee. The status moves to
executed. - Or the deadline. If the fact hasn't occurred, after the deadline the money goes back to the payer and the status moves to
refunded. WithonExpiry: execute, the money goes to the payee instead.
Until they have paid in, the payer can back out: on-chain with cancel, and the status becomes cancelled. In the pilot, the app and the API don't expose this yet. In any case, a draft that is never funded moves nothing.
Oracle and attestor
The oracle watches the fact and reports it, with proof: for a shipment, the tracking status on AfterShip, the courier, the delivery date. It decides nothing.
The attestor signs what the oracle saw. The contract releases the money only with the signature of the attestor recorded in the Scontract when it was opened, and only for an observation made before the deadline.
Today the attestor is a key held by our backend. It is a trusted attestor, not a public oracle: whoever controls that key can release the funds. That is fine for the pilot, and it is why the oracle and the attestor are separate: the key can move to a third-party service, or to a network, without touching anything else.
Statuses
| Status | What it means |
|---|---|
draft |
The payee has signed. Waiting for funding. |
funded |
The money is in the contract. The condition is being observed. |
executed |
Paid to the payee. |
refunded |
Expired without the fact: the money went back to the payer. |
cancelled |
Cancelled by the payer, before funding. |
Each step is an event in the Scontract's history: created, funded, condition-met, executed, expired, refunded, cancelled. The attestation stays attached, with the proof of what the oracle saw.