In this codelab, you will practice developing and deploying smart contracts of your own. Before starting, you will need an understanding of how to use Ethereum wallets and Remix work from previous codelabs. You may want to more deeply familiarize yourself with the types in Solidity. For this, I would recommend visiting the reference site at http://solidity.readthedocs.io/en/latest/types.html or viewing the associated screencast lectures available from class.

For our first smart contract, we will be creating a fundraiser platform similar to kickstarter, where the owner of the fundraiser sets a fundraising goal that must be met within a certain amount of time. If the fundraiser gets fully funded to a goal, the owner receives the funds, but if it does not reach the goal, then the contributors will be refunded their contributions.

State Variables

We will need to store a few things in state variables: the address of the owner of the fundraiser, the target goal for amount of funds to be raised, the time at which the fundraiser ends, and a list of contributors objects that include both the address and the contribution of each contributor.

pragma solidity ^0.4.24;
contract Fundraiser {
    address public owner;
    //target fundraising value
    uint public target;
    //time that fundraiser ends
    uint public endTime;
    //list of contributors
    Contributor[] contributors;
    
    struct Contributor{
        address userAddress;
        uint contribution;
    }

Contract Constructor

The constructor of contracts is executed when the contract is created and can take arguments for the contract. In our case, we want to set the owner's address, the target goal, and the duration of the Fundraiser.

  constructor(uint _target, uint duration) public payable {
     owner = msg.sender;
     target = _target;
     endTime = now + duration;
  }

Note that in Remix, to create a contract that takes in parameters, we must specify them when we "Deploy" it later.

Contribute function

You may be wondering where funds go, or how to "pay into" the smart contract. This is established by declaring a function "payable" as can be seen on the first line below. When a EOA sends funds to a smart contract they're authorizing it hold the money. In our case, that money will go to the owner of the contract if the goal is met. It's important to do logic checks here, do we want "now" to be greater than the the expiration of the contract? No! So we require that it is less than the last block's publication time ("now").

  function contribute() public payable {
     //require that fundraiser hasn't ended yet
     require(now < endTime);
     //add to list of contributors
     contributors.push(Contributor(msg.sender,msg.value));
  }

Collect Function

We need a function that the owner can call to receive the funds in the case that the fundraising goal is met. We can use the function "selfdestruct(address)" to destroy the contract and send the funds held by the contract to the given address.

  function collect() public{
     //once target has been reached, owner can collect funds
     require(address(this).balance >= target);
     require(msg.sender == owner);
     selfdestruct(owner);
  }

Refund Function

We also need a function that can be called to refund contributors in the case that the fundraiser ends without the goal being reached. This can be done using a for loop to iterate through each contributor and refund the amount that they contributed.

  function refund() public{
     //allow refunds once time has ended if goal hasn't been met
     require(now > endTime);
     require(address(this).balance < target);
     //refund all contributors
     for(uint i; i<contributors.length;i++) {
         contributors[i].userAddress.transfer(contributors[i].contribution);
     }
  }

Balance function

We'd like to check the funds that have been paid into this smart contract. To do so, use the keyword "this", cast to an address and refer to it's balance field.

  function balance() public view returns(uint){
    return address(this).balance;
  }
}

Test it out

In Remix, compile the contract. Then, switch to the tab for deploying and running transactions. Our contract's constructor takes in two parameters: a target value for the fundraiser and a duration. Expand out the "Deploy" section by clicking on the drop-down icon on the right.

Then, specify a target of 100 Wei and a duration of 1 hour (3600 seconds) as shown below:

Click on "transact", confirm the transaction in Metamask, and wait for the contract to be deployed and the contract appears below with its address in Remix. Click on the left button to expand out the details of the deployed contract, including its functions.

Test out the fundraiser.

Now, try an unsuccessful fundraiser

Now we will implement a piggy bank smart contract for you to write your own Solidity code.

Template source code

Below we have outlined the basic structure of the code, but we have replaced some of the code with ellipses (...). The functionality desired is as follows: Given a duration passed to the constructor, accept funds into the contract via the deposit function before the endTime. Calculate the endTime given duration. Do not allow any deposits outside of that timeframe. Implement a function to withdraw the funds once endTime has been surpassed.

pragma solidity ^0.4.24;

contract TimedPiggyBank {
    //owner of piggybank
    address public owner;
    //time that funds can be withdrawn
    uint public endTime;
    
    constructor(uint duration) public {
        //store message sender as piggybank owner
        ...
        //set endTime
        ...
    }
    
    //payable function that can be used to deposit funds
    //leaving this blank will allow users to send funds without running any code
    function deposit() public payable {}
    
    function withdraw() public {
        //make sure the message sender is the owner
        ...
        //make sure that the current time is after endTime
        ...
        //send funds to the owner and destroy the contract
        ...
    }
    
    function balance() public view returns(uint){
       return address(this).balance;
    }
}

Test it out

In Remix, compile and deploy the contract. Then, after creating a piggy bank:

Include screenshots of the final set of contract transactions via Etherscan for your lab notebook. Transactions should include the initial deployment, the incremental deposit of funds, and the multiple attempts to withdraw funds.

We will now implement an escrow account smart contract. If you're unfamiliar with escrow accounts, they hold funds involved in a financial transaction until a third party determines that the terms of the contract are met, at which point the receiver of funds will be paid. The payee is an individual paying into the contract, the payer is waiting for some completed goal. The agent is a trusted third party that determines if the terms of the contract has been completed. The payer will either be refunded in the case that the terms are not completed by a certain expiration time, or the payee will be paid if the agent determines that the terms are completed. Find another classmate or use two accounts to test the functionality of your contract.

Template source code

Below is a template of the Escrow contract. As before, fill in the missing parts to implement your contract.

pragma solidity ^0.4.24;

contract Escrow {
    //creator of contract and sender of funds
    address payer;
    //receiver of funds, party that must finish terms
    address payee;
    //third party that determines when terms have been met
    address agent;
    //time after which sender can void the contract
    uint expirationTime;
    
    constructor(address _payee, address _agent, uint timeBeforeExpiration) public payable {
        payee = _payee;
        agent = _agent;
        payer = msg.sender;
        expirationTime = now + timeBeforeExpiration;
    }
    
    function voidContract() public {
        //make sure the message sender is the payer
        ...
        //make sure the contract has expired
        ...
        //destroy contract and send funds to payer
        ...
    }
    
    function confirmCompletion() public {
        //make sure agent is message sender
        ...
        //destroy contract and send funds to payee
        ...
    }
    
    function balance() public view returns(uint){
       return address(this).balance;
    }
}

Test it out

The Escrow contract requires multiple accounts to show how it works. For this part, ask a classmate to help you demonstrate that your code works. You may also create an additional wallet in Metamask and switch between them in order to show the contract works.

In Remix, compile and deploy the contract. Then, with multiple accounts or with a partner's account:

Include screenshots of the final set of contract transactions via Etherscan to include in your lab notebook.

Commit your code as

Within each directory, also create a file called contract_address.txt that has the address of your deployed contract

You've written and deployed your own smart contract code. Celebrate (or not).