法的合意のためのスマートコントラクト: Solidity と Vyper
2025 年に、アリゾナ州とワイオミング州は、成文化された契約を明確に認めました。 スマートコントラクトでは、要件を満たしていれば法的拘束力を持つことができます。 契約法の伝統的な側面: 申し出、受諾、大義および当事者の能力。 ただし、重要な違いは依然として残っています。 スマートコントラクト そしてそのプログラム 事前定義された条件を自動的に実行します。ある 法的契約 そして合意 当事者間の拘束。すべてのスマート コントラクトが法的な契約であるわけではなく、すべての契約が法的な契約であるわけではありません 合法的かつスマートコントラクトで自動化可能。
この記事では、契約のためのスマートコントラクトの実際的な実装について検討します。 実際の法律: 自動エスクロー、条件付き支払い、オンチェーン強制執行による NDA、 そしてそれぞれの場合における法的影響。コードはSolidity (Ethereum)にあります。 高セキュリティのユースケースには Vyper を使用します。
何を学ぶか
- スマートコントラクトと法的コントラクトの違い: 機能する場合と機能しない場合
- Solidity の自動エスクロー パターン
- 条件付き支払契約(代金引換)
- 高セキュリティ スマート コントラクトの Vyper: 利点と Solidity の比較
- ヘルメットとファウンドリを使用したテスト
- セキュリティ監査: 一般的な脆弱性とその回避方法
- 法的ラッパー: スマート コントラクトを従来のコントラクトに接続する
スマートコントラクトが法的に意味がある場合
スマート コントラクトは、次のようなシナリオで優れています。
- 条件はチェーン上で客観的に検証可能です。 支払いが到着(ブロックチェーンイベント)、有効期限が経過(ブロックタイムスタンプ)、 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: オラクルによる条件付き支払い
多くの法的合意は、直接的なものではない現実世界の出来事に依存しています。 オンチェーンで検証可能: 「製品はイタリアに到着しました」、「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 は、EVM 用の Python に似たプログラミング言語です。 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 は、サポート付きの 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+。
- オラクルの操作: 契約が神託に依存している場合、 高度な攻撃者はオラクル データを操作することができます (例: フラッシュ ローン + 価格オラクル)。 Chainlink を時間加重平均価格 (TWAP) で使用します。
- フロントランニング: トランザクションは最初にメモリプールに表示されます 確認のこと。順序が重要な契約の場合は、commit-reveal スキームを使用します。
- バグの不変性: 従来のソフトウェアとは異なり、 スマート コントラクトのバグは、展開後に「パッチ」を適用することはできません。パターンを使用する アップグレード可能なプロキシ (EIP-1967) または緊急時のフリーズ メカニズムを追加します。
Legal Wrapper: 現実世界とのつながり
スマートコントラクトだけでは、当事者間に法的義務が発生するわけではありません。 A.が必要です 法的ラッパー: 従来の契約上の合意:
- 実際の個人データを使用して契約当事者を特定する
- アドレス X のスマート コントラクトと実行メカニズムを指定します 契約書に記載されている合意内容
- 適用される管轄区域と紛争の裁判地を定義します
- 参照用に契約ソース コードの SHA-256 ハッシュが含まれます
- スマートコントラクトのバグまたは予期しない動作が発生した場合に何が起こるかを確立します。
結論
法的合意のためのスマート コントラクトは強力なツールですが、次のものが必要です。 適切なユースケースとコードを特定するための慎重な設計 高いセキュリティ基準に従って作成され、徹底的なテストが行われ、監査を受けています。 ブロックチェーンセキュリティの専門家と、彼らをシステムに固定する法的ラッパー 伝統的な合法。
合法的なスマートコントラクトの将来は、従来のコントラクトに取って代わるものではありません。 しかし、それらを補完するもの: 最も複雑な貿易協定はハイブリッド型になるでしょう。 with 句はオンチェーンで自動的に実行可能であり、管理された解釈句 人間の審判によって。
リーガルテックとAIシリーズ
- 契約分析のための NLP: OCR から理解まで
- 電子証拠開示プラットフォームのアーキテクチャ
- 動的ルールエンジンによるコンプライアンスの自動化
- 法的契約のためのスマート コントラクト: Solidity と Vyper (この記事)
- 生成 AI による法的文書の要約
- 検索エンジンの法則: ベクトル埋め込み
- Scala でのデジタル署名と文書認証
- データプライバシーとGDPRコンプライアンスシステム
- 法務 AI アシスタント (法務副操縦士) の構築
- LegalTech データ統合パターン







