Condition-Orientated Programming

How it works

If you have already used Solidity, the chances are that you inadvertently flirted with COP already. Let’s look at a simple token contract:

contract Token {
// The balance of everyone
mapping (address => uint) public balances;
// Constructor - we're a millionaire!
function Token() {
balances[msg.sender] = 1000000;
}
// Transfer `_amount` tokens of ours to `_dest`.
function transfer(uint _amount, address _dest) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
}
}
function transfer(uint _amount, address _dest) {
if (balances[msg.sender] >= _amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
}
}
function transfer(uint _amount, address _dest) {
if (balances[msg.sender] < _amount)
return;
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
}
modifier only_with_at_least(uint x) {
if (balances[msg.sender] >= x) _
}
function transfer(uint _amount, address _dest)
only_with_at_least(_amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
}

Abstraction and Reuse

Suppose we have another function, which allows anyone with a balance more than 1000 to vote on some issue. We’ll assume for now that voting is just a case of setting the value of an address-indexed mapping.

function vote(uint _opinion) {
if (balances[msg.sender] >= 1000) {
votes[msg.sender] = _opinion;
}
}
function vote(uint _opinion) only_when_at_least(1000) {
votes[msg.sender] = _opinion;
}

More complex transitions

By discouraging conditional paths from our state-transitions, we limit the complexity of our state-transitions. This hugely helps with auditing since it allows us apply the divide and conquer strategy to program logic analysis and independently check the logic of state transitions from the conditional logic on which they are gated. However sometimes the state transition itself includes gated logic internally.

function transfer(uint _amount, address _dest) {
if (balances[msg.sender] >= _amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
if (balances[msg.sender] < 1000) {
votes[msg.sender] = 0; // Clear their vote.
}
}
}
function clear_undeserved_vote(account _who)
only_with_under(1000)
only_when_voted {
delete votes[_who];
}
modifier only_with_under(uint x) { if (balances[msg.sender] < x) _ }
modifier only_when_voted { if (votes[msg.sender] != 0) _ }
function transfer(uint _amount, address _dest)
only_with_at_least(_amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
clear_undeserved_vote();
}

Conclusion

The main part of our final contract has changed from:

contract Token
{
//...
function transfer(uint _amount, address _dest) {
if (balances[msg.sender] >= _amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
if (balances[msg.sender] < 1000) {
votes[msg.sender] = 0; // Clear their vote.
}
}
}
function vote(uint _opinion) {
if (balances[msg.sender] >= 1000) {
votes[msg.sender] = _opinion;
}
}
}
contract Token
{
//...
modifier only_with_at_least(uint x) {
if (balances[msg.sender] >= x) _
}
modifier only_with_under(uint x) {
if (balances[msg.sender] < x) _
}
modifier only_when_voted {
if (votes[msg.sender] != 0) _
}
function clear_undeserved_vote(account _who)
only_with_under(1000) only_when_voted {
delete votes[_who];
}
function transfer(uint _amount, address _dest)
only_with_at_least(_amount) {
balances[msg.sender] -= _amount;
balances[_dest] += _amount;
clear_undeserved_vote();
}
function vote(uint _opinion)
only_when_at_least(1000) {
votes[msg.sender] = _opinion;
}
}

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store