Ethereum Smart Contract Programming 101
Ethereum Smart Contract Programming 101
Link to Remix: https://remix.ethereum.org
Forum Discussion: https://forum.ivanontech.com/t/solidity-basics/9637
Hello World
Solidity Basics
Forum Discussion: https://forum.ivanontech.com/t/solidity-basics/9637
WorldHello.sol
contract HelloWorld{
// State Variables
string public message = "Hello World";
// Functions
function getMessage() public view returns(string memory){
return message;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
@filip Seems using quotes on text in set function is not required. When pressing message with and without
quotes result is the same.
Maybe it does. I can remember some time I had issues with that and I had to use quotes in remix. Maybe it has
changed. However, it’s a good habit to have to put quotes around your strings. In truffle you would have to, for
example.
Forum Discussion: https://forum.ivanontech.com/t/solidity-basics/9637
Mark As Complete
Array:
contract HelloWorld{
// State Variables
string public message = "Hello World";
uint public number = 123;
bool public isHappy = true;
address public contractCreator = 0xAFED31ee923Fea479708a2669E7C6777c55A08b3;
uint[] public numbers = [1, 20, 45];
uint[2] public fixArrayNumbers = [1, 2]; // Only two values size=2 and can't be modified
string[] public messages = ["hello", "world"];
// Functions
function getMessage() public view returns(string memory){
return message;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
function getNumber(uint index) public view returns(uint) {
return numbers[index];
}
function setNumber(uint newNumber, uint index) public {
numbers[index] = newNumber;
}
function addNumber(uint newNumber) public {
numbers.push(newNumber);
}
}
2/35
7/7/2020 Preview
Structs
struct per definire strutture.
contract HelloWorld{
struct Person {
uint id;
string name;
uint age;
uint height;
//address walletAddress;
}
Person[] public people;
function createPerson(string memory name, uint age, uint height) public {
// Add a Person to the people array
//people.push( Person(people.length, name, age, height) );
// Or you can do in this way
Person memory newPerson;
newPerson.id = people.length;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
people.push( newPerson );
}
}
Introduction to Mappings
Mapping = dictionary or key -> values storage
mapping in solidity:
3/35
7/7/2020 Preview
to assign:
balance[address] = 10
to get it:
balance[address]
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
//address walletAddress;
}
//Person[] public people;
mapping(address => Person) private people;
function createPerson(string memory name, uint age, uint height) public {
address creator = msg.sender;
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height );
}
}
4/35
7/7/2020 Preview
Your Answer
check_boxData records that can be quickly retrieved using a key that uniquely identifies the record
check_circle
How do I set the value 50, for the key Bob, in the mapping age?
Your Answer
check_boxage["Bob"] = 50
check_circle
Your Answer
check_boxNo
check_circle
Your Answer
check_boxNo
check_circle
Your Answer
newPerson.senior = false;
}
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
//Person[] public people;
mapping(address => Person) private people;
function createPerson(string memory name, uint age, uint height) public {
address creator = msg.sender;
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
}
6/35
7/7/2020 Preview
Introduction to Visibility
Visibility is a way to restrain access to functions and state variables.
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
//Person[] public people;
mapping(address => Person) private people;
function createPerson(string memory name, uint age, uint height) public {
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
7/35
7/7/2020 Preview
insertPerson(newPerson);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
}
Your Answer
check_circle
Your Answer
check_box"Internal" limits a functions visibility to the contract and all contracts that inherit from it.
check_circle
Your Answer
check_circle
For which visibility levels will solidity automatically create a getter function for us?
Your Answer
check_boxPublic
8/35
7/7/2020 Preview
Ethereum Gas
Solidity Basics
GAS:
With Gas there is an incentive to run a program (smart contract) and a disincentive to spam the network (the
sender pays)
Operations has to be done in the more efficient way and specially for storage that is the most expansive
ex:
require (inputnumber > 0)
require (msg.sender == owner) to make possible to do some operation only by the contract owner.
9/35
7/7/2020 Preview
Ex:
There is a funcion in a contract withdrawAll that transfer all coins to another address. At the end of a function the
balance should be 0 but if it is not the case (maybe for a programming error) with assert we throw an error
because our concept (account empty after transfering all coins) is not proved (condition not true)
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
address owner;
constructor() public {
owner = msg.sender;
}
//Person[] public people;
mapping(address => Person) private people;
address[] private creators;
function createPerson(string memory name, uint age, uint height) public {
require(age < 150, "age needs to be below 150!");
Person memory newPerson;
10/35
7/7/2020 Preview
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
insertPerson(newPerson);
creators.push(msg.sender);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
function deletePerson(address creator) public {
require(msg.sender == owner, "Caller needs to be owner");
delete people[creator];
}
function getCreator(uint index) public view returns(address) {
require(msg.sender == owner, "Caller needs to be owner");
return creators[index];
}
}
--------------------------
11/35
7/7/2020 Preview
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
address owner;
constructor() public {
owner = msg.sender;
}
//Person[] public people;
mapping(address => Person) private people;
address[] private creators;
function createPerson(string memory name, uint age, uint height) public {
require(age < 150, "age needs to be below 150!");
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
insertPerson(newPerson);
creators.push(msg.sender);
// Invariance
// people[msg.sender] == newPerson
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
12/35
7/7/2020 Preview
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
newPerson.senior
)
)
);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
function deletePerson(address creator) public {
require(msg.sender == owner, "Caller needs to be owner");
delete people[creator];
// Invariance
// people[creator].age == 0
assert( people[creator].age == 0 );
}
function getCreator(uint index) public view returns(address) {
require(msg.sender == owner, "Caller needs to be owner");
return creators[index];
}
}
13/35
7/7/2020 Preview
------------------------------
Your Answer
check_circle
Your Answer
check_circle
Which of the two cases will consume all of the remaining gas in a function call: assert(false) or require(false)?
Your Answer
check_boxassert(false)
----------------------------------------
Modifiers
Modifiers are a way to call a code before a function call . Like to call a require code before a function.
modifier onlyOwner() {
require(msg.sender == owner);
_; // Continue execution
}
14/35
7/7/2020 Preview
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
address owner;
modifier onlyOwner() {
require(msg.sender == owner);
_; // Continue execution
}
constructor() public {
owner = msg.sender;
}
//Person[] public people;
mapping(address => Person) private people;
address[] private creators;
function createPerson(string memory name, uint age, uint height) public {
require(age < 150, "age needs to be below 150!");
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
15/35
7/7/2020 Preview
}
insertPerson(newPerson);
creators.push(msg.sender);
// Invariance
// people[msg.sender] == newPerson
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
newPerson.senior
)
)
);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
function deletePerson(address creator) public onlyOwner {
//require(msg.sender == owner, "Caller needs to be owner");
delete people[creator];
// Invariance
// people[creator].age == 0
assert( people[creator].age == 0 );
16/35
7/7/2020 Preview
}
function getCreator(uint index) public view onlyOwner returns(address) {
//require(msg.sender == owner, "Caller needs to be owner");
return creators[index];
}
}
-------------------------------------------------------------------
Quiz: Modifiers
Your Answer
check_circle
Your Answer
------------------------------------------------------------------------------------
Data Location
Data location is where we tell solidity to save the data
There are three different data location:
- storage
data is stored permanently
ex:
address public owner;
mapping(address => Person) private people;
are stored permanently (until the contract is destroyed)
17/35
7/7/2020 Preview
- memory
data is only saved during a function execution
ex:
function createPerson(string memory name, uint age, uint height) public {
the var name will terminate to exists when the function createPerson is terminated.
- stack
stack is for hold local variabiles of value types
value types are integers, boolean values that are lower of 256bit in size
if inside a funcion I write:
uint score = 1
Is defaulted to stack
The function parameters are always defaulted to memory. But for more complex types like strings and struct and
arrays we need to specify their data location.
----------------------------------------
struct User{
uint id;
uint balance;
}
}
18/35
7/7/2020 Preview
Fixed with:
Hi. I had to change pragma solidity to 0.5.12 otherwise with google-chrome it won’t compile.
contract MemoryAndStorage {
struct User{
uint id;
uint balance;
}
modifier newUser(uint id) {
require( id > 0, "User id must be > 0" );
require( id != users[id].id, "User id is already used" );
_;
}
modifier userMustExist(uint id) {
require( id > 0, "User id must be > 0" );
require( id == users[id].id, "User doesn't exist" );
_;
}
function addUser(uint id, uint balance) public newUser(id) {
users[id] = User(id, balance);
}
---------------------------------------------------------
19/35
7/7/2020 Preview
Your Answer
check_boxMemory
check_boxStorage
check_boxStack
check_circle
Your Answer
check_boxNo
check_circle
Your Answer
---------------------------------------------
Events
Small notification you can send from your contract that get included in the blockchain, to notify people of
important events
So if a dapp is listening it can update the website with the "news" (like user added)
An event need first to be declared like this:
event personCreated(string name, bool senior);
Note there is no memory in name in this case is implicit (why??)
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
event personCreated(string name, bool senior);
event personDeleted(string name, bool senior, address deletedBy);
address owner;
modifier onlyOwner() {
require(msg.sender == owner);
_; // Continue execution
}
constructor() public {
owner = msg.sender;
}
//Person[] public people;
mapping(address => Person) private people;
address[] private creators;
function createPerson(string memory name, uint age, uint height) public {
21/35
7/7/2020 Preview
require(age < 150, "age needs to be below 150!");
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
insertPerson(newPerson);
creators.push(msg.sender);
// Invariance
// people[msg.sender] == newPerson
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
newPerson.senior
)
)
);
emit personCreated(newPerson.name, newPerson.senior);
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
22/35
7/7/2020 Preview
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
function deletePerson(address creator) public onlyOwner {
//require(msg.sender == owner, "Caller needs to be owner");
string memory name = people[creator].name;
bool senior = people[creator].senior;
delete people[creator];
// Invariance
// people[creator].age == 0
assert( people[creator].age == 0 );
emit personDeleted(name,senior,creator);
//emit personDeleted(name, senior, msg.sender);
}
function getCreator(uint index) public view onlyOwner returns(address) {
//require(msg.sender == owner, "Caller needs to be owner");
return creators[index];
}
}
[
{
"from": "0xd920953bb51535e6f1263c093ee34727039ee936",
"topic": "0xce505ea5e5a360ed968bacfdf5b52e168dfe627776422d678cb12143ebe187c8",
"event": "personDeleted",
"args": {
"0": "",
"1": false,
"2": "0xD920953bb51535e6f1263C093EE34727039eE936",
"name": "",
"senior": false,
"deletedBy": "0xD920953bb51535e6f1263C093EE34727039eE936",
"length": 3
}
}
]
23/35
7/7/2020 Preview
----------------------------------------------------
Quiz: Events
Your Answer
check_circle
Your Answer
------------------------------------
Receiving money
in smart contract we can manage money.
To tell solidity this we can say that a function can receive money adding the work payable
function createPerson(string memory name, uint age, uint height) public payable {
and inside the function check it with msg.value that contains the ethers (money)
for the amount we have to specify the unit of account (wei is default like satoshi in bitcoin) ether for ether.
wei, gwei, finney, ether
24/35
7/7/2020 Preview
contract Mapping{
struct Person {
//uint id;
string name;
uint age;
uint height;
bool senior;
//address walletAddress;
}
event personCreated(string name, bool senior);
event personDeleted(string name, bool senior, address deletedBy);
address owner;
modifier onlyOwner() {
require(msg.sender == owner);
_; // Continue execution
}
modifier costs(uint cost) {
require(msg.value >= cost);
_;
}
constructor() public {
owner = msg.sender;
}
//Person[] public people;
mapping(address => Person) private people;
address[] private creators;
uint private balance;
25/35
7/7/2020 Preview
function getBalance() public view onlyOwner returns(uint balanc) {
return balance;
}
function createPerson(string memory name, uint age, uint height) public payable costs(1 ether) {
require(age < 150, "age needs to be below 150!");
// Check if payment >= 1 ether
//require(msg.value >=1 ether);
Person memory newPerson;
newPerson.name = name;
newPerson.age = age;
newPerson.height = height;
if(age > 65) {
newPerson.senior = true;
} else {
newPerson.senior = false;
}
insertPerson(newPerson);
creators.push(msg.sender);
// Invariance
// people[msg.sender] == newPerson
assert(
keccak256(
abi.encodePacked(
people[msg.sender].name,
people[msg.sender].age,
people[msg.sender].height,
people[msg.sender].senior
)
)
==
keccak256(
abi.encodePacked(
newPerson.name,
newPerson.age,
newPerson.height,
26/35
7/7/2020 Preview
newPerson.senior
)
)
);
emit personCreated(newPerson.name, newPerson.senior);
balance += msg.value;
}
function insertPerson(Person memory newPerson) private {
address creator = msg.sender;
people[creator] = newPerson;
}
function getPerson() public view returns(string memory name, uint age, uint height, bool senior) {
return (people[msg.sender].name, people[msg.sender].age, people[msg.sender].height,
people[msg.sender].senior );
}
function deletePerson(address creator) public onlyOwner {
//require(msg.sender == owner, "Caller needs to be owner");
string memory name = people[creator].name;
bool senior = people[creator].senior;
delete people[creator];
// Invariance
// people[creator].age == 0
assert( people[creator].age == 0 );
emit personDeleted(name,senior,creator);
//emit personDeleted(name, senior, msg.sender);
}
function getCreator(uint index) public view onlyOwner returns(address) {
//require(msg.sender == owner, "Caller needs to be owner");
return creators[index];
}
}
-------------------------------
<address>.transfer(qta_to_transfer)
ex:
This is not safe to do for reentrance problem you will see it in security course in ethereum.
----------------------------------
28/35
7/7/2020 Preview
------------------------------
What keyword do we need to put in our function header in order to be able to receive money to that function?
Your Answer
check_boxpayable
check_circle
Your Answer
check_boxSo that our contract can query for its own balance
check_circle
Your Answer
check_circle
Your Answer
29/35
7/7/2020 Preview
check_circle
Your Answer
check_boxaddress(uint160(addressNonPayable))
------------------------------------------------------------
To learn the basics of inheritance in solidity, before we start to code, read through this blog post. The article goes
into quite deep detail, and unless you are curious, you can stop when you get to the section about
Polymorphism.
https://solidity.readthedocs.io/en/v0.5.3/contracts.html
Then answer the following questions. Post your answers in this forum thread.
----------------------------------
Inheritance in Solidity
import "./Ownable1.sol";
pragma solidity 0.5.12;
................
Inheritance Assignment
30/35
7/7/2020 Preview
import "Ownable.sol";
pragma solidity 0.5.12;
-----------------
contract Ownable {
address internal owner;
constructor() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_; // Continue execution
}
}
User keyword internal to make the owner var visibile in child contracts.
-----------------------
Quiz: Inheritance
Results Breakdown - 100%
check_circle
31/35
7/7/2020 Preview
Your Answer
check_boxcontract A is contract B
check_circle
Your Answer
check_circle
Your Answer
check_boxYes
check_circle
Your Answer
check_boxYes
check_circle
Contract A inherits from Contract B. Contract B has a public function called getBalance.
Your Answer
check_boxYes
check_circle
Your Answer
check_boxNo
32/35
7/7/2020 Preview
---------------------------------------------
External Contracts
user -> Conract A -> Contract B
Remeber msg.sender in called contract B will be the address of the caller A not the msg.sender (user) that is
using the contract A
contract ExternalContract is A
contract Mapping is B
// Inserface
contract Mapping {
// Insert only functions header
function createPerson(string memory name, uint age, uint height) public payable;
}
contract ExternalContract {
Mapping instance = Mapping(0x985B1a6fc1B41213BE06cD4DdA4B91f6Dc001f20);
function externalCreatePerson(string memory name, uint age, uint height) public payable {
// call createPerson in HelloWorld contract
// forward any ether to HelloWorld contract
// instance.createPerson(name,age,height); // This would be the straigh forward way but since it is
a payable function
// we have to send ether so:
instance.createPerson.value(msg.value)(name,age,height);
}
}
-----------------------------------------
33/35
7/7/2020 Preview
check_circle
What 2 things do we need in order for our contract to interact with other (external) contracts?
Your Answer
check_circle
Your Answer
check_boxBecause your contract needs to be aware of the function headers in order for you to interact
with the external contract.
check_circle
If you call a function in contract A, which calls a function in Contract B, what will msg.sender be in
Contract B's function?
Your Answer
------------------------------------------------------------------
Metamask setup
Deploying to the Testnet
Link to metamask: https://metamask.io
Link to faucet: https://faucet.metamask.io
Alternative: https://faucet.ropsten.be/
Forum Discussion: https://forum.ivanontech.com/t/deploying-to-testnet-discussion/9642
----------------------------------
34/35
7/7/2020 Preview
Forum Discussion: https://forum.ivanontech.com/t/deploying-to-testnet-discussion/9642
Remember to select in Remix in Environment the voice "Injected Web3" (from the original "Javascript VM") and
to put 100 wei as the cost of transaction createPerson
---------------------------------
This is the direct follow up course to this one. You will learn how to develop, test and deploy contracts using
Truffle and how to build dapps.
Learn the best security practices when building smart contracts and avoid the most common bugs and security
flaws. This is a must-take course for all looking to work as a professional developer.
All of these courses are available for our Premium Plan members. You can enroll in the Premium Plan and get
access to these courses by clicking here.
35/35