Chytrá smlouva pro právní dohody: Solidita a Vyper
V roce 2025 Arizona a Wyoming výslovně uznaly, že kodifikují smlouvy v inteligentních smlouvách mohou mít právně závaznou hodnotu, pokud splňují požadavky tradiční aspekty smluvního práva: nabídka, přijetí, příčina a kapacita stran. Nicméně kritický rozdíl zůstává: jeden chytré smlouvy and a program that automaticky provádí předem definované podmínky; A zákonná smlouva a dohoda závazné mezi stranami. Ne každá chytrá smlouva je zákonná smlouva a ne každá dohoda legální a automatizovatelné pomocí chytré smlouvy.
V tomto článku prozkoumáme praktickou implementaci inteligentních smluv pro dohody skutečné právní: automatická úschova u třetí osoby, podmíněná platba, smlouvy NDA s vynucováním v řetězci, a právní důsledky v každém případě. Kód je v Solidity (Ethereum) s porovnáním s Vyperem pro případy použití s vysokým zabezpečením.
Co se naučíte
- Rozdíl mezi inteligentní smlouvou a právní smlouvou: kdy to funguje a kdy ne
- Automatický vzor úschovy v Solidity
- Smlouva o podmíněné platbě (platba na dobírku)
- Vyper pro inteligentní smlouvy s vysokým zabezpečením: výhody versus solidnost
- Testování s Hardhat a Foundry
- Bezpečnostní audity: běžné zranitelnosti a jak se jim vyhnout
- Právní obal: propojení chytrých smluv s tradičními smlouvami
Když má inteligentní smlouva právní smysl
Inteligentní smlouvy vynikají ve scénářích, kde:
- Podmínky jsou objektivně ověřitelné na řetězci: platba přijde (událost blockchain), uplyne datum vypršení platnosti (časové razítko bloku), je převedena NFT.
- Strany si nevěří a chtějí je odstranit zprostředkovatele (banka, notář, agent důvěry).
- Automatické a požadované provedení: žádná strana nesmí "aktivovat" ručně, abyste splnili svůj závazek.
Chytré smlouvy jsou nevhodné pro:
- Dohody závislé na skutečných událostech, které nelze ověřit v řetězci (např. „zboží dorazilo v dobrém stavu“) – vyžadují spolehlivé věštce.
- Smlouvy, které vyžadují subjektivní výklad nebo vyjednávání stran.
- Kontexty, ve kterých může mít jedna strana důvod zpochybnit platnost samotného kódu.
Vzor 1: Automatická úschova
Úschova je nejpřirozenějším případem použití pro legální chytré smlouvy: nabyvatel ukládá prostředky, které se uvolní prodávajícímu pouze při splnění podmínky specifikace (potvrzená dodávka, schválený milník). V případě sporu rozhodce předem určená rozhoduje.
// 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);
}
}
Vzor 2: Podmíněná platba se společností Oracle
Mnoho právních dohod závisí na skutečných událostech, které nejsou přímo ověřitelné na řetězci: „produkt dorazil do Itálie“, „KPI bylo dosaženo“, „patent byl schválen“. The věštec (Chainlink je nejrozšířenější) přenášejí data mimo řetězec do blockchainu ověřitelným způsobem.
// 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 pro inteligentní smlouvy s vysokým zabezpečením
Mnoho týmů dává přednost právním dohodám s vysokou hodnotou Vyper ve společnosti Solidity z bezpečnostních důvodů. Vyper je programovací jazyk podobný Pythonu pro EVM, který záměrně eliminuje některé třídy zranitelností přítomných v Solidity: žádná dědičnost, žádné přetěžování, žádná rekurze, žádné dynamické úpravy polí.
# @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)
Testování s Hardhat
Žádná inteligentní smlouva by neměla jít do výroby bez kompletní testovací sady. Hardhat je standardní vývojové prostředí pro chytré smlouvy Ethereum s podporou pro TypeScript a testování s 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");
});
});
});
Běžné chyby zabezpečení a zabezpečení
Kritická zranitelnost v právních inteligentních smlouvách
- Reentrancy: vzor volání/přepojení může umožnit uzavření smlouvy škodlivé volat funkci znovu před aktualizací stavu. Vždy používejte OpenZeppelin checks-effects-interactions a vzor ReentrancyGuard.
- Přetečení/podtečení celého čísla: v Solidity před 0,8 to bylo běžné; od aritmetické operace verze 0.8 se automaticky vrátí zpět. Vždy používejte Pevnost 0,8+.
- Manipulace s Oracle: pokud smlouva závisí na věštci, a sofistikovaný útočník může manipulovat s daty oracle (např. flash půjčka + cena oracle). Použijte Chainlink s časově váženými průměrnými cenami (TWAP).
- Přední běh: transakce jsou nejprve viditelné v mempoolu potvrzení. Pro dohody, kde záleží na objednávce, použijte schéma potvrzení-odhalení.
- Neměnnost chyby: na rozdíl od tradičního softwaru, chybu v chytré smlouvě nelze po nasazení „opravit“. Používejte vzory upgradovatelné proxy (EIP-1967) nebo přidat mechanismus zmrazení pro případ nouze.
Legal Wrapper: Spojení se skutečným světem
Samotná inteligentní smlouva nevytváří mezi stranami právní závazky. A. je potřeba legální obaly: tradiční smluvní ujednání, které:
- Identifikujte smluvní strany skutečnými osobními údaji
- Určuje inteligentní smlouvu na adrese X a mechanismus provádění dohody popsané ve smlouvě
- Definuje příslušnou jurisdikci a místo pro spory
- Zahrnuje hash SHA-256 zdrojového kódu smlouvy pro referenci
- Stanovuje, co se stane v případě chyby nebo neočekávaného chování smart kontraktu
Závěry
Chytré smlouvy pro právní dohody jsou mocným nástrojem, ale vyžadují: pečlivý návrh k identifikaci vhodných případů použití, kód napsané s vysokými bezpečnostními standardy, důkladné testování, auditováno experti na bezpečnost blockchainu a právní obal, který je ukotví v systému tradiční právní.
Budoucnost legálních smart kontraktů není nahrazením tradičních kontraktů, ale jejich doplnění: nejsložitější obchodní dohody budou hybridní, s klauzulemi automaticky spustitelnými v řetězci a spravovanými interpretačními klauzulemi lidskými rozhodčími.
Série LegalTech a AI
- NLP pro analýzu smluv: od OCR k porozumění
- Architektura platformy e-Discovery
- Automatizace shody s dynamickými pravidly
- Chytrá smlouva pro právní dohody: Solidita a Vyper (tento článek)
- Shrnutí právních dokumentů s generativní AI
- Zákon o vyhledávačích: vektorové vkládání
- Digitální podpis a ověřování dokumentů ve společnosti Scala
- Ochrana osobních údajů a systémy dodržování GDPR
- Vytvoření právního asistenta AI (právní kopilot)
- Vzor integrace dat LegalTech







