#HowToBUIDL (2/n) :: Your First Smart Contract

in #ethereum6 years ago

Come One Come All! By the end of this post, you’ll write your very first SmartContract called the GreatestShow.

Come one! Come all! Come join the great Digital Railroad! There is no barrier to entry, as long as you participate within the framework of rules known as decentralized consensus. This means you’ll work for it. It won’t come easy but at least you’ve taken the 1st step. Come build the digital railroads of tomorrow! Three meals a day! Roof over your head! Fair and ample wages! Come, make your fortune! Not everyone will be up for the challenge. I guess I’ll leave that up to you. Opportunity awaits you.

If you haven’t, be sure to catch BUIDL :: Dev Environment Setup before reading.

railroads.png

Ethereum Blockchain lets us build the Digital Railroads of Tomorrow

Right here, Right now. I put the offer out…

In the 1st article in the #HowToBUIDL Series —  #BUIDL :: Dev Environment Setup we covered how to get your computer’s environment ready for this step. Maybe you’re still skeptical, or worry you don’t have the skills. You’re trapped in your own mind and just haven’t been shown the way.

So trade that typical, for something colorful. And if it’s crazy, live a little crazy. You can play it sensible, King of Conventional, or you can risk it all and see…

bar.png

At the heart of this general purpose Ethereum Blockchain lies a consensus framework that allows anyone to participate. The network is kept secure by an incentivized network, where miners are rewarded with cryptographically secure money called Ether. A Peer-to-Peer network of nodes keeps the network robust and allows people to send money anywhere around the world with no regard for national borders and negligible fees.

Decentralized platforms have benefits of massively reducing costs and barriers to entry, remove single points of failure, prevent censorship and ensure transparency and trust between all parties involved in an interaction. But there’s more to this space than just money transfer. Smart contracts really facilitate this idea of decentralization by removing intermediaries.

We have ways of representing any unique object as a ‘token’ and we can change the way we think of ownership. If you own the right to transfer data, you can own the right to transfer that which the data represents. In the future we may see digital transference of a land ownership, real estate titles, financing agreements and the right to use limited resources in the form of app Utility Tokens, or even good old fashioned Tickets redeemable for a show.

It’s not the largest entities that tend to change the status quo, but the smallest, often minority voices that are the progressives of change. In my opinion, empowerment comes from inclusion, and that’s exactly what blockchain type projects stand for. Nobody wants to be excluded from a marketplace, and a centralized one has the potential to exclude. Blockchain technologies offer a way to #BUIDL the digital railroads that connect people in a way never seen before.

shots.jpeg

We’re going to the other side.

Let’s Get Started.

Smart Contracts are constructs that hold onto the state of data, and provide a way for the outside world to interact with that data. The code represents the rules. Perhaps “smart” is a misnomer. The more complex the rules in the contract, the harder it is to follow, and the harder it is to create code that is bug-free. In fact, the more simple you make a contract, the more easily you can verify it to ensure the code does what it says is going to do. Once deployed to the Ethereum network, the code is unstoppable, so it’s worth grasping what can be stored in a contract. Although we are using the term “contract” but currently, code is not something held up and enforced in court. The rules must be defined in code and specifically written to enforce the logic embedded in the contract.

Every contract must start by describing the minimum version of Solidity, the language used to write smart contracts for Ethereum. Versions 0.4.22 and below are forbidden, https://github.com/ethereum/solc-js/releases contains information on each recent release.

Start by creating a new directory called ComeOneComeAll and fire up truffle.

mkdir ComeOneComeAll
cd ComeOneComeAll
truffle init
cd contracts/
npm install zeppelin-solidity

With your favorite editor of choice create a new file called GreatestShow.sol
For larger projects you might want to check out an editor called VSCode, but I think the Freemium editor called Sublime is fine for our purposes.

pragma solidity ^0.4.23;

Next, start the contract by giving it a name. Let’s build the GreatestShow. When we go to deploy the GreatestShow contract, the constructor will be called. Let’s use this opportunity to pass in some information and record some data that gets sent to us for free. We’ll hold onto the Ethereum address of the person that deployed it, and also make note of the ringmaster. Text can be represented as a string or as bytes depending on your storage and cost needs. Every operation in a contract, including storing data costs a little bit of money, that we refer to as ‘gas’. Let’s keep it simple for now and use a string.

contract GreatestShow {
    address public owner public; 
    string public ringMaster;
    constructor(string _ringMaster) {
        owner = msg.sender;
    }
}

If we were to deploy this contract, we would do something like the following in a migrations/2_deploy_contracts.sol file:

const GreatestShow = artifacts.require("./GreatestShow.sol");

module.exports = (deployer, network, accounts) => {
    deployer.deploy( GreatestShow, "P.T. Barnum" );
};

And from the command line, we would run:

truffle migrate

greatestShowman.jpeg

Great! We have a show. That’s technically all we need for a smart contract. As is, the code would compile and could be deployed to the Ethereum network. The problem is, that’s not much of a show if all you have is a ringmaster. There’s not even any attendees, actors or animals accounted for at the moment. Before we call our show a massive success, we better keep going.

Next, we’re going to add another property to the GreatestShow in order to make note of the total number of available seats. The type of variable required to store a number that is positive is an “unsigned integer” — denoted with uint. Again, for now just know it’s unsigned because it can’t be negative. This number can store a number that is as large as 2 to the 256 (giant number!) so in the future it would be better to be practical about storage and only allow for numbers that pertain to reality. We’ll add a function that lets the creator of the contract store the number of available seats.

One thing we didn’t touch on was the ‘public’ accessibility modifier for a variable. For purposes of data security, assume that all data stored in a contract is publicly available even when it is not marked so. That’s part of the beauty of adhering to the principles of being public, open and censorship resistant. When a variable is explicitly marked public, it is given a handy accessor function available to callers of the function by default. Since we’re not specifying that here, it’s worth demonstrating how you would implement a setter/getter pair in the body of the contract. The setter takes in a parameter that we assign to the state of the contract, and the getter notes that it returns(<type>). A Solidity function can return more than one value, but in this case it only makes sense to return one. Since the getter makes no changes to the state of the data, you should mark it as view only by convention.

    uint availableSeats;
    function setAvailableSeats( uint _availableSeats ) public {
        availableSeats = _availableSeats;
    }
    function getAvailableSeats() view public returns(uint) {
        return availableSeats;
    }

seats-and-ringmaster.png

Awesome. We’ve got some seats and we’re ready to entertain!

Right. Now we’ve got a Ringmaster and Seats, but nobody’s going to be entertained until we’ve got a full set of performances to entertain them.

Now let’s add some performances to the show. A performance can be given a name, a number of actors in the performance, number of tigers, bears and elephants required for the act, and the amount of time that will be allotted. In addition, it might be useful to make note that clearing the stage is required for the performance to take place. This will let the producers of the show know that some special prep is required in order to handle the performance.

All programming languages attempt to model reality by giving the programmers ways of defining objects through some sort of data structure. In Solidity, the most common way of doing this is by use of the struct — where user defined types can be defined. In the body of the contract, let’s define a Performance structure.

    struct Performance {
        string name;
        uint32 actors;
        uint8 tigers;
        uint8 bears;
        uint8 elephants;
        uint16 timeInSeconds;
        bool clearTheStageFirst;
    }

The creator of the contract will now need a way to add a Performance to the show by passing in the required values to a function. Storing this data in the contract has a cost; however, we’re not covering that right now. Right now we’re just demonstrating the need to keep track of it. So that means we’ll have to keep track of an array of Performance objects, denoted Performance[] performances. We’ll append to the array each time the addPerformance function is called.

Performance[] performances;
    
  function addPerformance( 
              string _name,
              uint32 _actors,                              
              uint8 _tigers, 
              uint8 _bears,
              uint8 _elephants, 
              uint16 _timeInSeconds,
              bool _clearTheStageFirst ) public {
      performances.push( 
          Performance( { name: _name, actors: _actors, 
              tigers: _tigers, bears: _bears, 
              elephants: _elephants,
              timeInSeconds: _timeInSeconds,
              clearTheStageFirst: _clearTheStageFirst } 
          ) 
      );
  }

We’re about to get ahead of ourselves for a second, but hopefully this will help clear things up. After the contract is deployed to the network, the owner of the contract might obtain the instance of the deployed contract using code intruffle console that looks similar to this.

// Add a performance of Knife Throwing requiring 2 people. 
// The performance will last 3 minutes and require zero animals.
GreatestShow.deployed().then( function(show) {
    show.addPerformance( "Knife Thrower", 2, 0, 0, 0, 180, false );
} );

knifethrower-spin.png

// Add a performance of 4 Flame Breathers lasting 30 seconds
GreatestShow.deployed().then( function(show) {
    show.addPerformance( "Flame Breather", 4, 0, 0, 0, 30, false );
} );

flamethrower-4.png

Arrays in Solidity, as in most languages, are zero-indexed. To get the first performance inside the contract code, you could reference performances[0]. But what if you wanted to access all the data for a particular performance as the caller of the contract? Imagine that a user of your contract will want to list information on some web app. We’ll add a handy method to get at all the data for a particular Performance instance at the _index passed in.

Note again that the data is not changing, but also note that we cannot directly return the Performance object. Solidity limitations require that the data must be returned as multiple values as indicated in the returns statement. We wrap all of the return values by ( parenthesis ).

function getPerformance( uint _index ) view public 
      returns (string, uint32, uint8, uint8, uint8, uint16, bool) {
    Performance storage perf = performances[_index];
    return (perf.name, perf.actors, perf.tigers, perf.bears, 
            perf.elephants, perf.timeInSeconds, 
            perf.clearTheStageFirst); 
  }

Imagine the third entry is Zendaya’s performance as Anne Wheeler the flying trapeze artist. She’s a one woman show, but needs a set of 8 other supporting actors to help catch and throw her around as she performs various acrobatic feats. and the stage needs to be clear for the equipment. An example of how to get some info from that performance:

GreatestShow.deployed().then( function(show) {
   let performance = show.getPerformance( 2 ); // 0-indexed
   console.log( "Name:" + performance.name );     // Flying Trapeze
   console.log( "Actors:" + performance.actors ); // 9 = 1 + 8
   console.log( "Clear:" + performance.clearTheStageFirst); // true
});

trapeze.png

Zendaya as Anne Wheeler the Flying Trapeze Artist and 8 supporting Actors

We’re almost done but we need a way to collect money for the people buying tickets! Luckily in Ethereum there is a way to create a function that accepts Ether, the native cryptocurrency representing digital money. Ether has a fluctuating exchange rate versus normal fiat currencies like the US Dollar or British Pound, but we’re not going to worry about that for the sake of argument. No, instead we are only going to worry about accepting Ether in exchange for a certain number of seats.

We haven’t discussed the mapping data type yet, but essentially it allows us to map one value to another value. Here we will demonstrate keeping track of ethereum addresses and their count of tickets in an unsigned integer.

mapping(address => uint256) internal tickets;

An easy way to accept Ether is to mark a fallback function as payable and do the logic inside that function to assign the function caller’s Ethereum address a number of tickets per unit cost. A simple method might look like this:

uint256 internal ethusd = 700; // peg to 700 dollars
uint256 internal rateInCents = 2000; // 2000 cents = $20.00
function () payable public {
   uint256 _wei = msg.value; // 1 Ether = 1000000000000000000 wei
   uint256 tickets4ETH = _wei.mul(ethusd).mul(100).div(rateInCents);
   tickets[msg.sender] = tickets[msg.sender].add( tickets4ETH );
}

Ignore for a moment that the ETHUSD rate is constantly changing in reality. We’re representing a pegged value of $700 per ether, and a ticket price of $20.00. There are some limitations in Solidity that do not allow for floating point numbers to be stored. Knowing that we have to deal with this limitation, we want to make sure that we have more places available to perform arithmetic so that we don’t lose as much precision as possible when multiplying or dividing so we first multiply by 100 before dividing the result by the rateInCents for each ticket. We are also relying on the SafeMath library for mul, div, and add as well as sub. We’re not getting into the reasons for that right now, but be aware that any arithmetic operation in a smart contract can have undesirable effects, and sometimes catastrophic data integrity issues — so we always check for overflow when performing math operations.

Back at the console, install zeppelin-solidity:

npm install zeppelin-solidity

And at the top of your file after pragma line & before contractGreatestShow:

import 'zeppelin-solidity/contracts/math/SafeMath.sol';

Then, inside the actual contract the first line should read:

using SafeMath for uint256; // adds helper functions to uint256

Ok. We’re ready to proceed! You have created your GreatestShow contract, you have a way of specifying the name of your ringMaster, we’re keeping track of the performances data structures represented by the Performance struct, and holding onto the number of availableSeats. To make sure that we can fill those seats, we make sure the payable fallback function is available so that when a customer sends us Ether from their account, we assign a value of tickets based on the amount they sent us, to their msg.sender address.

Let’s head back to the console and get ready for the magic to happen:

truffle compile

Compiling ./contracts/GreatestShow.sol...
Compiling zeppelin-solidity/contracts/math/SafeMath.sol...
Writing artifacts to ./build/contracts

FULL Disclosure: We purposely left a couple bugs in the application, and we’ll address them in the next episode, where we look at how to test our smart contract. Just because code compiles doesn’t mean it is logically correct; it just means that the code is syntactically correct. Testing is imperative when it comes to smart contracts. In past, developers could simply stop the server, revert the change in a database they control, and patch a bug fix. But we are in a new paradigm. The code that we deploy to a blockchain is immutable.

You made it!

Congratulations! You now know how to #BUIDL your first Ethereum Blockchain based Smart Contract using Solidity and the Truffle Framework!

Forget the cage, ’cause we know how to make the key.
Oh! Damn, suddenly you’re free to fly. I’ll take you to the other side.

otherside-standing.png

If you got stuck, have no fear. I have a copy of the full, completed project for you to download from a public GitHub repository where you can compare your code versus the working example I’ve checked in.

Greatest Showman, 2017

FULL CODE EXAMPLE

NOTE: We will be making several code edits (refactoring) in future articles.
This article’s purpose was to get you excited and spark your imagination for what’s possible. It’s not necessarily possible to cover every aspect of good or professional programming techniques and code style in one post, and we don’t want to overwhelm you on your first attempt. Thanks for coming along so far. Keep moving forward!

pragma solidity ^0.4.23;
import 'zeppelin-solidity/contracts/math/SafeMath.sol';
contract GreatestShow {
  
  address public owner;
  string public ringMaster;
  constructor(string _ringMaster) public {
    owner = msg.sender;
    ringMaster = _ringMaster;
  }
  uint availableSeats;
  function setAvailableSeats( uint _availableSeats ) public {
      availableSeats = _availableSeats;
  }
  
  function getAvailableSeats() view public returns(uint) {
      return availableSeats;
  }
  struct Performance {
    string name;
    uint32 actors;
    uint8 tigers;
    uint8 bears;
    uint8 elephants;
    uint16 timeInSeconds;
    bool clearTheStageFirst;
  }
  Performance[] performances;
    
  function addPerformance( 
              string _name,
              uint32 _actors,                              
              uint8 _tigers, 
              uint8 _bears,
              uint8 _elephants, 
              uint16 _timeInSeconds,
              bool _clearTheStageFirst ) public returns(uint) {
      performances.push( 
          Performance( { name: _name, actors: _actors, 
              tigers: _tigers, bears: _bears, 
              elephants: _elephants,
              timeInSeconds: _timeInSeconds,
              clearTheStageFirst: _clearTheStageFirst } 
          ) 
      );
      return performances.length;
  }
 
  function getPerformance( uint _index ) view public 
      returns (string, uint32, uint8, uint8, uint8, uint16, bool) {
      Performance storage perf = performances[_index];
      return (perf.name, perf.actors, perf.tigers, perf.bears, 
       perf.elephants, perf.timeInSeconds, perf.clearTheStageFirst);
  }
mapping(address => uint256) internal tickets;
uint256 internal ethusd = 700; // peg to 700 dollars
uint256 internal rateInCents = 2000; // 2000 cents = $20.00
using SafeMath for uint256;
  function () payable public {     
    uint256 _wei = msg.value; // 1 Ether = 1000000000000000000
    uint256 tickets4ETH =_wei.mul(ethusd).mul(100).div(rateInCents);
    tickets[msg.sender] = tickets[msg.sender].add( tickets4ETH );     
  }
      
}

Coin Marketplace

STEEM 0.19
TRX 0.16
JST 0.033
BTC 63927.21
ETH 2754.83
USDT 1.00
SBD 2.65