Skip to content

6. Contracts That Send Transactions

Mihai Alisie edited this page Mar 2, 2015 · 2 revisions

Contracts as first class citizens

One of the fundamental ideas behind the use of contracts is that contracts are equal in privileges and abilities to the users outside the network. Everything that a user with a valid private key can do – a contract can do as well. This includes but is not limited to: sending and receiving information to other contracts, creating new contracts, and sending transactions with ether.

Sending transactions

In this tutorial I am going to set out a relatively simple Dapp whose purpose is to raise a certain amount of ether in a fixed period of time.
It is intended to ape the behaviour of kickstarter or similar websites without charging a fee for it’s use. I will be using it as an example to demonstrate how contracts handle ether, how they can send transactions themselves, and how they can register the passing of time. It will also use some of the data structuring that you learnt about in the previous tutorial.
Most of you should be familiar with how a crowdfund campaign works – a proposer will describe to potential investors a campaign they are attempting to secure funding for, set a goal amount they wish to raise, and a time limit in which they wish to raise it. The campaign is successful if the investors contribute enough within the allotted time to reach the goal – in this case the funding is transferred to the proposer from the crowdfunder. If unsuccessful in the allotted time, the investors receive a full refund from the crowdfunder.
In our version of the crowdfunder we will use a contract where the contributions are made in transactions to the contract containing ether, the goal for fund raising is also in ether, and the allotted time is measured in blocks. The contract will need to be able to handle multiple crowdfunded campaigns at a time, keep track of each contribution made, evaluate if the campaign was successful and, either payout the proposer or, refund the investors at the end of time limit.

Data Structuring

When writing a contract the first thing to consider is what information will need to be stored in the contracts storage, and how that data will need to be structured. We can start by considering the information relating to the campaign itself:

Campaign ID
Recipient of funds
Funding Goal
Time Limit

This four item list is sufficient information for us to define the terms of the campaign – we could feasibly add information about the campaigns website or goals however this is probably best left in the front-end. Each campaign will have investors so we will need to store:

Investor account
Amount invested

This will need to be stored in a 2 x n mapping where n is the number of investments made in the campaign. Finally each campaign will need to keep a record of the number of investments made and the total of all the contributions – in order that we may refund contributors if a campaign fails to reach it’s goal.

So with this in mind we will define a pair of structs:

struct Funder { address addr; uint amount; } struct Campaign { address beneficiary; uint fundingGoal; uint numFunders; uint amount; uint deadline; mapping (uint => Funder) funders; }

The first struct ‘Funder’ contains two members ‘addr’ and ‘amount’ which will represent the address of a contributor and the size of their contribution. The second ‘campaign’ contains five elements which store the data relating to the campaign in storage and a sixth element which is a mapping containing the previously declared struct datatype ‘Funder’.

Finally, we declare a mapping of the datatype Campaign which we will call ‘campaigns’, and a stored integer called numCampaigns which will increment by one each time a new campaign is created.

mapping (uint => Campaign) campaigns; uint numCampaigns;

We now have a structured way to store information in the contract for the duration of the campaign.

The contracts functions

Next, we need to define what functions our contract will need to perform and what changes to the state of the contract transactions calling these functions will cause. Previously in our metaCoin examples we used just a single function to transfer coins from one account to another – Here we will need to define several functions. Looking at our definition of a crowdsource we can break it down into three types of operation triggered by three different kinds transactions.
Proposer’s transaction:
This is the transaction sent by the proposer which creates the campaign – it includes the goal amount, the recipient account and the campaign time limit. These are all immutable until the time limit expires.
Investors transactions:
Transactions sent by investors include ether and the ID of the campaign being funded. The transaction is recorded as an investment in the storage space outlined above and the ether sent is added to the total contribution.
End Of Campaign transaction:
When the time has expired a transaction sent to the contract should trigger the end of the fundraising. A transaction is required to be sent to trigger this action as contracts do not run continuously on the blockchain and must be ‘woken’ in order for them to run. This transaction will result in the contract assessing whether the fundraising goal has been met and then either: delivering the proceeds to the proposer, or, refunding the amounts sent by investors.

Our first function needs to create a new campaign:

function newCampaign(address beneficiary, uint goal, uint deadline) returns (uint campaignID) { campaignID = numCampaigns++; Campaign c = campaigns[campaignID]; c.beneficiary = beneficiary; c.fundingGoal = goal; c.deadline = block.number + deadline; }

You will have seen much of this in our previous examples – see how the function takes three values as arguments, and returns a single value ‘campaignID’ which is defined within the function.

Let’s go by this line by line to avoid confusion:
1.We define a function called 

newCampaign it takes three variables as arguments:

beneficiary, 

goal, and 

deadline. It returns one new variable 

campaignIDwhich is created in this line.
Line 2: Here we assign the variable 

campaignID a value equal to the value stored in the state variable 

numCampaigns. It also increments 

numCampaigns by one. This is the first change to the contracts storage.
Line 3: This creates a reference 

c which points to a location in the contracts storage.
Lines 4, 5, and 6: Taking the reference 

c we assign our parameters for the campaign into storage. Note that on line 6 we call a function we haven’t seen before: 

block.number – this returns the value of the current block which the code is executing within. We use this to set the timelimit of the campaign as 

deadlineblocks in the future.

This functions execution will set up a campaign ready for investor contributions.

The second function allows investors to add contributions by sending transactions to the contract:

function contribute(uint campaignID) { Campaign c = campaigns[campaignID]; Funder f = c.funders[c.numFunders++]; f.addr = msg.sender; f.amount = msg.value; c.amount += f.amount; }

This function only takes one argument when it is called 

campaignID but in fact you could consider that every function is also passed two additional arguments

msg.sender and 

msg.value. Going line by line to explain what is happening here:

Line 1: function is defined as taking a single argument campaignID – it has no return value.
Line 2 and 3: Creates references to locations in storage to store a new funder and iterates the number of contributors.
Line 4,5, and 6: here we take the amount of ether passed with the contributors transaction and the public address of the msg.sender and use it record the contribution from this transactions. This is necessary so that in case the campaign fails to meet it’s target we can return the ether to the sender.

We still need a function to end the event, so that’s what we will do next – as you may recall we have two conditions which end a campaign: the time limit runs out, or the fundraising goal is met.

This final function is rather large but you should be able to recognise what it does:

function checkGoalReached(uint campaignID) returns (bool reached) { Campaign c = campaigns[campaignID]; if (c.amount >= c.fundingGoal){ c.beneficiary.send(c.amount); c.amount = 0; c.beneficiary = 0; c.fundingGoal = 0; c.deadline = 0; uint i = 0; uint f = c.numFunders; c.numFunders = 0; while (i <= f){ c.funders[i].addr = 0; c.funders[i].amount = 0; i++; } return true; } if (c.deadline <= block.number){ uint j = 0; uint n = c.numFunders; c.beneficiary = 0; c.fundingGoal = 0; c.numFunders = 0; c.deadline = 0; c.amount = 0; while (j <= n){ c.funders[j].addr.send(c.funders[j].amount); c.funders[j].addr = 0; c.funders[j].amount = 0; j++; } return true; } return false; }

There are two conditionals, run sequentially; The first 

if checks if the fundraising goal has been reached – if it has then the function will send the total amount raised to the beneficiary address defined in our first function

c.beneficiary.send(c.amount);. It will also zero out the storage space that was used by the campaign as it is no longer needed. Finally, it will 

return trueand end code execution.

The second 

if will only execute if the first conditional evaluated to 

false and the goal has not been met. It will check if the deadline defined in the setup of the campaign has passed 

if (c.deadline <= block.number) – if it has, it will loop through the list of contributors returning their ether to them. It will also zero out the storage space that was used by the campaign as it is no longer needed. Finally, it will 

return true and end code execution.

If neither conditional is met, then the function will return false to tell the caller that the campaign hasn’t met either of the conditions required to end it.

Our complete contract looks like this:

contract CrowdFunding { // data structure to hold information about campaign contributors struct Funder { address addr; uint amount; } // Campaign data structure struct Campaign { address beneficiary; uint fundingGoal; uint numFunders; uint amount; uint deadline; mapping (uint => Funder) funders; } //Declares a state variable 'numCampaigns' uint numCampaigns; //Creates a mapping of Campaign datatypes mapping (uint => Campaign) campaigns; //first function sets up a new campaign function newCampaign(address beneficiary, uint goal, uint deadline) returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable Campaign c = campaigns[campaignID]; // assigns reference c.beneficiary = beneficiary; c.fundingGoal = goal; c.deadline = block.number + deadline; } //function to contributes to the campaign function contribute(uint campaignID) { Campaign c = campaigns[campaignID]; Funder f = c.funders[c.numFunders++]; f.addr = msg.sender; f.amount = msg.value; c.amount += f.amount; } // checks if the goal or time limit has been reached and ends the campaign function checkGoalReached(uint campaignID) returns (bool reached) { Campaign c = campaigns[campaignID]; if (c.amount >= c.fundingGoal){ uint i = 0; uint f = c.numFunders; c.beneficiary.send(c.amount); c.amount = 0; c.beneficiary = 0; c.fundingGoal = 0; c.deadline = 0; c.numFunders = 0; while (i <= f){ c.funders[i].addr = 0; c.funders[i].amount = 0; i++; } return true; } if (c.deadline <= block.number){ uint j = 0; uint n = c.numFunders; c.beneficiary = 0; c.fundingGoal = 0; c.numFunders = 0; c.deadline = 0; c.amount = 0; while (j <= n){ c.funders[j].addr.send(c.funders[j].amount); c.funders[j].addr = 0; c.funders[j].amount = 0; j++; } return true; } return false; } }

Like before let’s upload this contract to the blockchain in Alethzero. Make sure you have some ether and open up the ‘New Transaction’ window. Copy and paste the contracts code into the ‘Data’ box and you should see our contracts ABI in the compile box beneath. Copy and paste the function ID it into a text document for later and (making sure the ‘To’ address is left blank) hit execute.

Mine until your pending transaction disappears and you can see the contract in the contract’s pane. Now it is time to try out the contract – either create new accounts for yourself or use a dummy address for the recipient. Add a campaign using the newCampaign function – remember that we do this by sending a transaction with the 4 byte prefix for newCampaign ‘$0xdf175e9b’ followed by the arguments beneficiary, goal and deadline on separate lines.

You will then have to contribute to your campaign yourself and this will be a little different. Once again you will be sending a transaction with a data array to your contract but this time you will be passing it ether as well. ‘$0xc1cbbca7′ is the prefix for contribute and you only need to send it one argument – the campaign ID (this should be ‘0’).

You now have a choice either fully fund your campaign before triggering the end of the campaign using 

checkGoalReached or force mine a few more blocks until you the deadline has passed and the campaign has failed and the funds returned to the contributors. Whichever you choose you should see the campaign disappear from the contracts storage once completed.