법적 계약을 위한 스마트 계약: Solidity 및 Vyper
2025년 애리조나와 와이오밍은 성문화된 계약을 명시적으로 인정했습니다. 스마트 계약에서는 요구 사항을 충족하면 법적 구속력이 있는 가치를 가질 수 있습니다. 계약법의 전통적인 측면: 제안, 수락, 당사자의 원인 및 능력. 그러나 중요한 차이점은 다음과 같습니다. 스마트 계약 그리고 그 프로그램은 미리 정의된 조건을 자동으로 실행합니다. 에이 법적 계약 그리고 합의 당사자 간의 구속력. 모든 스마트 계약이 법적 계약은 아니며 모든 계약이 그런 것은 아닙니다. 합법적이며 스마트 계약을 통해 자동화할 수 있습니다.
이 기사에서는 계약을 위한 스마트 계약의 실제 구현을 살펴봅니다. 실제 법률: 자동 에스크로, 조건부 결제, 온체인 집행을 통한 NDA, 그리고 각 경우의 법적 의미. 코드는 Solidity(Ethereum)에 있습니다. 높은 보안 사용 사례를 위해 Vyper를 사용하세요.
무엇을 배울 것인가
- 스마트 계약과 법적 계약의 차이점: 작동할 때와 작동하지 않을 때
- Solidity의 자동 에스크로 패턴
- 조건부 결제 계약(대금 착불)
- 높은 보안 스마트 계약을 위한 Vyper: 장점과 Solidity 비교
- Hardhat 및 Foundry를 사용한 테스트
- 보안 감사: 일반적인 취약점과 이를 방지하는 방법
- 법적 래퍼: 스마트 계약을 기존 계약에 연결
스마트 계약이 법적 의미를 갖는 경우
스마트 계약은 다음과 같은 시나리오에서 탁월합니다.
- 조건은 온체인에서 객관적으로 검증 가능합니다. 결제 도착(블록체인 이벤트), 만료일 통과(블록 타임스탬프), NFT가 전송됩니다.
- 당사자들은 서로를 신뢰하지 않는다. 그리고 그들은 제거하고 싶어 중개자(은행, 공증인, 신탁 대리인).
- 자동 및 원하는 실행: 어떤 정당도 있어서는 안 된다 귀하의 의무를 이행하려면 수동으로 "활성화"하십시오.
스마트 계약은 다음에 적합하지 않습니다.
- 온체인에서 확인할 수 없는 실제 사건에 의존하는 계약 (예: "상품이 양호한 상태로 도착했습니다") - 신뢰할 수 있는 오라클이 필요합니다.
- 당사자의 주관적인 해석이나 협상이 필요한 계약.
- 일방 당사자가 타당성에 대해 이의를 제기할 이유가 있을 수 있는 상황 코드 자체의.
패턴 1: 자동 에스크로
에스크로는 법적 스마트 계약의 가장 자연스러운 사용 사례입니다. 인수자 조건이 충족되는 경우에만 판매자에게 지급되는 자금을 예치합니다. 사양(납품 확인, 승인된 마일스톤). 분쟁이 있는 경우 중재인이 미리 지정하여 결정합니다.
// 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);
}
}
패턴 2: Oracle을 통한 조건부 결제
많은 법적 계약은 직접적으로 발생하지 않는 실제 사건에 의존합니다. 검증 가능한 온체인: "제품이 이탈리아에 도착했습니다", "KPI가 달성되었습니다", "특허가 승인되었습니다." 그만큼 신탁 (체인링크가 가장 널리 퍼져 있음) 그들은 검증 가능한 방식으로 오프체인 데이터를 블록체인으로 가져옵니다.
// 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
가치가 높은 법률 거래의 경우 많은 팀이 선호합니다. 바이퍼 솔리디티에서 안전상의 이유로. Vyper는 Python과 유사한 EVM용 프로그래밍 언어입니다. Solidity에 존재하는 일부 취약점 클래스를 의도적으로 제거합니다. 상속 없음, 오버로드 없음, 재귀 없음, 배열의 동적 수정 없음.
# @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)
Hardhat으로 테스트하기
완전한 테스트 스위트 없이는 어떤 스마트 계약도 프로덕션에 들어가서는 안 됩니다. Hardhat은 Ethereum 스마트 계약을 위한 표준 개발 환경입니다. TypeScript 및 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");
});
});
});
일반적인 취약점 및 보안
법적 스마트 계약의 심각한 취약점
- 재진입: 통화/전환 패턴으로 계약이 가능해집니다. 상태가 업데이트되기 전에 함수를 다시 호출하는 것은 악의적입니다. 항상 OpenZeppelin의 검사-효과-상호작용 및 ReentrancyGuard 패턴을 사용하십시오.
- 정수 오버플로/언더플로: Solidity 0.8 이전 버전에서는 일반적이었습니다. 에서 버전 0.8 산술 연산은 자동으로 되돌려집니다. 항상 사용 견고성 0.8+.
- 오라클 조작: 계약이 오라클에 의존하는 경우 정교한 공격자는 오라클 데이터(예: 플래시 대출 + 가격 오라클)를 조작할 수 있습니다. 시간 가중 평균 가격(TWAP)과 함께 Chainlink를 사용하세요.
- 전면 실행: 트랜잭션은 먼저 mempool에 표시됩니다. 확인의. 순서가 중요한 계약의 경우 커밋-공개 체계를 사용하세요.
- 버그 불변성: 기존 소프트웨어와 달리 스마트 계약의 버그는 배포 후에 "패치"할 수 없습니다. 패턴 사용 업그레이드 가능한 프록시(EIP-1967)를 사용하거나 긴급 상황에 대비한 동결 메커니즘을 추가하세요.
법적 래퍼: 현실 세계와의 연결
스마트 계약만으로는 당사자 간에 법적 의무가 발생하지 않습니다. A. 필요하다 법적 포장지: 다음과 같은 전통적인 계약 합의:
- 실제 개인 데이터로 계약 당사자 식별
- 주소 X의 스마트 계약과 실행 메커니즘을 지정합니다. 계약서에 명시된 계약의
- 분쟁에 대한 적용 가능한 관할권 및 장소를 정의합니다.
- 참조용으로 계약 소스 코드의 SHA-256 해시를 포함합니다.
- 스마트 계약에 버그가 있거나 예상치 못한 동작이 발생하는 경우 어떤 일이 발생하는지 설정합니다.
결론
법적 계약을 위한 스마트 계약은 강력한 도구이지만 다음이 필요합니다. 적절한 사용 사례를 식별하기 위한 신중한 설계, 코드 높은 보안 표준으로 작성되었으며 철저한 테스트를 거쳐 감사를 받았습니다. 블록체인 보안 전문가 및 그들을 시스템에 고정시키는 법적 래퍼 전통적인 법률.
합법적인 스마트 계약의 미래는 전통적인 계약을 대체하는 것이 아니라, 그러나 그 보완은 가장 복잡한 무역 협정이 하이브리드가 될 것이라는 점입니다. 온체인에서 자동으로 실행 가능한 조항 및 관리되는 해석 조항 포함 인간 심판에 의해.
LegalTech 및 AI 시리즈
- 계약 분석을 위한 NLP: OCR에서 이해까지
- e-Discovery 플랫폼 아키텍처
- 동적 규칙 엔진을 통한 규정 준수 자동화
- 법적 계약을 위한 스마트 계약: Solidity 및 Vyper(이 기사)
- Generative AI를 사용한 법률 문서 요약
- 검색 엔진 법칙: 벡터 임베딩
- Scala의 디지털 서명 및 문서 인증
- 데이터 개인정보 보호 및 GDPR 규정 준수 시스템
- 법률 AI 보조원 구축(Legal Copilot)
- LegalTech 데이터 통합 패턴







