❑ The deployed Ethereum smart contract(DApp) is immutable.
❑ Solidity file extension.: *.sol
❑ Version pragma: It is the starting source code of a DApp, and tells you the version of Solidity.
pragma solidity ^0.4.24;
❑ Contract: The basic components of the Ethereum DApp. All variables and functions are in the Contract.
Contract HelloWorld {
}
}
❑ State variables are permanently stored in Contract storage.
❑ exponential operator(x^2) : x ** 2
❑ Data type : int, uint(Unsigned integer) == uint256, uint8, uint16, uint32, struct, string, array, mapping, address.
❍ uint
- Generally, uint8 uses the same storage space as uint256.
- But, uint8 in a struct uses less space than uint256.
❍ Struct struct Person { uint age; string name; }
// The continuous uint32 is packed when it stored. : struct abc2 { uint32 a; uint32 b; }
// The spaced uint32 is packed when it stored : struct abc2 { uint32 a; uint c; uint32 b; }
// The continuous uint32 is packed when it stored. : struct abc2 { uint32 a; uint32 b; }
// The spaced uint32 is packed when it stored : struct abc2 { uint32 a; uint c; uint32 b; }
❍ Array uint[2] arr1; // A fixed array of uints.
string[5] arr2; // A fixed array of strings.
Person[] people // A dynamic array of Person structs.
uint[] arr3; // A dynamic array of uints.
uint[] memory values = new uint[](3); // Allocate memory.
values.push(1); // The push function returns the current length of the array.
values.push(2);
values.length // Length of the array.
string[5] arr2; // A fixed array of strings.
Person[] people // A dynamic array of Person structs.
uint[] arr3; // A dynamic array of uints.
uint[] memory values = new uint[](3); // Allocate memory.
values.push(1); // The push function returns the current length of the array.
values.push(2);
values.length // Length of the array.
❍ mapping : The data type of the "key => value" format.
// account => data.
mapping (address => uint) public favoriteNumber;
// The update of a mapping value.
favoriteNumber[msg.sender] = _Anumber;
// The call of a mapping value.
favoriteNumber[msg.sender]
mapping (address => uint) public favoriteNumber;
// The update of a mapping value.
favoriteNumber[msg.sender] = _Anumber;
// The call of a mapping value.
favoriteNumber[msg.sender]
❍ address: The datatype that stores the Ethernet address (0x~~~~~~~~~).
* The address refers to a specific user(Wallet) or smart contract.
❑ Function modifier: It is the syntax to control the function of a function.
* Generally, It is used to verify that the requirements are fulfilled before the function is executed.
* There are "Visibility modifier", "State modifier", "Custom modifier" etc.
* Several modifiers can be used together.
❑ Visibility modifier: It is the syntax that specifies where variables/functions can be called.
❍ Private [Variable | Function] : It is the Variables/functions that only functions in the Contract can call.
❍ Public [Variable | Function] : It is the Variables/functions that other Contract can call.
* If you do not specify a "visibility modifier", the Solidity function is declared public.
* When a public variable is declared, a public "getter()" function with the same name is created internally.
❍ Internal [Variable | Function] : It is "private modifier" but, It is also accessible from contracts that inherit the defined Contract.
❍ External [Variable | Function] : It is "public modifier" but, It is only accessible from outside the Contract.
// The dynamic array of Zombie structs. Other Contracts can use this array.
Zombie[] public zombies;
// The method of making a private function.
// The underbar character is used to mean a local variable/function. "_name" and "_dna" are local variables. "_createZombie" is a private function.
function _createZombie(string _name, uint _dna) private {}
Zombie[] public zombies;
// The method of making a private function.
// The underbar character is used to mean a local variable/function. "_name" and "_dna" are local variables. "_createZombie" is a private function.
function _createZombie(string _name, uint _dna) private {}
❑ State modifier: It is the syntax that specifies how the Solidity source code interacts with the blockchain.
* "external [View | Pure]" function does not consume gas because it does not access the block.
❍ View: The read-only function. It just read stored data.
* Query occurred. No transaction occurred.
❍ Pure: The function that calculates and returns a value using a parameter. The side-effect(Changing variable's value)does not occur.
// This function returns a uint value.
// This function is a view function.
function _generateRandomDna(string _str) private view returns (uint) {}
// This function is a view function.
function _generateRandomDna(string _str) private view returns (uint) {}
❑ Custom modifier: It is the user-defined syntax.
// When the "setLevelUpFee()" function is called, the "onlyOwner()" modifier is executed first.
// The "onlyOwner()" modifier allows only the contract owner for use the function.
modifier onlyOwner() {
require(msg.sender == owner);
_; // Return to the function that called modifier.
}
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
// Modifier can use parameters.
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) {
}
// The "onlyOwner()" modifier allows only the contract owner for use the function.
modifier onlyOwner() {
require(msg.sender == owner);
_; // Return to the function that called modifier.
}
function setLevelUpFee(uint _fee) external onlyOwner {
levelUpFee = _fee;
}
// Modifier can use parameters.
modifier aboveLevel(uint _level, uint _zombieId) {
require(zombies[_zombieId].level >= _level);
_;
}
function changeName(uint _zombieId, string _newName) external aboveLevel(2, _zombieId) {
}
❑ Payable modifier: It is the syntax that is used in ETH coin transfer.
* The ETH that is transferred to a Contract is kept in the Contract.
[Contract]
contract OnlineStore {
function buySomething() external payable {
// Checking that 0.001 ETH is transmitted from the client when executing the function.
// The data type of 0.001 ether is uint.
require(msg.value == 0.001 ether);
}
// The source code for ETH withdrawals stored in the contract.
function withdraw() external onlyOwner {
// this.balance : The total ETH balance stored in the contract.
owner.transfer(this.balance);
}
}
[Client]
// The client source code written in web3.js.
// You can only send "value" to the function for which the payable modifier is set.
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)});
contract OnlineStore {
function buySomething() external payable {
// Checking that 0.001 ETH is transmitted from the client when executing the function.
// The data type of 0.001 ether is uint.
require(msg.value == 0.001 ether);
}
// The source code for ETH withdrawals stored in the contract.
function withdraw() external onlyOwner {
// this.balance : The total ETH balance stored in the contract.
owner.transfer(this.balance);
}
}
[Client]
// The client source code written in web3.js.
// You can only send "value" to the function for which the payable modifier is set.
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)});
❑ keccak256(): It is the SHA3-based hash function that returns a hexadecimal value of 256 bits.
// Generating a SHA-3 value.
// 6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
// b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
// How to generate random numbers using hash values(0 to 99).
// Timestamp of current time(now), a msg.sender and the temporary value(nonce) are needed.
// Because a malicious node may try to write to a block only when the desired value comes out, this code is vulnerable.
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
// 6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256("aaaab");
// b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256("aaaac");
// How to generate random numbers using hash values(0 to 99).
// Timestamp of current time(now), a msg.sender and the temporary value(nonce) are needed.
// Because a malicious node may try to write to a block only when the desired value comes out, this code is vulnerable.
uint randNonce = 0;
uint random = uint(keccak256(now, msg.sender, randNonce)) % 100;
randNonce++;
uint random2 = uint(keccak256(now, msg.sender, randNonce)) % 100;
❑ Typecasting
uint8 a1 = 5;
uint a2 = 6;
// uint256 → uint8
uint8 a3 = uint8(a2);
uint a2 = 6;
// uint256 → uint8
uint8 a3 = uint8(a2);
❑ Fallback function : No-name function. It is the function that is called when calling a function that does not exist in the contract. The fallback function prevents the error caused by not executing.
function () {
}
}
❑ event: It is the syntax that occurs logs.
// The declaring the log to be generated.
event NewZombie(uint zombieId, string name, uint dna);
// Each time the "_createZombie()" function is called, logs of "id", "_name", "_dna" values are generated.
function _createZombie(string _name, uint _dna) private {
NewZombie(id, _name, _dna);
}
event NewZombie(uint zombieId, string name, uint dna);
// Each time the "_createZombie()" function is called, logs of "id", "_name", "_dna" values are generated.
function _createZombie(string _name, uint _dna) private {
NewZombie(id, _name, _dna);
}
❑ msg.value: The amount of ETH sent.
❑ msg.sender: The person or the smart contract that called the current function.
❑ require: It is the syntax to raise an error message and stop execution when the condition is not true.
// Checking that "_name"'s value is the same as "abc".
// Solidity has no default string comparing function.
require(keccak256(_name) == keccak256("abc"));
// If the above condition is true, the following source code will be executed:
return "Hi!";
// Solidity has no default string comparing function.
require(keccak256(_name) == keccak256("abc"));
// If the above condition is true, the following source code will be executed:
return "Hi!";
❑ Inheritance
contract Doge {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
// "BabyDoge" contract inherits "Doge" and "ERC721" contract.
contract BabyDoge is Doge, ERC721 {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
// "BabyDoge" contract inherits "Doge" and "ERC721" contract.
contract BabyDoge is Doge, ERC721 {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
❑ import : The syntax to load solidity files(*.sol).
import "./someothercontract.sol";
❑ Storage: The variable that is used to store a value permanently in the blockchain.
* High cost syntax.
* [Array].push() changes the size of the array.
❑ Memory: The variable that is used to store a value temporarily.
* Low cost syntax compared to storage.
* [Array].push() doesn't change the size of the array.
❑ Without storage/memory syntax, If the storage/memory syntax doesn't exist, state variables(the Variables declared outside functions) are stored in storage, and variables in functions are stored in memory.
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// "mySandwich" is a pointer pointing "sandwiches [_index]".
Sandwich storage mySandwich = sandwiches[_index];
// This changes the storage's "status" value permanently.
mySandwich.status = "Eaten!";
// "anotherSandwich" copies the value of "sandwiches [_index]".
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// This changes the "status" value of memory.
anotherSandwich.status = "Eaten!";
}
}
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// "mySandwich" is a pointer pointing "sandwiches [_index]".
Sandwich storage mySandwich = sandwiches[_index];
// This changes the storage's "status" value permanently.
mySandwich.status = "Eaten!";
// "anotherSandwich" copies the value of "sandwiches [_index]".
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// This changes the "status" value of memory.
anotherSandwich.status = "Eaten!";
}
}
❑ Interface: The syntax to use a contract that you do not own.
// The interface declaration has the same format as a contract.
contract KittyInterface {
// Copy the function from the name until the returns syntax and put a semicolon at the end.
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract test {
// The address of the smart contract to use.
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
// This initializes the interface.
KittyInterface kittyContract = KittyInterface(ckAddress);
// Now you can use the smart contract.
// This stores the last of the 10 returned values in "kittyDna".
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
}
contract KittyInterface {
// Copy the function from the name until the returns syntax and put a semicolon at the end.
function getKitty(uint256 _id) external view returns (
bool isGestating,
bool isReady,
uint256 cooldownIndex,
uint256 nextActionAt,
uint256 siringWithId,
uint256 birthTime,
uint256 matronId,
uint256 sireId,
uint256 generation,
uint256 genes
);
}
contract test {
// The address of the smart contract to use.
address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
// This initializes the interface.
KittyInterface kittyContract = KittyInterface(ckAddress);
// Now you can use the smart contract.
// This stores the last of the 10 returned values in "kittyDna".
uint kittyDna;
(,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
}
❑ Constructor: The function with the same name as the contract. It is executed once when the contract is created.
contract Ownable {
function Ownable() public {
owner = msg.sender;
}
}
function Ownable() public {
owner = msg.sender;
}
}
❑ Time(The value related in the time.)
/*
now // Returns UNIX timestamp(32 Bits uint).
seconds
minutes // Minute of the current time converted in seconds.
5 minutes // 300(60*5)
hours
1 hours // 3600(60*60)
days
weaks
years
*/
uint lastUpdated;
function updateTimestamp() public {
lastUpdated = now;
}
// It returns "true" if it has been 5 minutes since the "updateTimestamp" function was called.
function fiveMinutesHavePassed() public view returns (bool) {
return (now >= (lastUpdated + 5 minutes));
}
now // Returns UNIX timestamp(32 Bits uint).
seconds
minutes // Minute of the current time converted in seconds.
5 minutes // 300(60*5)
hours
1 hours // 3600(60*60)
days
weaks
years
*/
uint lastUpdated;
function updateTimestamp() public {
lastUpdated = now;
}
// It returns "true" if it has been 5 minutes since the "updateTimestamp" function was called.
function fiveMinutesHavePassed() public view returns (bool) {
return (now >= (lastUpdated + 5 minutes));
}
❑ For
for(uint i=0; i<10; i++) {
}
}
❑ If
if (var1 == var2) {
} else {
}
} else {
}
❑ ERC721 token
❍ function balanceOf(): It returns the number of tokens held by "_owner".
function balanceOf(address _owner) public view returns (uint256 _balance) {
return ownerZombieCount[_owner];
}
return ownerZombieCount[_owner];
}
❍ function ownerOf(): It returns the address that owns "_tokenId".
function ownerOf(uint256 _tokenId) public view returns (address _owner) {
return zombieToOwner[_tokenId];
}
return zombieToOwner[_tokenId];
}
❍ function transfer(): It transfers the token from "_from" address to "_to" address.
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
Transfer(_from, _to, _tokenId); // The event defined by ERC721.
}
// The modifier that checks access permission to a token.
modifier onlyOwnerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
_transfer(msg.sender, _to, _tokenId);
}
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
Transfer(_from, _to, _tokenId); // The event defined by ERC721.
}
// The modifier that checks access permission to a token.
modifier onlyOwnerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
_transfer(msg.sender, _to, _tokenId);
}
❍ function approve(): It approves tokens that is transferred.
function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _to;
Approval(msg.sender, _to, _tokenId); // The event defined by ERC721.
}
zombieApprovals[_tokenId] = _to;
Approval(msg.sender, _to, _tokenId); // The event defined by ERC721.
}
❍ function takeOwnership(): It calls "_transfer()" after ensuring that the "msg.sender" is approved to have a token.
function takeOwnership(uint256 _tokenId) public {
require(zombieApprovals[_tokenId] == msg.sender);
address owner = ownerOf(_tokenId);
// Since msg.sender calls this function, it becomes a recipient.
_transfer(owner, msg.sender, _tokenId);
}
require(zombieApprovals[_tokenId] == msg.sender);
address owner = ownerOf(_tokenId);
// Since msg.sender calls this function, it becomes a recipient.
_transfer(owner, msg.sender, _tokenId);
}
❑ The Library is a special kind of contract In Solidity.
❑ Library SafeMath
library SafeMath {
// Be careful not to input any values other than uint256(e.g. uint16, uint32 etc.).
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
// Check overflow..
// "require()" and "assert()" are equivalent in that they return an error when the condition is not satisfied.
// "require()" returns the remaining gas when the condition is not satisfied, but "assert()" doesn't return the remaining gas.
assert(c >= a);
return c;
}
}
contract abc {
// This applies the SafeMath library to the data type.
using SafeMath for uint;
uint a = 5;
// The variable "a" is entered as the first argument of "add()".
uint b = a.add(3); // 5 + 3 = 8
uint c = a.mul(2); // 5 * 2 = 10
}
// Be careful not to input any values other than uint256(e.g. uint16, uint32 etc.).
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
// Check overflow..
// "require()" and "assert()" are equivalent in that they return an error when the condition is not satisfied.
// "require()" returns the remaining gas when the condition is not satisfied, but "assert()" doesn't return the remaining gas.
assert(c >= a);
return c;
}
}
contract abc {
// This applies the SafeMath library to the data type.
using SafeMath for uint;
uint a = 5;
// The variable "a" is entered as the first argument of "add()".
uint b = a.add(3); // 5 + 3 = 8
uint c = a.mul(2); // 5 * 2 = 10
}
❑ Comment: Natspec comment is the standard comment format in Solidity.
❍ // @title: The title of a comment.
❍ // @autor: The author of a comment.
❍ // @notice: The description of a source code.
❍ // @dev: The description for developers.
❍ // @param: The parameter used by the function..
❍ // @return: The value returned by the function.