Solidity


Solidity Compiler

converts object-oriented, high-level language to implement contract templates into simple binaries, assembly and various other related metadata

pragma solidity

pragma solidity >=0.4.0 <0.7.0; // only compatible with
// solidity compiler bigger or equal to v0.4.0
// but smaller than v0.7.0

(see all compiler versions)


Simple Coin Contract Template


Contract Template

define State Variables

Are like a single slot in a database.

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address X; // declares a state variable X which can store
  // an address of an external (="wallet") or contract **account**.
  // Other accounts have no access to this state variable
}

X is of type address (=160-bit value that does not allow any arithmetic operations).


Contract Template

define Functions

  • set and get view as part of public interface
  • functions can take arguments and can return values
pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address account;
  // takes one argument `x` of type `address`
  function set (address x) public { account = x; }
  // `view` guarantees function to not change state
  // (=read only of state)
  // returns a value of type `address`
  function get () public view returns (address) { return account; }
}

these functions can be used by other acounts to set/modify/alter or get/query/retrieve a value of a state variable.


Contract Template

Access Modifier

public keyword auto-generates a read access function for other accounts

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public X; // behaves like:
  // function X() public view returns (address) { return X; }
}

Contract Template

mapping data type

map key of type1 to value of type2 for later lookup

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public minter;
  mapping (address => uint) balances;
  // type `uint` (=unsigned integer of 256 bits)
}
// lookup `balances[minter]` defaults to value `0` of type `uint`
// assign `balances[minter] = 123`
// => to map `minter` address to uint `123`
// lookup `balances[minter]` now returns `123` of type `uint`

All keys by default map to all zeros byte-representation values and listing all is not possible


Contract Template

mapping data type

map key of type1 to value of type2 for later lookup

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public minter;
  mapping (address => uint) public balances;
  // function balances(address X) external view returns (uint) {
  //   return balances[X];
  // }
}

Using public on a mapping auto-generates a more complex read access function for other accounts to query the balance of a single account

  • e.g. balances(minter) => 123

Contract Template

global variables

// sender of the message (current call)
msg.sender (address payable)

// MORE:
// number of wei sent with the message
msg.value (uint)
msg.data (bytes calldata) // complete calldata
// first four bytes of the calldata (i.e. function identifier)
msg.sig (bytes4)
// current block timestamp (alias for block.timestamp)
now (uint)
gasleft() returns (uint256) // remaining gas
// gas price of the transaction
tx.gasprice (uint)
// sender of the transaction (full call chain)
tx.origin (address payable)
// hash of one of the most recent 256 blocks, but not current
blockhash(uint blockNumber) returns (bytes32)
block.coinbase (address payable) // current block miner’s address
block.difficulty (uint) // current block difficulty
block.gaslimit (uint) // current block gaslimit
block.number (uint) // current block number
// current block timestamp as seconds since unix epoch
block.timestamp (uint)

Contract Template

constructor

Constructor function runs only once when the contract is published

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public minter; // will store address of publisher
  mapping (address => uint) public balances;
  // executes ONLY once at contract creation time
  constructor () public {
    minter = msg.sender; // `msg.sender` always refers to the
    // account address which sent the current transaction
  }
}

Contract Template

declare events

declares an event which can be emitted and allows clients (e.g. dapps) to react

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public minter;
  mapping (address => uint) public balances;
  constructor() public { minter = msg.sender; }
  event Sent(address from, address to, uint amount);
}

Contract Template

emit events

Event listeners receive emitted arguments

pragma solidity >=0.4.0 <0.7.0;
contract Coin {
  address public minter;
  mapping (address => uint) public balances;
  constructor() public { minter = msg.sender; }
  event Sent(address from, address to, uint amount);
  // Sends an amount of existing coins
  // from any caller to an address
  function send (address receiver, uint amount) public {
    balances[msg.sender] -= amount;
    // short for: balances[msg.sender] = balances[msg.sender] - amount;
    balances[receiver] += amount;
    // short for: balances[receiver] = balances[receiver] + amount;
    emit Sent(msg.sender, receiver, amount);
    // arguments (`from`,`to`,`amount`) to track transactions
  }
  // Sends an amount of newly created coins to an address
  // Can only be called by the contract creator
  function mint (address receiver, uint amount) public {
    balances[receiver] += amount;
    // short for: balances[receiver] = balances[receiver] + amount;
  }
}

Dapp

event listening

// ethers
// abi
// bytecode
var currentProvider = window.web3.currentProvider
var provider = new ethers.providers.Web3Provider(currentProvider)
var contractAddress = ''  // paste contract's address
var dapp = new ethers.Contract(contractAddress, abi, provider)

async function getEvent () {
    dapp.on("Sent", (from, to, event) => {
        console.log(`From (address): ${from}`)
        console.log(`To (address): ${to}`)
        console.log(`Event: ${event}`)
    })
}

getEvent()



Dapp

calling functions

// ...
const receiver = '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'
const amount = 500
async function dapp (contract) {
  await contract.mint(receiver, amount)
  const currentValue = await contract.balances(receiver)
  console.log(currentValue) // => 500
}

Contract Template

require guard

If "1st argument" of require is false,
execution ends and all state and ether balance changes are reverted.

pragma solidity >=0.4.0 <0.7.0;

contract Coin {
  
  address public minter;
  mapping (address => uint) public balances;
  constructor() public { minter = msg.sender; }
  event Sent(address from, address to, uint amount);
  
  function send (address receiver, uint amount) public {
    // use `require` to check if functions are called as intended.
    // A 2nd argument can provide explanations about whats wrong.
    require(amount <= balances[msg.sender], "Not enough funds.");
    // guard: can be used by anyone who has the amount of tokens to send
    balances[msg.sender] -= amount;
    balances[receiver] += amount;
    emit Sent(msg.sender, receiver, amount);
  }
  
  function mint (address receiver, uint amount) public {
    require(msg.sender == minter);
    // guard: ensures only the contract publisher can call mint
    require(amount < 1e60);
    // guard: prevents overflow errors in the future
    balances[receiver] += amount; // assign tokens to address
  }
  
}

Demo Time

copy to open in play-ed

pragma solidity >=0.4.0 <0.7.0;

contract Coin {

  address public minter;
  mapping (address => uint) public balances;
  constructor() public { minter = msg.sender; }
  event Sent(address from, address to, uint amount);

  function send (address receiver, uint amount) public {
    require(amount <= balances[msg.sender], "Not enough funds.");
    balances[msg.sender] -= amount;
    balances[receiver] += amount;
    emit Sent(msg.sender, receiver, amount);
  }
  
  function mint (address receiver, uint amount) public {
    require(msg.sender == minter);
    require(amount < 1e60);
    balances[receiver] += amount;
  }
  
}

Simple Ballot Contract Template


Contract Template

struct type & bool type & NatSpec

pragma solidity >=0.4.22 <0.7.0;
// NatSpec uses `///` to include informative comments in compiler output
// see: https://solidity.readthedocs.io/en/latest/natspec-format.html#tags

/// @title Transparent voting with automated vote counting.
contract Ballot { // 1 contract per ballot with option short-names
  address public chairperson;
  // `struct` declares a new complex type to be used for variables later
  struct Voter { // represents a single voter.
    uint weight; // weight 0 prevents from voting
    bool voted;  // if true, that person already voted
    uint vote;   // index of the voted proposal
  }
}

bool can only store one of two values at a time (true or false)


Contract Template

bytes32 type

pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  address public chairperson;
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { // This is a struct type for a single proposal.
    bytes32 name;   // short name (up to 32 bytes) of text
    uint voteCount; // (=unit256) number of accumulated votes
  }
}
ethers.utils.formatBytes32String('More flowers on the streets')
ethers.utils.parseBytes32String('0x4d616b652062696767657220726f616473000000000000000000000000000000')

Contract Template

"struct array" type

pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }

  address public chairperson;
  // Declares state variable to store `Voter` struct for address.
  mapping(address => Voter) public voters;
  // A dynamically-sized array (="list") of `Proposal` structs.
  Proposal[] public proposals;
}

Contract Template

Reference Types

  • memory keyword used for "reference types"
pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }

  address public chairperson;
  mapping(address => Voter) public voters;
  Proposal[] public proposals;
  // Makes new `Ballot` contract to choose one of `proposalNames`.
  constructor (bytes32[] memory proposalNames) public {
    // ...
  }
}

Contract Template

for loop, "struct object" and array.push

pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }

  address public chairperson;
  mapping(address => Voter) public voters;
  Proposal[] public proposals;
  constructor (bytes32[] memory proposalNames) public {
    chairperson = msg.sender; // creator is chairperson
    voters[chairperson].weight = 1;
    // For each of provided proposal names,
    // create new proposal object to add to end of array.
    for (uint i = 0; i < proposalNames.length; i++) {
      // `Proposal({...})` creates a temporary Proposal object
      // and `proposals.push(...)` appends it to end of `proposals`.
      proposals.push(Proposal({
        name: proposalNames[i],
        voteCount: 0
      }));
    }
  }
}

Contract Template

Reference Types

pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }
  address public chairperson;
  mapping(address => Voter) public voters;
  Proposal[] public proposals;
  constructor (bytes32[] memory proposalNames) public {
    // ...
  }
  /// Give `voter` the right to vote on this ballot.
  // May only be called by `chairperson`.
  function giveRightToVote (address voter) public {
    require(msg.sender == chairperson, "Only chairperson adds voters.");
    require(!voters[voter].voted, "The voter already voted.");
    require(voters[voter].weight == 0);
    voters[voter].weight = 1;
  }
  /// Give your vote to proposal `proposals[proposal].name`.
  function vote (uint proposal) public {
    Voter storage sender = voters[msg.sender];
    require(sender.weight != 0, "Has no right to vote");
    require(!sender.voted, "Already voted.");
    sender.voted = true;
    sender.vote = proposal;
    // If `proposal` is out of the range of the array,
    // this will throw automatically and revert all
    // changes.
    proposals[proposal].voteCount += sender.weight;
  }
}

Contract Template

"named returns" and "function re-use"

pragma solidity >=0.4.22 <0.7.0;
contract Ballot {
  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }
  address public chairperson;
  mapping(address => Voter) public voters;
  Proposal[] public proposals;
  constructor (bytes32[] memory proposalNames) public {
    // ...
  }
  function giveRightToVote (address voter) public {
    // ...
  }
  function vote (uint proposal) public {
    // ...
  }
  /// @dev Computes winning proposal from all previous votes.
  /// @return proposal with most votes.
  function winningProposal () public view returns (uint winningProposal_) {
    uint winningVoteCount = 0;
    for (uint p = 0; p < proposals.length; p++) {
      if (proposals[p].voteCount > winningVoteCount) {
        winningVoteCount = proposals[p].voteCount;
        winningProposal_ = p;
      }
    }
    // returns `winningProposal_` at the end as the named return value
  }
  // Calls winningProposal() function to get the index
  // of the winner contained in the proposals array and then
  /// @return the name of the winning proposal
  function winnerName() public view returns (bytes32 winnerName_) {
    // re-uses the `winningProposal()` function to calculate result
    winnerName_ = proposals[winningProposal()].name;
    // returns `winnerName_` at the end as the named return value
  }
}

Demo Time

copy to open in play-ed

ethers.utils.formatBytes32String('More flowers on the streets')
ethers.utils.parseBytes32String('0x4d616b652062696767657220726f616473000000000000000000000000000000')
pragma solidity >=0.4.22 <0.7.0;

contract Ballot {

  struct Voter { uint weight; bool voted; uint vote; }
  struct Proposal { bytes32 name; uint voteCount; }
  address public chairperson;
  mapping(address => Voter) public voters;
  Proposal[] public proposals;
  
  constructor (bytes32[] memory proposalNames) public {
    chairperson = msg.sender;
    voters[chairperson].weight = 1;
    for (uint i = 0; i < proposalNames.length; i++) {
      proposals.push(Proposal({
        name: proposalNames[i],
        voteCount: 0
      }));
    }
  }
  
  function giveRightToVote (address voter) public {
    require(msg.sender == chairperson, "Only chairperson adds voters.");
    require(!voters[voter].voted, "The voter already voted.");
    require(voters[voter].weight == 0);
    voters[voter].weight = 1;
  }
  
  function vote (uint proposal) public {
    Voter storage sender = voters[msg.sender];
    require(sender.weight != 0, "Has no right to vote");
    require(!sender.voted, "Already voted.");
    sender.voted = true;
    sender.vote = proposal;
    proposals[proposal].voteCount += sender.weight;
  }
  
  function winningProposal () public view returns (uint winningProposal_) {
    uint winningVoteCount = 0;
    for (uint p = 0; p < proposals.length; p++) {
      if (proposals[p].voteCount > winningVoteCount) {
        winningVoteCount = proposals[p].voteCount;
        winningProposal_ = p;
      }
    }
  }
  
  function winnerName() public view returns (bytes32 winnerName_) {
    winnerName_ = proposals[winningProposal()].name;
  }
  
}

Thank you

questions?

connect with us

twitter/telegram

chat

Select a repo