The Friendzone: An ethereum-based solution for organizing plans

December 26th, dinner with your group of friends. After sharing some appetizers and eating some tasty entrees accompanied with one or two bottles of wine, “that” friend stops all conversations and says:

“Hey! Hey hey hey! *pokes continuously friend’s arm* why don’t we all go… next year… to __(enter random place here)__!
We could go and do __(crazy activity )__!
And then go to __(weird place)__!
For friendship!”
— “That” friend

The friend group gets excited about doing something together, now that everyone is living their own lives working in different cities it is hard to get to spend some quality memorable time with each other.

The difficult part is not to want to do something with your friends, but to agree on what/where/when that “thing” would be.

It feels like the ethereum blockchain was built with this issue in mind. It is our fair and always-reliable friend with perfect memory and no greediness nor reason to sway a vote in their favor. We shall not let any more friendships be broken!

The plan here is to build a set of smart contracts that will manage this tough endeavor for us.

We are going to write three contracts:

  • The Friendzone (Friendzone.sol): In charge of creating new plans. It will serve as entry point for the smart contract system.
  • The Plan (Plan.sol): An instance of a Plan will be what friends will gather around to add and vote for initiatives. An example could be “trip to Spain” with 8 friends.
  • The Initiative (Plan.sol): An instance of an Initiative will be voted on by the members of the plan it belongs to. The owner of the plan will determine how many votes are needed to pass the initiative. The owner is also in charge of opening and closing the initiative for voting. Members cannot vote more than once on a given initiative.

For information on how to set up the tools we will be using visit check out the README on the Github repository I put together with the code.

Before we jump into the code, a bit about how we will structure the contracts:

We are going to use Solidity 0.4.11, a high-level programming language that looks similar to Javascript and compiles to Ethereum Virtual Machine (EVM) code, to compile the contracts. So as good practice, we should add the corresponding pragma to the top of each contract file:

pragma solidity ^0.4.11;

This means that the compilation will fail if it the code is being compiled with a lower version of solidity.

To try to keep things organized, we will organize the contracts the following way:

pragma;
imports;
contract ContractName {
  public storage variables; ...
private storage variables; ...
  events; ...
  modifiers; ...
  constructor; ...
  methods;   ...
}

I’ll give a quick description of each piece:

  • pragma: As mentioned before, defining a pragma sets some constraints on which version of solidity we will accept to compile our code.
  • imports: As you probably already know because of other programming languages, imports are used to include other files to be used within this file. We will be importing other contracts.
  • contract ContractName {}: The definition of the actual contract. If you are familiar with Javascript ES6 it’s worth noting that the contract with the same name as the file name will be exported by default.
  • public storage variables: If you want to know more about the difference between storage and memory stored variables check out the solidity docs section about it here. Making a variable public will create a getter method for it.
  • private storage variables: Storage variables without getter methods associated to them.
  • events: When an event is called, listeners in the “outside world” (TM) will be notified. For example, if you are using Javascript’s Web3 library to interact with a set of contracts, you can listen to events in a contract and execute some JS code (a getter for some new information for example) to process the event.
  • modifiers: They are used to extend a method’s functionality without repeating code. A good example for this is the Ownable.sol library in the OpenZeppelin framework which allows you to make a function only callable only by the owner of the contract.
  • constructor: The constructor of a contract is executed only once when the contract is created. It is defined as a function that has the same name as the contract itself. It is a good place to set some ownership rights since the creator of the contract is usually the address that wants to have more privileges over it.
  • methods: We will write any methods that interact with the contract here. These methods will either mutate the state of the blockchain or get and process some of it. It is good practice to add the constant keyword to methods that will not mutate the state.

The Friendship Code

For the entry point we will write Friendzone.sol. This contract will be in charge exclusively of creating plans. The definition of what a plan is will be set in Plan.sol, but we know what will be needed to create a new Plan:

  • A name for the plan
  • A set of initial members

The creation of the plan will be then emitted through an event, which will communicate to anyone listening that a new plan has been created under the address passed as an argument.

pragma solidity ^0.4.11;
import './Plan.sol';
contract Friendzone {
event NewPlanCreated(Plan planAddress);
function createNewPlan(bytes32 name, address[] members) returns (Plan planAddress) {
// we instantiate the new plan
Plan newPlan = new Plan(name, members);
// we emit the new plan
NewPlanCreated(newPlan);
// we return the address of the new plan
return newPlan;
}
}

In the contract Plan.sol, which is where the logic for managing each Plan and its Initiatives will live, is what users will be interacting with once the plan has been created.

The Plan contract is going to be a bit more complex. It will be in charge of managing the creation of new initiatives and the addition of new members.

Let’s use our “Trip to Spain” as an example to explain what a real-life instance of Plan.sol would look like. I will be the owner, and my friends Alex, Diego and Miguel are coming with me, therefore we will all be members of the plan.

  • As the owner of the plan, I should be the one to add members to the new plan. I should also be able to transfer the ownership of the plan to another member.
  • Any member should be able to create a new initiative. But only the owner of the plan should be able to open and close any initiative for voting. We will get to that in the next article when we get into voting and Initiatives. Diego might want to suggest we visit the Cave of Altamira to see ancient meme drawings by creating the initiative, but a certain level of coordination will be needed for the voting part.

Let’s translate this logic now to a smart contract. We will be following the contract schema I defined earlier.

pragma solidity ^0.4.11;
import './Initiative.sol';
contract Plan {
  // Public
address public owner;
bytes32 public name;
address[] public members;
mapping(address => bool) public membersMap;
  // Private
Initiative[] initiatives;
  // Events
event NewInitiativeAdded(address initiative);
event NewMemberAdded(address newMember);
event OwnershipTransferred(address newOwner);
  // Modifiers
modifier onlyOwner() {}
modifier onlyMembers() {}
  // Constructor
function Plan(bytes32 _name, address[] _members) {}
  // Methods
function transfer(address _newOwner) onlyOwner returns (bool) {}
function addMember(address newMember) onlyOwner returns (bool) {}
function addInitiative(
uint votesNeededToPass,
bytes32 name,
bytes32 description
) onlyMembers returns (Initiative newInitiativeAddress) {}
}

For more information about the address and mapping types check out this and this respectively.

You might be wondering why we have a membersMap mapping and a members array. The idea behind this is to easily check if a member is part of a plan in constant time through the mapping and to iterate over the set of members through the members array since there is no easy way of iterating over mappings. The array will also be helpful when we are creating new Initiatives for a collection of voters.

Another thing that might have caught your attention is how a modifier is used. But before we get there, let me quickly show you what a modifier looks like:

modifier onlyOwner() {
  require(owner == msg.sender);

_;
}

require is used to throw an exception if it evaluates the wrapped expression as false. For our onlyOwner modifier, we are checking if the address that is operating with the contract is the same as the address stored in the owner variable. So if you are calling a method marked with this modifier and you are not the owner of the contract, you will get an exception. The _; is a placeholder for where the rest of the code of the method you are marking will go. Then, the only thing you will have to do is add onlyOwner right after the closing parenthesis in any method.

Finally you might want to point out the fact that I am using bytes32 instead of string as type for the name of the plan. The reason here is pretty well explained in this stack-overflow post. There is a reason for everything on the World Wide Web!


With this ends the first installment of our “ethereum-based friendship issue solver” articles. In the next article we will be implementing the methods in Plan.sol, we will take a stab at what an Initiative could look like and we will figure out how the voting system for a given initiative will work. Hopefully we have set the grounds on smart contracts’ structures and have covered the basics of Solidity so that we can focus on preventing friendships from self-destruction.

Notes

If you want to learn more about ethereum check out the white paper.

If you want to learn about how to make ethereum contracts secure give Zeppelin Solutions and their zeppelin-solidity Github repo a read. They are great resources to understand what are good and bad ideas when writing smart contracts. zeppelin-solidity is also a great set thoroughly audited contracts you can use in your own projects to achieve a high level of security.

Another great resource for best practices is this repo by ConsenSys.

Thank you for reading! Feel free to reach out to me over in the comments section or through Twitter.

Disclaimer: This codebase is a living thing! Feel free to contribute with thoughts, ideas or constructive criticism.