Site Color

Text Color

Ad Color

Text Color

Evergreen

Duotone

Mysterious

Classic

Sign Up to Save Your Colors

or

Decide on dinner with an Ethereum arbitration contract by@captaindaylight

Decide on dinner with an Ethereum arbitration contract

paul christophe Hacker Noon profile picture

paul christophe

Software Engineer

Jack and Jill can’t decide on dinner so they’re bringing in an arbiter to hear their dispute. The problem is they don’t trust the other to independently choose an arbiter.

Let’s write an Ethereum contract to handle their dispute!

This article assumes you have a basic understanding of Ethereum. If you’re still shaky, start with this fantastic article by Preethi Kasireddy.

How Our Contract Will Work

1. Person One creates a new arbitration instance and gives it a description. 🌟

2. Person One and Person Two submit their differing opinions. 😠

3. Person One and Person Two also choose an arbiter. The process doesn’t go forward until both agree on the same account address. 👍

4. The arbiter reviews the two points of view and decide the winner. 🤔 ➡ 🏆

Setup

Feel free to move to the next section if you’ve run a truffle project before

We’ll be using a framework called Truffle to develop our smart contract. It’s a one stop shop for handling compilation, testing, deployment, migrations and much more. We’ll only be using certain features in this post but I encourage you to dig deeper.

Before we dive in, globally install truffle and testrpc.

npm install -g truffle ethereumjs-testrpc

Then, in a new directory run:

truffle init

You should see truffle’s folder structure inflate with some simple examples in the contracts/ folder.

Get Your Feet Wet

Let’s get a feel for the workflow.

1. Kickstart our test client with testrpc. You’ll see some of the accounts you’ll be using to simulate the interaction:

2. In separate tab run truffle compile. This generates a corresponding JSON artifacts from the contracts and drops them into a build folder.

3. Now run truffle migrate to generate files to facilitate contract deployment to the blockchain. Important, testrpc must be running when you migrate, otherwise your contracts won’t deploy to that testing instance.

4. Run truffle console which will connect to our running Ethereum client and allow us to interact with our contracts.

Poke Around

We’re going to modify one of the existing contracts (technically a library… works for our purposes though) ConvertLib.sol. It provides a single method to multiply two numbers.

A method that changes state on the chain is a transaction (costs gas) while any method that only returns data is a call (free).

Truffle’s console has web3 and our contracts ready to go, give it a go:

// get the deployed contract by its class name, returns a promise
> const contract = ConvertLib.deployed()
// invoke the 'convert' method with 'call', also returns a promise
> const response = contract.then(c => c.convert.call(5, 20))
// print out our result!!
> response.then(res => res.toNumber())
100

Great, we’ve interacted with a deployed contract to multiply two numbers!

Let’s Write Some Solidity

Ethereum contracts are written in a high level language called Solidity. If you’re familiar with Javascript or object oriented programming you’ll notice a lot of similarities.

With Solidity we’ll define state variables and establish our rules of arbitration. Afterwards we’ll use Truffle to compile the contract and deploy it to our testing environment. The state and code we define in our contract will then live at a specific address on the Ethereum blockchain.

Let’s start by cleaning house. Remove contracts/ConvertLib.sol and contracts/MetaCoin.sol but keep contracts/Migrations.sol so we can perform migrations.

Then, create a file in the contracts/ folder called Arbitration.sol. Now go into your migrations/ folder and modify 2_deploy_contracts.js:

var Arbitration = artifacts.require('./Arbitration.sol');
module.exports = function(deployer) {
deployer.deploy(Arbitration);
};

Open contracts/Arbitration.sol and begin by defining the solidity version and contract name.

You’ll be adding public variables description and winningOpinion as well as the transaction method setDescription to set the description.

pragma solidity ^0.4.11;
contract Arbitration {
string public description;
string public winningOpinion;
  function setDescription(string _description) {
description = _description;
}
}

Next use the struct type to define how our claimants’ data should look. Then, we’ll use that Claimant type we made to declare an array called claimants. Finally, create a method that adds a new claimant.

contract Arbitration {
string public description;
  struct Claimant {
string opinion;
address addr;
address arbiter;
}
  Claimant[] public claimants;

function setDescription(string _description) {
description = _description;
}
  function addClaimant(string opinion, address arbiter) {
if (claimants.length < 2) {
claimants.push(Claimant(opinion, msg.sender, arbiter));
}

}
}

One thing to note, msg.sender will give us the address of the account sending the addClaimant transaction. msg has other valuable info that it passes along as well.

After claimants have added their proposals, the arbiter will be able to review the disagreement by calling getProposals.

contract Arbitration {
  ...
  function getProposals()
returns (
string _description,
string _proposalOne,
string _proposalTwo
)
{
require(claimants.length == 2);
return (
description,
claimants[0].opinion,
claimants[1].opinion
);
}
}

In the function declaration you’ll see that we define what will be returned with the returns keyword. We use require to allow the code to continue only if two claimants have submitted.

Next, we’ll create a method that allows only the arbiter to choose a winner. First we’ll create a helper method verifyArbiter that selectWinner will use. verifyArbiter cycles through the claimants and makes sure that the sender’s address matches what each claimant has selected. If require doesn’t throw an error, it then sets the contract’s state variable winningOpinion.

contract Arbitration {
  ...
  function verifyArbiter() returns (bool isArbiter) {
for (uint i = 0; i < claimants.length; i++) {
if (msg.sender != claimants[i].arbiter) {
return false;
}
}
    return true;
}
  function selectWinner(uint proposalNumber) {
require(verifyArbiter());
    winningOpinion = claimants[proposalNumber].opinion;
}
}

Interacting With the Contract

Open terminal and run testrpc. Then, in a new tab run truffle compile, truffle migrate and truffle console as we did in the section above. Let’s start by getting a reference to our contract and adding a description to it:

> const contract = Arbitration.deployed()
> contract.then(c => c.description())
''
> contract.then(c => c.setDescription('What to eat for dinner'))
> contract.then(c => c.description())
'What to eat for dinner'

As you can see, we can access state by calling the variable name like a function: description().

Now, go back to your testrpc output and look at the ten addresses it gives you. Set ARBITER_ADDRESS to any of the addresses in the list other than the first because truffle by default uses the first account as the sender.

> contract.then(c => c.addClaimant('burgers', 'ARBITER_ADDRESS'))
> contract.then(c => c.claimants(0))
[ 'burgers',
'FIRST_ADDRESS_LISTED',
'ARBITER_ADDRESS' ]

To add the second claimant, we’ll modify how we call the addClaimant method. The last parameter will take another address from the testrpc account list and simulate sending a call from there.

> contract.then(c => c.addClaimant('pizza', 'ARBITER_ADDRESS', {from: 'SECOND_CLAIMANT_ADDRESS'}))
> contract.then(c => c.claimants(1))
[ 'pizza',
'SECOND_CLAIMANT_ADDRESS',
'ARBITER_ADDRESS' ]

Now imagine the arbiter would like to see the current state of the proposals. Assuming both claimants have voted (otherwise it will throw an error) they’ll get a response like:

> contract.then(c => c.getProposals.call())
[ 'What to eat for dinner', 'burgers', 'pizza' ]

Using the arbiter’s address, let’s select a winner using either 0 for burgers or 1 for pizza.

> contract.then(c => c.selectWinner(1, {from: 'ARBITER_ADDRESS'}))

After the arbiter has selected a winner, anyone can consult the contract for the winner.

> contract.then(c => c.winningOpinion())
'pizza'

We knew pizza was going to win but writing this contract was nonetheless a fruitful exercise in understanding Ethereum. Check out the code here!

Tags