Il contratto Escrow
Escrow.sol, in packages/contracts, è dove sta la somma di ogni scontratto. Un deal per mandato. Fa tre cose: accetta il versamento, paga chi riceve su attestazione firmata, restituisce a chi ha versato dopo la scadenza.
Non ha un owner, non si mette in pausa, non si aggiorna. Nessuno, nemmeno noi, può muovere i soldi fuori dalle sue regole.
L'indirizzo di questo ambiente è nella pagina Ambiente di test.
Funzioni
| Funzione | Chi | Cosa fa |
|---|---|---|
open(mandate, payeeSignature, attestor) |
chi versa | Apre il deal. L'id è l'hash EIP-712 del mandato. Verifica che la firma sia di chi riceve, su quei termini. |
fund(id) |
chi versa | Preleva la somma, che va autorizzata prima con approve. Solo prima della scadenza. |
release(id, observedAt, signature) |
chiunque | Paga chi riceve, con la firma dell'attestatore registrato nel deal. Vale solo per un'osservazione fatta entro la scadenza. |
settleExpired(id) |
chiunque, dopo la scadenza | Restituisce a chi ha versato, o paga chi riceve se il mandato prevede l'esecuzione tacita. |
cancel(id) |
chi versa | Annulla un deal aperto e non ancora versato. |
deals(id) |
vista | Le parti, il token, la somma, la condizione, la scadenza, l'attestatore, lo stato. |
mandateId(mandate) |
vista | L'id del deal, cioè l'hash EIP-712 del mandato. |
attestationDigest(id, conditionHash, observedAt) |
vista | Cosa firma l'attestatore. |
Lo stato di un deal è None, Open, Funded, Released, Refunded o Cancelled.
Le firme
Il dominio EIP-712 è { name: "Scontratto", version: "1", chainId, verifyingContract: <escrow> }. È calcolato ogni volta, così una copia del contratto a un altro indirizzo o su un'altra chain ha un dominio diverso, e le firme non si possono riusare.
Il mandato lo firma chi riceve:
Mandate(address payer, address payee, address token, uint256 amount,
bytes32 conditionHash, uint64 deadline, bool executeOnExpiry, bytes32 nonce)
L'attestazione la firma l'attestatore:
Attestation(bytes32 dealId, bytes32 conditionHash, bool met, uint64 observedAt)
Lo stesso hash lo calcola il core in TypeScript. Un test in Foundry confronta i due byte per byte: se divergessero, la firma data nell'app non aprirebbe il deal.
Eventi
Opened(id, payer, payee, amount, conditionHash, deadline), Funded(id), Released(id, conditionHash), Refunded(id), Cancelled(id).
Errori
| Errore | Quando |
|---|---|
NotPayer |
Apre, versa o annulla qualcuno che non è chi versa. |
BadPayeeSignature |
La firma non è di chi riceve, o non è su questi termini. |
Exists |
Un deal con lo stesso mandato esiste già. |
Unknown |
Il deal non esiste. |
WrongStatus |
Il deal non è nello stato giusto per questa azione. |
BadAttestor |
L'attestazione non è firmata dall'attestatore del deal. |
Expired |
Versamento o osservazione dopo la scadenza. |
NotExpired |
settleExpired prima della scadenza. |
TransferFailed |
Il token ha rifiutato il trasferimento. |
Prima di mainnet
Il contratto è uno scheletro senza dipendenze esterne. Prima di andare su mainnet, la firma passa alle librerie di OpenZeppelin, e il contratto passa da un audit. Il passo dopo sul versamento è EIP-3009: chi versa firma una volta sola, senza transazione di approve e senza gas.