[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)