Legal Design Patterns in Smart Contracts
In my previous post I focused on security-related design patterns, and mechanisms for propagating security best practices, in Solidity. In this post I focus on jurisdictional law-related design patterns, replacing security as my primary concern and turning my attention to legal jurisdictional compliance.
Developing a properly-architected smart contract system requires legal considerations in addition to security. One technical solution to a peer-to-peer payments problem domain may legally classify a smart contract system as a money services business, while an alternative implementation may operate free of the responsibilities of an MSB.
Additionally, users considering an interaction with a smart contract may be as concerned with the question of legality as they may be with that of security. In order to ensure the health and growth of any smart contract ecosystem, the users — those interacting with the contracts — must not perceive a legal threat any more than they perceive a security threat.
Leveraging the history of contract law, Bill Marino and Ari Juels have provided a starting point for Solidity design patterns in an attempt to align smart contract composition with modern contract law. In their piece, they advocate for instantiation of several contract law practices within smart contract systems.
Specifically, Marino and Juels provide a contract law perspective and Solidity design patterns for contract termination, rescission and modification. These design patterns bring smart contracts closer to integrating smoothly with a legal domain and jurisdiction, in this case United States contract law.
This mechanism of demonstrating legal compliance attached to a specific jurisdiction is going to be an important component of smart contracts as they integrate into the ways humans and devices interact. Nick Szabo describes this as when “wet code interacts with dry code” — where wet code is the law of humans, and dry code is the law of automated digital contracts and devices.
Marino and Juels present the contract law design patterns Undoable and Modifiable which provide for the following elements of contract law:
- Termination by Right
- Rescission by Agreement
- Rescission by Court
- Modification by Right
- Modification by Agreement
- Reformation
Here is the body of a (sample, not secure) Undoable Solidity contract implementing the Undoable design pattern:
contract Undoable { address partyWithTerminationRight; address partyWithoutTerminationRight; address partialPerformanceApprover; address court; uint terminationFee; uint suggestedPartialPerformanceCompensation; enum State {RescissionByAgreementSuggested, PartialPerformanceCompensationSuggested, PartialPerformanceCompensationApproved} State public state; modifier inState(State _state) { if (state != _state) throw; _
} function terminateOrRescind(uint _terminationFee, address _partyWithTerminationRight, address _partyWithoutTerminationRight, address _court, address _partialPerformanceApprover) { terminationFee = _terminationFee; partyWithTerminationRight = _partyWithTerminationRight; partyWithoutTerminationRight = _partyWithoutTerminationRight; partialPerformanceApprover = _partialPerformanceApprover; court = _court;
} function suggestPartialPerformanceCompensation(uint _suggestedPartialPerformanceCompensation) { if ( partyWithoutTerminationRight == msg.sender) { suggestedPartialPerformanceCompensation = _suggestedPartialPerformanceCompensation; state = State.PartialPerformanceCompensationSuggested;
}
} function approvePartialPerformanceCompensation()inState( State.PartialPerformanceCompensationSuggested){ if (partyWithTerminationRight == msg.sender) { state = State.PartialPerformanceCompensationApproved ;
}
} function terminate()inState(State.PartialPerformanceCompensationApproved){ if (partyWithTerminationRight == msg.sender) { partyWithoutTerminationRight.send(terminationFee); partyWithoutTerminationRight.send( suggestedPartialPerformanceCompensation); selfdestruct(partyWithoutTerminationRight);
}
} function rescindByCourt()inState(State.PartialPerformanceCompensationApproved){ if (court == msg.sender) { partyWithoutTerminationRight.send(suggestedPartialPerformanceCompensation); selfdestruct(partyWithoutTerminationRight);
}
} function suggestRescissionByAgreement()inState(State.PartialPerformanceCompensationApproved){ if (partyWithoutTerminationRight == msg.sender) { state = State.RescissionByAgreementSuggested;
}
} function approveRescissionByAgreement()inState(State.RescissionByAgreementSuggested){ if (partyWithTerminationRight == msg.sender) { partyWithoutTerminationRight.send(suggestedPartialPerformanceCompensation); selfdestruct(partyWithoutTerminationRight);
}
}
}
Marino and Juels further refine the implementation of Undoable into Solidity modifiers.
Here is the body of a Modifier Solidity contract implementing the Modifier design pattern:
contract Satellite { function returnPrice() returns (uint _price){ //calculate price }
}contract Modifiable { uint price; address party1; address party2; address satelliteAddress; address suggestedSatelliteAddress; function setPrice(){ Satellite m = Satellite(satelliteAddress); price = m.returnPrice();
} enum State {ModificationSuggested,ModificationApproved} State public state; modifier inState(State _state) { if (state != _state) throw; _
} function suggestModification(address _suggestedSatelliteAddress){ if (party1 == msg.sender){ suggestedSatelliteAddress = _suggestedSatelliteAddress; state = State.ModificationSuggested;
}
} function approveModification()inState(State.ModificationSuggested){ if (party2 == msg.sender){ satelliteAddress = suggestedSatelliteAddress;
}
}
}
The question of users of smart contracts acting in compliance with jurisdictional law gets trickier when we have parties that wish to interact between jurisdictions, particularly when their local policies are in conflict.
In response to this, law-governed interaction provides a precedent of design patterns for providing locality and interoperability. Locality is when actors engage in a mode of interaction governed by an explicitly stated local (e.g., jurisdictional) policy. Interoperability is when actors with differing or conflicting local policies can interact successfully.
The principles of locality and interoperability are to be realized in Solidity by a Contract architecture that includes the Controller (for locality) and Portal (for interoperability) roles of the LGI framework.
A natural next step from that point is to expand the scope of available legal design patterns and to integrate jurisdiction-specific legal design patterns (such as Undoable and Modifiable) with Locality and Interoperability contract design patterns.
Note that contract source code verification tools will at some point assess the presence and composition of design patterns for legal compliance, even by jurisdiction. These assessments are a reasonable starting point for automation of scoring local (jurisdictional) and interoperable (universal) legal risk of smart contracts.
Providing robust security and legal assurances will set ecosystem-friendly smart contracts apart from a murky landscape of questionable smart contracts.
Thank you to Jordi Baylina and Nick Szabo for comments on this article.