Contract inteligent pentru acorduri juridice: Solidity și Vyper
În 2025, Arizona și Wyoming au recunoscut în mod explicit că contractele codificate în contractele inteligente, acestea pot avea valoare juridică obligatorie dacă îndeplinesc cerințele aspecte tradiționale ale dreptului contractelor: oferta, acceptarea, cauza și capacitatea părților. Cu toate acestea, distincția critică rămâne: unu contracte inteligente şi un program care execută automat condiții predefinite; o contract legal si un acord obligatorie între părți. Nu orice contract inteligent este un contract legal și nu orice acord legal și automatizat cu un contract inteligent.
În acest articol, explorăm implementarea practică a contractelor inteligente pentru acorduri legal real: escrow automat, plată condiționată, NDA-uri cu aplicare în lanț, și implicațiile juridice în fiecare caz. Codul este în Solidity (Ethereum) cu comparație cu Vyper pentru cazuri de utilizare de înaltă securitate.
Ce vei învăța
- Diferența dintre contract inteligent și contract legal: când funcționează și când nu
- Model automat de escrow în Solidity
- Contract de plata conditionata (plata la livrare)
- Vyper pentru contracte inteligente de înaltă securitate: avantaje vs soliditate
- Testare cu cască și turnătorie
- Audituri de securitate: vulnerabilități comune și cum să le evitați
- Wrapper legal: conectarea contractelor inteligente la contractele tradiționale
Când un contract inteligent are sens legal
Contractele inteligente excelează în scenariile în care:
- Condițiile sunt verificabile în mod obiectiv în lanț: plata sosește (eveniment blockchain), data expirării trece (marca temporală a blocului), este transferat un NFT.
- Părțile nu au încredere una în alta și vor să elimine intermediarul (bancă, notar, agent de încredere).
- Execuție automată și dorită: nicio parte nu trebuie „activați” manual pentru a vă îndeplini obligația.
Contractele inteligente nu sunt potrivite pentru:
- Acorduri care depind de evenimente din lumea reală care nu pot fi verificate în lanț (de exemplu, „mărfurile au ajuns în stare bună”) — necesită oracole de încredere.
- Contracte care necesită interpretare subiectivă sau negociere de către părți.
- Contexte în care una dintre părți poate avea motive să conteste validitatea a codului în sine.
Model 1: escrow automat
Escrow este cel mai natural caz de utilizare pentru contractele inteligente legale: un dobânditor depune fonduri care sunt eliberate vânzătorului numai atunci când este îndeplinită o condiție specificație (livrare confirmată, etapă aprobată). În caz de dispută, un arbitru hotărăște pre-desemnate.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title LegalEscrow
* @notice Contratto di escrow per accordi commerciali.
* @dev Collega automaticamente pagamento a consegna verificata.
* Non sostituisce un accordo contrattuale scritto:
* usare come complemento tecnico a un contratto tradizionale.
*/
contract LegalEscrow {
// --- Tipi ---
enum EscrowState { Created, Funded, Delivered, Disputed, Released, Refunded }
struct EscrowAgreement {
address payable buyer;
address payable seller;
address arbiter; // arbitro per le dispute
uint256 amount; // importo in wei
uint256 releaseAfter; // timestamp dopo cui il buyer può forzare il rilascio
EscrowState state;
string legalContractHash; // SHA-256 del contratto legale allegato
string description;
}
// --- Storage ---
mapping(uint256 => EscrowAgreement) public agreements;
uint256 private nextAgreementId;
// --- Events ---
event AgreementCreated(uint256 indexed id, address buyer, address seller, uint256 amount);
event Funded(uint256 indexed id, uint256 amount);
event DeliveryConfirmed(uint256 indexed id, address confirmedBy);
event DisputeRaised(uint256 indexed id, address raisedBy, string reason);
event ArbiterDecision(uint256 indexed id, bool releasedToSeller);
event FundsReleased(uint256 indexed id, address recipient, uint256 amount);
// --- Modificatori ---
modifier onlyBuyer(uint256 id) {
require(msg.sender == agreements[id].buyer, "Solo il buyer può eseguire questa azione");
_;
}
modifier onlySeller(uint256 id) {
require(msg.sender == agreements[id].seller, "Solo il seller può eseguire questa azione");
_;
}
modifier onlyArbiter(uint256 id) {
require(msg.sender == agreements[id].arbiter, "Solo l'arbitro può eseguire questa azione");
_;
}
modifier inState(uint256 id, EscrowState expected) {
require(agreements[id].state == expected, "Operazione non valida nello stato corrente");
_;
}
// --- Funzioni ---
/**
* @notice Crea un nuovo accordo di escrow.
* @param seller Indirizzo del venditore
* @param arbiter Indirizzo dell'arbitro (notaio, avvocato, DAO di arbitrato)
* @param legalContractHash Hash SHA-256 del contratto PDF allegato off-chain
* @param description Descrizione dell'accordo
* @param daysToAutoRelease Giorni dopo i quali il buyer può forzare il rilascio
*/
function createAgreement(
address payable seller,
address arbiter,
string calldata legalContractHash,
string calldata description,
uint256 daysToAutoRelease
) external returns (uint256 agreementId) {
require(seller != address(0) && arbiter != address(0), "Indirizzi non validi");
require(seller != msg.sender, "Buyer e seller non possono coincidere");
agreementId = nextAgreementId++;
agreements[agreementId] = EscrowAgreement({
buyer: payable(msg.sender),
seller: seller,
arbiter: arbiter,
amount: 0,
releaseAfter: block.timestamp + (daysToAutoRelease * 1 days),
state: EscrowState.Created,
legalContractHash: legalContractHash,
description: description
});
emit AgreementCreated(agreementId, msg.sender, seller, 0);
}
/**
* @notice Il buyer finanzia l'escrow inviando ETH.
*/
function fund(uint256 id)
external
payable
onlyBuyer(id)
inState(id, EscrowState.Created)
{
require(msg.value > 0, "Importo deve essere maggiore di zero");
agreements[id].amount = msg.value;
agreements[id].state = EscrowState.Funded;
emit Funded(id, msg.value);
}
/**
* @notice Il buyer conferma la consegna e rilascia i fondi al seller.
*/
function confirmDelivery(uint256 id)
external
onlyBuyer(id)
inState(id, EscrowState.Funded)
{
agreements[id].state = EscrowState.Released;
agreements[id].seller.transfer(agreements[id].amount);
emit DeliveryConfirmed(id, msg.sender);
emit FundsReleased(id, agreements[id].seller, agreements[id].amount);
}
/**
* @notice Il buyer o il seller apre una disputa.
*/
function raiseDispute(uint256 id, string calldata reason)
external
inState(id, EscrowState.Funded)
{
require(
msg.sender == agreements[id].buyer || msg.sender == agreements[id].seller,
"Solo buyer o seller possono aprire una disputa"
);
agreements[id].state = EscrowState.Disputed;
emit DisputeRaised(id, msg.sender, reason);
}
/**
* @notice L'arbitro risolve la disputa.
* @param releaseToSeller true = fondi al seller, false = rimborso al buyer
*/
function resolve(uint256 id, bool releaseToSeller)
external
onlyArbiter(id)
inState(id, EscrowState.Disputed)
{
uint256 amount = agreements[id].amount;
agreements[id].state = releaseToSeller ? EscrowState.Released : EscrowState.Refunded;
if (releaseToSeller) {
agreements[id].seller.transfer(amount);
emit FundsReleased(id, agreements[id].seller, amount);
} else {
agreements[id].buyer.transfer(amount);
emit FundsReleased(id, agreements[id].buyer, amount);
}
emit ArbiterDecision(id, releaseToSeller);
}
}
Model 2: Plată condiționată cu Oracle
Multe acorduri legale depind de evenimente din lumea reală care nu sunt direct verificabil în lanț: „produsul a ajuns în Italia”, „KPI-ul a fost atins”, „brevetul a fost aprobat”. The oracol (Legătura este cea mai răspândită) ele aduc date în afara lanțului în blockchain într-un mod verificabil.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
/**
* @title ConditionalPayment
* @notice Pagamento condizionale: si sblocca quando un KPI supera una soglia.
* Esempio: bonus al fornitore quando il tasso di soddisfazione cliente > 4.5/5
*/
contract ConditionalPayment {
address payable public beneficiary; // chi riceve il pagamento
address public payer; // chi ha depositato i fondi
uint256 public threshold; // soglia KPI (in base agli accordi)
uint256 public depositedAmount;
bool public paid;
// Oracle Chainlink per il KPI (es. customer satisfaction score)
AggregatorV3Interface internal kpiOracle;
event PaymentReleased(address to, uint256 amount, int256 kpiValue);
event PaymentRefunded(address to, uint256 amount, int256 kpiValue);
constructor(
address payable _beneficiary,
uint256 _threshold,
address _oracleAddress // indirizzo del price feed / data feed Chainlink
) {
payer = msg.sender;
beneficiary = _beneficiary;
threshold = _threshold;
kpiOracle = AggregatorV3Interface(_oracleAddress);
}
function deposit() external payable {
require(msg.sender == payer, "Solo il payer può depositare");
require(msg.value > 0, "Importo deve essere positivo");
depositedAmount = msg.value;
}
function checkAndPay() external {
require(!paid, "Pagamento già eseguito");
require(depositedAmount > 0, "Nessun fondo depositato");
// Leggi il valore del KPI dall'oracle Chainlink
(, int256 kpiValue, , ,) = kpiOracle.latestRoundData();
paid = true;
if (uint256(kpiValue) >= threshold) {
// KPI raggiunto: paga il beneficiario
beneficiary.transfer(depositedAmount);
emit PaymentReleased(beneficiary, depositedAmount, kpiValue);
} else {
// KPI non raggiunto: rimborsa il payer
payable(payer).transfer(depositedAmount);
emit PaymentRefunded(payer, depositedAmount, kpiValue);
}
}
}
Vyper pentru contracte inteligente de înaltă securitate
Pentru tranzacții juridice de mare valoare, multe echipe preferă Vyper la Soliditate din motive de siguranta. Vyper este un limbaj de programare asemănător Python pentru EVM care elimină prin proiect unele clase de vulnerabilități prezente în Solidity: fără moștenire, fără supraîncărcare, fără recursivitate, fără modificare dinamică a matricelor.
# @version 0.3.10
# @title SimpleEscrow - Vyper version
# @notice Implementazione Vyper dell'escrow per alta sicurezza.
# Vyper e più verboso ma più leggibile e auditable di Solidity.
# --- Strutture Dati ---
struct Agreement:
buyer: address
seller: address
amount: uint256
released: bool
refunded: bool
disputed: bool
# --- Storage ---
agreements: HashMap[uint256, Agreement]
agreement_count: uint256
owner: address
# --- Events ---
event Funded: indexed(agreement_id: uint256, amount: uint256)
event Released: indexed(agreement_id: uint256)
event Disputed: indexed(agreement_id: uint256)
# --- Costruttore ---
@external
def __init__():
self.owner = msg.sender
self.agreement_count = 0
# --- Funzioni ---
@external
def create_agreement(seller: address) -> uint256:
"""Crea un nuovo accordo di escrow."""
assert seller != msg.sender, "Buyer e seller non possono coincidere"
assert seller != empty(address), "Seller non valido"
agreement_id: uint256 = self.agreement_count
self.agreements[agreement_id] = Agreement({
buyer: msg.sender,
seller: seller,
amount: 0,
released: False,
refunded: False,
disputed: False
})
self.agreement_count += 1
return agreement_id
@external
@payable
def fund(agreement_id: uint256):
"""Il buyer finanzia l'escrow."""
agreement: Agreement = self.agreements[agreement_id]
assert agreement.buyer == msg.sender, "Solo il buyer può finanziare"
assert msg.value > 0, "Importo deve essere positivo"
assert agreement.amount == 0, "Accordo già finanziato"
self.agreements[agreement_id].amount = msg.value
log Funded(agreement_id, msg.value)
@external
def release(agreement_id: uint256):
"""Il buyer rilascia i fondi al seller."""
agreement: Agreement = self.agreements[agreement_id]
assert agreement.buyer == msg.sender, "Solo il buyer può rilasciare"
assert not agreement.released, "Gia rilasciato"
assert not agreement.disputed, "In corso di disputa"
assert agreement.amount > 0, "Nessun fondo depositato"
amount: uint256 = agreement.amount
self.agreements[agreement_id].released = True
self.agreements[agreement_id].amount = 0
send(agreement.seller, amount)
log Released(agreement_id)
Testarea cu cască de protecție
Niciun contract inteligent nu ar trebui să intre în producție fără o suită completă de testare. Hardhat este mediul de dezvoltare standard pentru contractele inteligente Ethereum, cu suport pentru TypeScript și testare cu Ethers.js.
import { ethers } from "hardhat";
import { expect } from "chai";
import { LegalEscrow } from "../typechain-types";
describe("LegalEscrow", function () {
let escrow: LegalEscrow;
let buyer: any, seller: any, arbiter: any, other: any;
beforeEach(async function () {
[buyer, seller, arbiter, other] = await ethers.getSigners();
const EscrowFactory = await ethers.getContractFactory("LegalEscrow");
escrow = await EscrowFactory.deploy() as LegalEscrow;
});
describe("Happy path: buyer conferma consegna", function () {
it("dovrebbe rilasciare i fondi al seller dopo conferma", async function () {
// Crea accordo
const tx = await escrow.connect(buyer).createAgreement(
seller.address, arbiter.address,
"abc123sha256hash", "Fornitura software", 30
);
const receipt = await tx.wait();
const agreementId = 0;
// Buyer finanzia
const amount = ethers.parseEther("1.0");
await escrow.connect(buyer).fund(agreementId, { value: amount });
// Verifica saldo seller prima
const sellerBalanceBefore = await ethers.provider.getBalance(seller.address);
// Buyer conferma consegna
await escrow.connect(buyer).confirmDelivery(agreementId);
// Verifica saldo seller dopo: deve essere aumentato
const sellerBalanceAfter = await ethers.provider.getBalance(seller.address);
expect(sellerBalanceAfter - sellerBalanceBefore).to.equal(amount);
});
});
describe("Dispute path: arbitro risolve a favore buyer", function () {
it("dovrebbe rimborsare il buyer su decisione dell'arbitro", async function () {
const agreementId = 0;
const amount = ethers.parseEther("2.0");
await escrow.connect(buyer).createAgreement(
seller.address, arbiter.address, "hash456", "Consulenza", 30
);
await escrow.connect(buyer).fund(agreementId, { value: amount });
await escrow.connect(buyer).raiseDispute(agreementId, "Deliverable non conforme");
const buyerBalanceBefore = await ethers.provider.getBalance(buyer.address);
await escrow.connect(arbiter).resolve(agreementId, false); // false = rimborso buyer
const buyerBalanceAfter = await ethers.provider.getBalance(buyer.address);
expect(buyerBalanceAfter - buyerBalanceBefore).to.be.closeTo(amount, ethers.parseEther("0.01"));
});
});
describe("Sicurezza: accessi non autorizzati", function () {
it("dovrebbe rigettare confirmDelivery da non-buyer", async function () {
await escrow.connect(buyer).createAgreement(
seller.address, arbiter.address, "hash789", "Test", 30
);
await escrow.connect(buyer).fund(0, { value: ethers.parseEther("1.0") });
await expect(
escrow.connect(other).confirmDelivery(0)
).to.be.revertedWith("Solo il buyer può eseguire questa azione");
});
});
});
Vulnerabilități comune și securitate
Vulnerabilități critice în contractele inteligente legale
- Reintrare: modelul de apel/transfer poate activa un contract rău intenționat să apeleze din nou funcția înainte ca starea să fie actualizată. Utilizați întotdeauna modelul de verificări-efecte-interacțiuni și ReentrancyGuard de la OpenZeppelin.
- Număr întreg depășire/depășire: în Solidity pre-0,8 a fost comun; din Versiunea 0.8 operațiile aritmetice sunt inversate automat. Utilizați întotdeauna Soliditate 0,8+.
- Manipularea Oracle: dacă contractul depinde de un oracol, a atacatorul sofisticat poate manipula datele oracolului (de exemplu, împrumut flash + oracol de preț). Utilizați Chainlink cu prețuri medii ponderate în timp (TWAP).
- Funcționare față: tranzacțiile sunt vizibile mai întâi în mempool de confirmare. Pentru acordurile în care ordinea contează, utilizați schema commit-reveal.
- Imuabilitatea erorilor: spre deosebire de software-ul tradițional, o eroare dintr-un contract inteligent nu poate fi „corticată” după implementare. Folosiți modele proxy-uri actualizabile (EIP-1967) sau adăugați un mecanism de înghețare pentru situații de urgență.
Legal Wrapper: Conexiunea cu lumea reală
Un contract inteligent în sine nu creează obligații legale între părți. A. este nevoie ambalaje legale: un acord contractual tradițional care:
- Identificați părțile contractante cu date personale reale
- Specifică contractul inteligent la adresa X și mecanismul de execuție a acordului descris în contract
- Definește jurisdicția aplicabilă și locul de desfășurare a litigiilor
- Include codul hash SHA-256 al codului sursă al contractului pentru referință
- Stabilește ce se întâmplă în cazul unui bug sau al unui comportament neașteptat al contractului inteligent
Concluzii
Contractele inteligente pentru acordurile legale sunt un instrument puternic, dar necesită: proiectare atentă pentru a identifica cazurile de utilizare adecvate, un cod scris cu standarde ridicate de securitate, testare exhaustivă, auditat de experți în securitate blockchain și un pachet legal care îi ancorează în sistem juridic tradițional.
Viitorul contractelor inteligente legale nu este înlocuirea contractelor tradiționale, dar completarea lor: cele mai complexe acorduri comerciale vor fi hibride, cu clauze executabile automat în lanț și clauze interpretative gestionate de către arbitri umani.
Seria LegalTech și AI
- NLP pentru analiza contractelor: de la OCR la înțelegere
- Arhitectura platformei e-Discovery
- Automatizarea conformității cu Dynamic Rules Engine
- Contract inteligent pentru acorduri juridice: Solidity și Vyper (acest articol)
- Rezumat documente legale cu IA generativă
- Legea motoarelor de căutare: înglobări vectoriale
- Semnătura digitală și autentificarea documentelor la Scala
- Confidențialitatea datelor și sisteme de conformitate GDPR
- Crearea unui asistent legal AI (copilot juridic)
- Model de integrare a datelor LegalTech







