# First Smart Contract

In lab01, we learned how to run a local geth node and make simple transactions that sends Ether to an account or call methods.

In this tutorial, we will learn the basic concept of smart contracts that run on Ethereum. There are many different ways (opens new window) to learn how to write a smart contract, but we will introduce two popular development tools, namely Remix (opens new window) and Brownie (opens new window). Remix (opens new window) is a simple web-based IDE, which provides an easy, quick way to learn the concept of smart contracts using Solidity (opens new window), a popular language to write an Ethereum smart contract. Brownie (opens new window) is a Python-based framework that provides full-fledged development features like deployment and testing.

You can complete Lab02 by using only Remix but we would like to start using Brownie and Brownie-like frameworks such as Hardhat (opens new window) and Truffle (opens new window). These framework embeds basic "Web3" libraries like Web3.js (opens new window), Ether.js (opens new window) or Web3.py (opens new window) as their foundation along with own local nodes (opens new window) and testing tools for your convenience.

# Remix

We will use the hosted version of Remix (opens new window).

Remix

Let's take a look at 1_Storage.sol (under "contracts" / "default_workspace").

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage {
    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256) {
        return number;
    }
}

The basic elements of Solidity will be introduced in the lecture and we will use this as a running example to deploy, interact and debug smart contracts in Remix. The Storage contract provides a store(uint256) and retrieve() API method that stores and retrieve a number from the blockchain.

If you click the green triangle on the top (or press Ctrl+S), Remix compiles the current contract and makes it ready to deploy. It works like any IDE that you are familiar with, so take some time and tinker with it for a while: e.g., make it to spit some compilation errors. Most features should be pretty intuitive.

1_Storage.sol

Remix compiles the contract with solc (opens new window) with a version specified in the contract (e.g., pragma solidity >=0.7.0 <0.9.0) and prepares a binary blob (or artifact) for deployment.

Deployment

The "Environment" tab shows various Ethereum providers, which help you interact with the Ethereum network, like geth nodes, MetaMask, or test nodes provided by each framework. The "Remix VM (London)" under the tab shows a local EVM for the London fork (opens new window), and helps you test your contract without connecting to an external provider.

Let's click "Deploy" to deploy the compiled "Storage" to the "Remix VM".

Deployment

Once deployed, the console shows the basic information about the transaction and you can interact with via store and retrieve buttons under the "Deployed Contracts". Under the hood, Remix reads the ABI generated by solc, and populates the UI accordingly.

You can now store and retrieve a number to/from the local EVM! Once you test the contract, please deploy it to the ledger by simply changing the provider: either "Injected Provider" (MetaMask) or "External Http Provider" (geth). It is also a great time to explore a few examples in here (opens new window)!

To know more about Remix, please refer to these Remix documents: Deploy & Run (opens new window) Deploy & Run (part 2) (opens new window).

Task

Your job is to write a Fibonacci contract that calculates a Fibonacci number (opens new window). The contract should implement the function below:

function calculate(uint number) external returns (uint);

Deploy the Fibonacci contract and submit it via (in the dev console):

contract.submitFibonacci(address)

# Brownie

First, install Brownie by following this tutorial (opens new window), and then check out its Quickstart (opens new window)!

You can find some tools (sctips/pwn.py) to interact with our labs:

$ git pull
$ ./setup.sh

$ brownie run scripts/pwn.py -I
...
>>> help()
           load_abi(name): Load ABI/json of the label instance
              load_main(): Load the Main contract
          load_gamedata(): Fetch the gamedata from the website
   get_instance_abi(name): Get the name of the instance's ABI
          new_level(name): Create a new level instance
         load_level(name): Load an existing instance
    submit_level(address): Submit the level instance for grading
    set_default_account(): Set a default account

 e.g., lab02 = new_level("lab02")
       submit_level(lab02.address)

Note that -I at the end of the command spawns an interactive console after executing the script.

Task

Do you remember you transferred some ether to the Lab01 instance? Let's withdraw the ether back to your account.

>>> lab01 = load_level("lab01")
>>> lab01
<lab01 Contract '0xcDC3E4D34f0A36D47b4E7FdF3d3384B500cFf6e7'>

>>> lab01.balance()
2000000000

>>> lab01.withdraw()
Transaction sent: 0xfd6a07e3295dd9caedfaa78911f941dd1b3ed8c7cacbfd357a16038d41b295a2
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 620
  Transaction confirmed   Block: 627   Gas used: 22816 (0.19%)
<Transaction '0xfd6a07e3295dd9caedfaa78911f941dd1b3ed8c7cacbfd357a16038d41b295a2'>

>>> lab01.balance()
0

You can submit the lab01 instance like below in Brownie:

>>> lab02 = load_level("lab02")
>>> lab02
<lab02 Contract '0x6c52BBd5767f4c4780B3301483f6F9FcE94B94CE'>

>>> lab02.completed2()
False

>>> lab02.submitLab01(lab01.address)
Transaction sent: 0x0226b6b6215a0bbd34c5048e49699ffc998c55398a8aa7b66fc8df23339f85ae
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 625
  Transaction confirmed   Block: 632   Gas used: 28351 (0.24%)
<Transaction '0x0226b6b6215a0bbd34c5048e49699ffc998c55398a8aa7b66fc8df23339f85ae'>

>>> lab02.completed2()
True

You can script your tasks, of course: simply, put your python code under scripts/ and import pwn.py (i.e., from scripts.pwn import *) in your script.

Task

Similar to lab01, transfer "1 gwei" to the lab2 instance in Brownie!

>>> lab02.balance()
0

>>> tx = a[0].transfer(lab02.address, "1 gwei")
Transaction sent: 0xe1c82323275c5674d07c7c1393d5b22e25395fda5e5239de7a49c12004527631
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 627
  Transaction confirmed   Block: 634   Gas used: 21055 (0.18%)
<Transaction '0xe1c82323275c5674d07c7c1393d5b22e25395fda5e5239de7a49c12004527631'>

>>> tx.info()
Transaction was Mined
---------------------
Tx Hash: 0xe1c82323275c5674d07c7c1393d5b22e25395fda5e5239de7a49c12004527631
From: 0xfed61D4a212A14143DC51f21d8D6617072e7a621
To: 0x6c52BBd5767f4c4780B3301483f6F9FcE94B94CE
Value: 1000000000
Block: 634
Gas Used: 21055 / 12000000 (0.2%)

>>> lab02.balance()
1000000000

In Brownie, you can simply put a solidity contract under /contracts/ for compilation and deployment. For example, the Storage contract (/contracts/Storage.sol) will be compiled and loaded to the console when launched. If you'd like to deploy the Storage contract, simply try Storage.deploy()!

Task

The last task is to write a simple contract that invokes submitMagicNumber() with 0xdeadbeef as an augment along with 1 gwei.

interface Lab02 {
    function submitMagicNumber(uint _magic) payable external;
}

...

function exploit(address addr) public payable {
   Lab02(addr).submitMagicNumber{value: 1 gwei}(0xdeadbeef);
}

If you are successfully done, you can invoke lab02.completed() to see if it is indeed completed! It's a time to submit your instance for grade. In the Brownie console, submit_level(lab02.address) will submit your instance!