[TOC]
# Solidity (0.8.4)
# Link
## 1. [Remix (Online IDE)](http://remix.ethereum.org/)
## 2. [MetaMask](https://metamask.io/)
## 3. [Goeril TestNet](https://goerli.etherscan.io/)
- [Goerli 水龍頭 (發錢用)](https://faucet.goerli.mudit.blog/)
# Remix Env Settings
- Deploy & run transaction -> Environment -> Injected Web3 (Connect with MetaMask)
# Solidity 合約佈署
- Deploy & run transaction
1. 按下 Deploy 佈署合約
# Simple Solidity code
- SPDX 幫助減少軟體的衝突
```=Solidity
// SPDX-License-Identifier: GPL-3.0
```
- 定義版本範圍
```=Solidity
pragma solidity >=0.7.0 <0.9.0;
```
```=Solidity
/**
* @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;
}
}
```
# Solidity Explanation
## 1. Visibility (可視性)
*public, private, external, internal*
- public
- 被宣告為 public 的 function 將成為 ABI 的一部分,可被大家看見
- **能**被 internal call (來自自己的合約呼叫)
- **能**被**外部**呼叫 message call
- public 宣告 state variables 將產生 getter function
- private
- 不會出現在 ABI 裡面
- 只能被定義的 contract 內成員呼叫
- **不能**被**外部**呼叫 message call
- 外部不能互動,不代表不能被看見。數值仍然可被外部觀察者看見
- external
- 被宣告為 external 的 function 將成為 ABI 的一部分,可被大家看見
- **不能**被 internal call (來自自己的合約呼叫)
- function f() external {}
- f() -> 失敗
- this.f() -> 成功,通過 message call 呼叫
- **能**被 [**外部的合約**] 或 [**Transaction**] 呼叫
- 處理[大陣列]的時候很有效率,external function 不會做 memory copy 的操作
- internal
- 被宣告 internal 的 function 或 state variables 只能被定義的合約 (defined contract) 或者派生合約 (derived contract) 呼叫
## 2. State Mutability (變異性)
*pure, view, default*
| | pure | view | default |
| --- | ---- | ------------------ | ------------------ |
| 讀 | :x: | :heavy_check_mark: | :heavy_check_mark: |
| 寫 | :x: | :x: | :heavy_check_mark: |
## 3. 如何宣告 function
- 回傳值
```=solidity
function name(args) visibility mutability returns(return variables) {
...
}
```
- 不回傳值
```=solidity
function name(args) visibility mutability {
...
}
```
## 4. Getter Function
- 當自己定義
```=solidity
public int a;
```
- Solidity 將自動產生 function
```=solidity
function a() public view returns (int) {
return a;
}
```
## 5. Constructor
- 建構子,為 contract 被建立時自動被執行的 function
- 通常在建構子裡設定資料的初始值
- 不宣告,Solidity會自動插入空的 constructor
```=solidity
constructor() {}
```
- 建構 constructor
```=solidity
constructor(args) {
...
}
```
- 解構子
```=solidity
selfdestruct()
```
## 6. Receive Function
- 接收 ether
- 一個 contract 只能有一個 receive function
```=solidity
receive() external payable {
...
}
```
- send(), transfer() 會觸發這個 function
- EOA message call
- 若 receive 不存在,尋找 fallback function
## 7. Fallback Function
- 一個 contract 只能有一個 fallback function
- 收 ether
```=solidity
fallback() external payable
```
- 不收 ether
```=solidity
fallback() external
```
- 若沒有 payable,收到 ether 會觸發 exception 並且退回 ether
- fallback 可能是無意見觸發的,因此 fallback function 內最好減少 gas 的消耗,盡量少於 2300 gass
- 以下這些行為可能大於 2300 gas,fallback 中盡量不要使用:
- Modify storage
- Create a contract
- Call an external function
- Send Ethers
### Example (Ether simulator)
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract etherSim {
mapping(address => uint256) public balances;
address payable wallet;
constructor(address payable _wallet) {
wallet = _wallet;
}
// Any call with non-empty calldata to this contract will execute
// the fallback function (even if Ether is sent along with the call).
fallback() external payable {
}
// This function is called for plain Ether transfers, i.e.
// for every call with empty calldata
receive() external payable {
buyToken();
}
function buyToken() public payable {
// buy a buyToken
balances[msg.sender] += 1;
// send ether to the wallet
wallet.transfer(msg.value);
}
}
```
- CALLDATA 如果**沒有**值,執行 **receive function**
- CALLDATA 如果**有**值,執行 **fallback function**
## 8. Event
- 監聽事件
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract etherSim {
mapping(address => uint256) public balances;
address payable wallet;
event Purchase (
address indexed _buyer,
uint256 _amount
);
constructor(address payable _wallet) {
wallet = _wallet;
}
receive() external payable {
buyToken();
}
function buyToken() public payable {
// buy a buyToken
balances[msg.sender] += 1;
// send ether to the wallet
wallet.transfer(msg.value);
emit Purchase(msg.sender, 1);
}
}
```
- logs
```=json
[
{
"from": "0x3328358128832A260C76A4141e19E2A943CD4B6D",
"topic": "0x2499a5330ab0979cc612135e7883ebc3cd5c9f7a8508f042540c34723348f632",
"event": "Purchase",
"args": {
"0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
"1": "1",
"_buyer": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
"_amount": "1"
}
}
]
```
# Example 1 (簡易計算機)
```=Solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract Calc {
int private result;
function add(int a, int b) public returns(int c) {
result = a + b;
c = result;
}
function minus(int a, int b) public returns(int) {
result = a - b;
return result;
}
function mul(int a, int b) public returns(int) {
result = a * b;
return result;
}
function div(int a, int b) public returns(int) {
result = a / b;
return result;
}
function getResult() public view returns(int) {
return result;
}
}
```
# Example 2 (小豬撲滿)
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract PiggyBank {
uint public goal;
constructor(uint myGoal) {
goal = myGoal;
}
// 收取 ether
receive() external payable {}
function getMyBalance() public view returns(uint) {
return address(this).balance;
}
function withdraw() public {
if (getMyBalance() > goal) {
// 銷毀合約
selfdestruct(payable(msg.sender));
}
}
}
```
# Value Type
## 1. Boolean
- true or false
- ! && || == !=
## 2. Integer
- int (int256) | 以太輸出最大 256 bit
- uint (uint256)
- (u)intX, X $\in$ 8 的倍數, where X <= 256
- Type 資訊
- type(T).min
- type(T).max
## 3. Fixed-size Byte Arrays
- bytesX where X $\in$ $[1,32]$
- byte = bytes1
- bytes[k] 讀取締 k 個 byte
## 4. Literals
- Address Literal: 0xD05Ebe8E7BB56450d34784996Db6D22909E035Af
# Enum
- enum讓使用者可以自定義變數而不需要指定型態
- example
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract MyContract {
enum State {Waiting, Ready, Active}
State public state;
constructor() {
state = State.Waiting;
}
function active() public {
state = State.Active;
}
function isActive() public view returns(bool) {
return state == State.Active;
}
}
```
# Storage Area
## 1. storage
- 永久儲存在區塊練中,存儲是一個 key/value 庫
- 每個 account 都會有 storage data area
- 存在在 function 和 transactions 之中
- 讀取成本相對較高,初始化和修改存儲的成本更高
- 佔用一個256位的槽需要消耗20000 gas
- 修改一個已經使用的存儲槽的值,需要消耗5000 gas
## 2. memory
- 臨時儲存
- 合約調用完成即被移除
- 讀或寫一個內存槽都會消耗 3 gas
- 為了避免礦工的工作量過大,memory 使用越大越耗費 gas fee
- **calldata**
- **stack**
# Example 3 (People List)
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract myContract {
Person[] public people;
uint256 public peopleCount;
struct Person {
string _firstName;
string _lastName;
}
function addPerson(string memory _firstName, string memory _lastName) public {
people.push(Person(_firstName, _lastName));
peopleCount += 1;
}
}
```
使用 [Mapping](https://docs.soliditylang.org/en/v0.8.4/types.html?highlight=mapping#mapping-types)
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract MyContract {
uint256 public peopleCount;
mapping(uint => Person) public people;
struct Person {
uint _id;
string _firstName;
string _lastName;
}
function addPerson(string memory _firstName, string memory _lastName) public {
peopleCount += 1;
people[peopleCount] = Person(peopleCount, _firstName, _lastName);
}
}
```
## Modifier example
只讓 owner 調用 addPerson 函數
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract MyContract {
uint256 public peopleCount;
mapping(uint => Person) public people;
address owner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
struct Person {
uint _id;
string _firstName;
string _lastName;
}
constructor() {
// msg.sender is the account deploy this contract
owner = msg.sender;
}
function addPerson(
string memory _firstName,
string memory _lastName
)
public
onlyOwner
{
incrementCount();
people[peopleCount] = Person(peopleCount, _firstName, _lastName);
}
function incrementCount() internal {
peopleCount += 1;
}
}
```
## Timer example
設定過了 openingTime (epoch time) 才能執行 addPerson
```=solidity
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract MyContract {
uint256 public peopleCount;
mapping(uint => Person) public people;
uint256 openingTime = 1624039337;
modifier onlyWhileOpen() {
require(block.timestamp >= openingTime);
_;
}
struct Person {
uint _id;
string _firstName;
string _lastName;
}
function addPerson(
string memory _firstName,
string memory _lastName
)
public
onlyWhileOpen
{
incrementCount();
people[peopleCount] = Person(peopleCount, _firstName, _lastName);
}
function incrementCount() internal {
peopleCount += 1;
}
}
```
# Multiple Smart Contract
# Solidity Security
- [原文](https://blog.sigmaprime.io/solidity-security.html)
- [中文](https://paper.seebug.org/632/#%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%E7%9A%84%E4%BE%8B%E5%AD%90dao)