# Solidity8_perfect **Repository Path**: rayest1108/solidity8_perfect ## Basic Information - **Project Name**: Solidity8_perfect - **Description**: Solidity 8.0 精通教程 - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 4 - **Created**: 2022-09-07 - **Last Updated**: 2022-09-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Solidity 8.0 perfect ## 1. 环境部署 部署开发环境,及本地的发布测试环境 ### 1.1 开发环境 本教程使用 vscode ,在插件市场中下载 Ethereum Remix 插件,自动安装所需插件。编译版本选择 0.8.7及以上 ### 1.2 发布测试环境 下载安装 Ganache 选择 quickstart 部署本地发布测试环境 ## 2. HelloWorld - 代码部分 创建文件夹 solidity8_perfect,在根目录创建 HelloWorld.sol 文件 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; contract HelloWorld { string public myString = "hello world!"; } ``` - 发布 (compile) - 点击 Run & Deploy 先连接本地测试环境,获取 Ganache 中RPC Server 地址,默认 http://127.0.0.1:7545 - 发布 输出显示成功 ``` bash [4:51:24 PM]: Loading remote version v0.8.7+commit.e28d00a7... please wait [4:51:35 PM]: Remote version v0.8.7+commit.e28d00a7 loaded. [4:51:36 PM]: Compilation finished for token/solidity/solidity8_perfect/HelloWorld.sol with solidity version 0.8.7+commit.e28d00a7.Emscripten.clang. ``` - 部署 (deploy) - ABI 其中生成合约的 ABI,可复制用于其他 Dapp 项目 - 合约地址:Deployed Contranct Address - CALL 输出内容: ``` bash [5:00:11 PM]: Calling method 'myString' with [] from 0xa1A59E6D9B69D50aaF7ead540f5bE888B6ee47b3 at contract address 0x64342db66D48fd4e7923828608aa3ee77BFc76e9 [5:00:11 PM]: "hello world!" ``` ## 3. 变量类型 bool, uint, int, address, bytes32 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 类型 */ contract Types { bool public b = true; // uint = uint256 0 to 2**256 -1 // uint8 0 to 2**8 - 1 // uint16 0 to 2**16 - 1 uint public u = 123; // int = int256 -2**255 to 2**255 -1 int public i = -123; int public minInt = type(int).min; int public maxInt = type(int).max; address public addr = 0x08655Ac0d18E0a77C04cdec8bd53A38a925d27f6; bytes32 public b32 = 0x657468657265756d000000000000000000000000000000000000000000000000; } ``` ## 4. 函数 关键字 - pure 纯函数,不能够读写状态变量,只能拥有局部变量 - view 能读写状态变量 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 函数 */ contract Functions { // external : 外部函数 // pure : 纯函数,不能够读写状态变量,只能拥有局部变量 function add(uint x, uint y) external pure returns (uint) { return x + y; } function sub(uint x, uint y) external pure returns (uint) { return x - y; } } ``` ## 5. 变量 - 状态变量:写入区块链的变量数据 ``` solidity uint public myUint = 123; uint public i; bool public b; address public myAddress; ``` - 局部变量 ``` solidity function foo() external { uint x = 123; bool f = false; x += 456; f = true; // 修改上文中的状态变量的值 i = 123; b = true; myAddress = address(1); } ``` - 全局变量 ``` solidity function globalVars() external view returns (address, uint, uint) { address sender = msg.sender; uint timestamp = block.timestamp; uint blockNum = block.number; return (sender, timestamp, blockNum); } ``` ## 6. 只读函数 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 只读函数 */ contract VuewAndPureFunctions06 { uint public num; // view 函数可以读取链上的信息 function ViewFunc() external view returns (uint) { return num; } // pure 不能读取链上信息 function PureFunc() external pure returns (uint) { return 1; } function AddToNum(uint x) external view returns (uint) { return num + x; } function Add(uint x, uint y) external pure returns (uint) { return x + y; } } ``` ## 7. 计数器合约 对一个状态变量进行增加、减少的操作,编译部署后测试。 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 计数器合约 * 对状态变量进行增加、减少操作 */ contract Counter { uint public count; function inc() external { count += 1; } function dec() external { count -= 1; } } ``` ## 8. 默认值 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 默认值 */ contract DefaultValues { bool public b; // default: false uint public u; // default: 0 int public i; // default: 0 address public a; // default: 0x0000000000000000000000000000000000000000 bytes32 public b32; // default: 0x0000000000000000000000000000000000000000000000000000000000000000 // mapping, structs, enums, fixed sized arrays } ``` 编译部署后,运行 ``` bash [2:17:10 PM]: Calling method 'b32' with [] from 0x821251076b7B613AEF850A84a79977C4d7C2E329 at contract address 0xDED404CE158A48a8A0cFBA66eEA67327aFf69D9b [2:17:10 PM]: "0x0000000000000000000000000000000000000000000000000000000000000000" [2:17:35 PM]: Network is a local or custom network! [2:17:35 PM]: Calling method 'a' with [] from 0x821251076b7B613AEF850A84a79977C4d7C2E329 at contract address 0xDED404CE158A48a8A0cFBA66eEA67327aFf69D9b [2:17:35 PM]: "0x0000000000000000000000000000000000000000" [2:17:58 PM]: Network is a local or custom network! [2:17:58 PM]: Calling method 'i' with [] from 0x821251076b7B613AEF850A84a79977C4d7C2E329 at contract address 0xDED404CE158A48a8A0cFBA66eEA67327aFf69D9b [2:17:58 PM]: "0" [2:18:01 PM]: Network is a local or custom network! [2:18:01 PM]: Calling method 'u' with [] from 0x821251076b7B613AEF850A84a79977C4d7C2E329 at contract address 0xDED404CE158A48a8A0cFBA66eEA67327aFf69D9b [2:18:02 PM]: "0" [2:18:11 PM]: Network is a local or custom network! [2:18:11 PM]: Calling method 'b' with [] from 0x821251076b7B613AEF850A84a79977C4d7C2E329 at contract address 0xDED404CE158A48a8A0cFBA66eEA67327aFf69D9b [2:18:11 PM]: false ``` ## 9. 常量 使用常量消耗gas比变量少 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 常量 * 读取常量消耗的gas会比较小,编译部署,运行下面合约看看调用address分别消耗多少gas(详细需要部署到页面端Remix查看) */ contract Constants { address public constant MY_ADDRESS = 0x08655Ac0d18E0a77C04cdec8bd53A38a925d27f6; uint public constant MY_UINT = 123; } contract NormalVar { address public NorAddress = 0x08655Ac0d18E0a77C04cdec8bd53A38a925d27f6; } ``` ## 10. 结构控制 使用 if else 进行结构控制,或者使用三元运算符 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 结构控制 */ contract IfElse { function example (uint x) external pure returns (uint) { if(x < 10) { return 1; } else if (x < 20) { return 2; } else { return 3; } } function ternary(uint x) external pure returns (uint) { // 三目运算 return x < 10 ? 1 : 2; } } ``` ## 11. 循环 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 循环 */ contract ForAndWhileLoops { function forLoops () external pure { for(uint i = 0; i < 10; i++) { // code ... if(i == 3) { // 跳过之后的代码 continue; } // more code ... if(i == 5) { // 跳出循环 break; } } } function whileLoops() external pure { uint j = 0; while(j < 10) { j++; } } // s 加 n 次 function sum(uint n) external pure returns (uint) { uint s; for(uint i = 1; i <= n; i++) { s += i; } return s; } } ``` ## 12. 报错控制 报错用法 - require; - revert; - assert 自定义报错,节省gas - custom error ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * require, revert, assert * - gas refund, state updates are reverted * custom error * - save gas */ contract Error { function testErquire(uint i) public pure { // 需要 i <= 10 才能执行后面代码,否则报错 i > 10 require(i <= 10, "i > 10"); // code } function testRevert(uint i) public pure { if(i > 10) { // 直接抛出错误信息 revert("i > 10"); } } uint public num = 123; function testAssert() public view { // 断言,成功则执行后面代码,否则报错 assert(num == 123); } error MyError(adress caller, uint i); function testCustomError(uint i) public view { if(i> 10) { revert MyError(msg.sender, i); } } } ``` ## 13. 函数修改器 复用代码,注意代码执行顺序。 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * Function modifier * - reuse code before and / or after function * Basic, inputs, sandwich */ contract FunctionModifier { bool public paused; uint public count; function setPause(bool _paused) external { paused = _paused; } // ------ 原始函数 ------ function inc() external { require(!paused, "paused"); count += 1; } function dec() external { require(!paused, "paused"); count -= 1; } // ------ end ------- // ------ 使用函数修改器 ------ modifier whenNotPaused() { require(!paused, "paused"); _; } function incNew() external whenNotPaused { count += 1; } function decNew() external whenNotPaused { count -= 1; } // ------ end ------- // ----- inputs:带参数的函数修改器-原始函数 ------ function incBy(uint _x) external whenNotPaused { require(_x < 100, "x >= 100"); count += _x; } // ------ end ------- // ----- inputs:带参数的函数修改器 ------ modifier cap(uint _x) { // 在修改器中检查 require(_x < 100, "x >= 100"); _; } function incByNew(uint _x) external whenNotPaused cap(_x) { count += _x; } // ------ end ------- // ------ sandwich ------ modifier sandwich() { // code here count += 10; _; // more code here count *= 2; } // 代码运行顺序 // 0: 先运行sandwich修改器中 _ 以上的代码 // 1: 运行foo函数中的代码 // 2: 运行sandwich修改器中 _ 以下的代码 function foo() external sandwich { count += 1; } // ------ end ------- } ``` ## 14. 构造函数 构造函数:只在合约部署的时候调用一次,之后不再调用 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * Constructor * - 构造函数:只在合约部署的时候调用一次,之后不再调用 */ contract Constructor { address public owner; uint public x; constructor(uint _x) { // 让 owner 是合约的部署者 owner = msg.sender; // 用户输入 x 值 x = _x; } } ``` ## 15. Ownable 权限管理合约 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * Ownable 权限管理合约 * 重新设置合约拥有者 * 只有合约拥有者能调用的函数 * 其他人可以调用的函数 */ contract Ownable { address public owner; constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "not owner"); _; } // 设置新的所有者 function setNewOwner(address _newOwner) external onlyOwner { // 不可以是 0 地址 require(_newOwner != address(0), "address invalid"); owner = _newOwner; } function onlyOwnerCanCallThisFunc() external onlyOwner { } function anyOneCanCallThisFunc() external { } } ``` ## 16. 函数返回值 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 函数返回值 */ contract FunctionOutputs { function returnManay() public pure returns (uint, bool) { return (1, true); } function named() public pure returns (uint x, bool b) { return (1, true); } // 隐式返回 function namedAssigned() public pure returns (uint x, bool b) { x = 1; b = false; } // 获取返回值 function destructingAssigments() public pure { (uint x, bool b) = returnManay(); // 只取一个返回值 (, bool c) = returnManay(); x = x + 1; if(b) { x = x * 1; } if(c) { x = x * 2; } } } ``` ## 17. 数组 dynamic or fixed size 动态数组或者固定长度数组 * Initialization * Insert(push), get, update, delete, pop, length * Creating array in memory * Returning array from function ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 数组 * - dynamic or fixed size 动态数组或者固定长度数组 * Initialization * Insert(push), get, update, delete, pop, length * Creating array in memory * Returning array from function */ contract Array { uint[] public nums = [1, 2, 3]; uint[5] public numsFixed = [4, 5, 6, 7, 8]; function examples() external { // 向数组尾部推入数据 nums.push(4); // [1, 2, 3, 4] uint x = nums[0]; // x = 1 nums[2] = 777; // [1, 2, 777, 4] // 删除数组的值,但不会更改长度,赋 0 delete nums[1]; // [1, 0, 777, 4] // 弹出数组最后一位数据 nums.pop(); // [1, 2, 777] uint len = nums.length; // create array in memory uint[] memory a = new uint[](5); // 内存中只能定义定长数组,根据索引赋值。 a[1] = 123; } // 返回数组所有内容,内存类型 function returnArray() external view returns (uint[] memory) { return nums; } } ``` ## 18. * 删除数组元素通过移位/替换 - 通过移位删除数组元素 - 实现思路:先把 index 后的值前移 1 位,然后再 pop 掉最后一位元素 - 示例:[1, 2, 3] --> remove(1) --> [1, 3, 3] --> [1, 3] - 优点:数组顺序没有变化 - 缺点:消耗大量gas - 通过替换删除数组元素 - 实现思路:把要删除的元素数据用最后一个元素数据替换,最后再pop掉最后一个数据 - 示例:[1, 2, 3, 4] --> removeNew(1) --> [1, 4, 3, 4] --> [1, 4, 3] - 优点:消耗少量gas - 缺点:数组顺序发生变化 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; contract ArrayShift { uint[] public arr; function example() public { arr = [1, 2, 3]; delete arr[1]; // [1, 0, 3] } // 实现 remove 数组中的某个值。(数组的顺序不变,但是比较消耗gas) // 例如 // [1, 2, 3] --> remove(1) --> [1, 3, 3] --> [1, 3] // 实现思路 先把 index 后的值前移 1 位,然后再 pop 最后一位 function remove(uint _index) public { require(_index < arr.length, "index out of bound!"); for (uint i = _index; i < arr.length; i++) { arr[i] = arr[i + 1]; } arr.pop(); } // 实现方法二:比较少消耗gas,但是顺序打乱了 // 实现思路:把要删除的数据用最后一个数据替换,最后再pop掉最后一个数据 // [1, 2, 3, 4] --> removeNew(1) --> [1, 4, 3, 4] --> [1, 4, 3] function removeNew(uint _index) public { require(_index < arr.length, "index out of bound!"); arr[_index] = arr[arr.length - 1]; arr.pop(); } // 测试 function testNew() external { arr = [1, 2, 3, 4]; // remove(1); removeNew(1); assert(arr.length ==3); assert(arr[0] == 1); assert(arr[1] == 4); assert(arr[2] == 3); } } ``` ## 19. 映射 从查找是否有 "tom" 这个值 - 第一种 - 示例:["jacky1", "jacky2", "jacky3", "jacky4"] - 思路:挨个循环查找,消耗大量gas; - 第二种 - 示例:["jacky1":true, "jacky2":true, "jacky3":true, "jacky4":true] - 思路:键值映射 true,只要判断arr["tom"]是否true就可以。 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; // Mapping // How to declare mapping (simple and nested) // Set, get, delete contract Mapping { // 地址,余额映射 mapping(address => uint) public balances; // 多重映射 mapping(address => mapping(address => bool)) public isFirend; function examples() external { // 赋值 balances[msg.sender] = 1234; // 获取值 uint bal = balances[msg.sender]; uint bal2 = balances[address(1)]; // 不存在应设置,默认返回 uint 默认值0 balances[msg.sender] += 456; // 123 + 456 delete balances[msg.sender]; // 删除后,变成默认值 0 isFirend[msg.sender][address(this)] = true; } } ``` ## 20. 迭代映射 ``` solidity // SPDX-License-Identifier: GPL-2.0 pragma solidity >=0.8.7 <0.9.0; /** * IterableMapping */ contract IterableMapping { // 余额 mapping(address => uint256) public balances; // 某地址是否存在映射中 mapping(address => bool) public inserted; // 所有存在地址 address[] public keys; /** 添加地址 */ function set(address _key, uint256 _val) external { // 给地址赋值余额 balances[_key] = _val; // 判断地址是否在映射中 if (!inserted[_key]) { // 添加映射 inserted[_key] = true; // 添加存在地址 keys.push(_key); } } /** 获取地址数组长度 */ function getSize() external view returns (uint256) { return keys.length; } /** 获取第一个地址余额 */ function getFirstAddressBal() external view returns (uint256) { return balances[keys[0]]; } /** 获取最后一个地址的余额 */ function getLastAddressBal() external view returns (uint256) { return balances[keys[keys.length - 1]]; } /** 获取任意位置地址的余额 */ function getAddressBalAtIndex(uint256 _index) external view returns (uint256) { return balances[keys[_index]]; } /** 遍历所有地址余额,getSize + getAddressBalAtIndex 结合使用 */ } ``` ## 21. Structs 结构体 ``` solidity // SPDX-License-Identifier: GPL-2.0 pragma solidity >=0.8.7 <0.9.0; /** * Structs */ contract Structs { struct Car { string model; uint256 year; address owner; } // 单辆车 Car public car; // 多辆车 Car[] public cars; // 某人有多辆车 mapping(address => Car[]) public carsByOwner; function example() external { Car memory ec6 = Car("NIO EC6", 2021, msg.sender); Car memory et7 = Car({model: "NIO ET7", year: 2022, owner: msg.sender}); Car memory es8; es8.model = "NIO ES8"; es8.year = 2019; es8.owner = msg.sender; cars.push(ec6); cars.push(et7); cars.push(es8); Car memory _car = cars[0]; _car.year; Car storage _carModify = cars[0]; _carModify.year = 2020; delete _carModify.owner; delete cars[1]; } } ``` ## 22. 枚举 enum ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 枚举 */ contract Enum { enum Status { None, Pending, Shipped, Completed, Rejected, Canceled } Status public status; struct Order { address buyer; Status status; } Order[] public orders; function get() external view returns (Status) { return status; } function set(Status _status) external { status = _status; } function ship() external { status = Status.Shipped; } function reset() external { delete status; // 恢复默认值 None } } ``` ## 23. 部署 Constract ## 24. 数据存储位置 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7 <0.9.0; /** * 数据存储位置 * - storage, memory and calldata(只用于输入参数) */ contract DataLocations { struct MyStruct { uint256 foo; string text; } mapping(address => MyStruct) public myStructs; function examples(uint256[] calldata y) external returns (uint256[] memory) { myStructs[msg.sender] = MyStruct(123, "bar"); MyStruct storage myStruct = myStructs[msg.sender]; // 修改后,状态变量的值随之改变 myStruct.text = "newBar"; MyStruct memory myStructMem = myStructs[msg.sender]; // 修改后,状态变量不改变,只不过是局部变量myStructMem改变,并随着函数执行完而从内存中消失 myStructMem.foo = 235; _internal(y); uint256[] memory memArr = new uint256[](3); memArr[0] = 345; return memArr; } function _internal(uint256[] calldata y) private pure { uint256 x = y[0]; x++; } } ``` ## 25. 简单存储 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7; /** * 简单存储 * - */ contract SimpleStorage { string public text; // calldata 89626 gas // memory 90114 gas function set(string calldata _text) external { text = _text; } function get() external view returns (string memory) { return text; } } ``` ## 26. 待办事项列表 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.8.7; /** * 待办事项列表 * - Insert, update, read from array of structs */ contract TodoList { // 事项结构体 struct Todo { string text; // 待办状态 bool completed; } // 待办数组 Todo[] public todos; /** 创建待办事项 */ function create(string calldata _text) external { todos.push(Todo({ text: _text, completed: false })); } /** * 更新待办事项 * _index: 待办事项索引 * _text: 更新内容 */ function updateText(uint _index, string calldata _text) external { // 35138 gas 如果只更新一个属性,则节省gas,更新多个多次装入内存反而消耗更多gas todos[_index].text = _text; // todos[_index].text = _text; // todos[_index].text = _text; // todos[_index].text = _text; // 34578 gas 更新多个属性则节省gas // Todo storage todo = todos[_index]; // todo.text = _text; // todo.text = _text; // todo.text = _text; // todo.text = _text; } /** * 获取待办事项 * params * _index: 事项索引 */ function get(uint _index) external view returns (string memory, bool) { // storage - 29397 直接从状态变量中拷贝过来 Todo storage todo = todos[_index]; // memory - 29480 从状态变量中拷贝到内存 // Todo memory todo = todos[_index]; return (todo.text, todo.completed); } /** * 更新是否完成 * _index: 事项索引 */ function toggleCompleted(uint _index) external { todos[_index].completed = !todos[_index].completed; } } ``` ## 27. 事件 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; /** * 事件:记录当前智能合约运行状态 * 时间存储更节约gas */ contract Event { // 声明事件 event Log(string message, uint val); // 带有索引的事件,最多不超过3个索引 event IndexedLog(address indexed sender, uint val); function example() external { // 触发事件 emit Log("foo", 123); emit IndexedLog(msg.sender, 345); } event Message(address _from, address _to, string message); function sendMsg(address _to, string calldata message) external { emit Message(msg.sender, _to, message); } } ``` ## 28. 继承 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract A { function foo() public pure virtual returns (string memory) { return "A"; } function bar() public pure virtual returns (string memory) { return "A"; } function baz() public pure returns (string memory) { return "A"; } } contract B is A { function foo() public pure override returns (string memory) { return "B"; } function bar() public pure override returns (string memory) { return "B"; } // B合约因为继承A合约仍然包含 baz() 函数 } ``` ## 29. 继承,构造函数传参 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // 2 ways to call parent constructors // Order of initialization contract S { string public name; constructor(string memory _name) { name = _name; } } contract T { string public text; constructor(string memory _text) { text = _text; } } // 继承后向构造函数传参 contract U is S("s"), T("t") { } contract V is S, T { constructor(string memory _name, string memory _text) S(_name) T(_text) { } } contract VV is S("s"), T { constructor(string memory _text) T(_text) { } } ``` ## 30. 可视范围 ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // visibility // private - only inside contract // internal - only inside contract and child contracts // public - inside and outside contract // external - only from outside contract contract VisibilityBase { uint private x = 0; uint internal y = 1; uint public z = 2; function privateFunc() private pure returns (uint) { return 0; } function internalFunc() internal pure returns (uint) { return 100; } function publicFunc() public pure returns (uint) { return 200; } function externalFunc() external pure returns (uint) { return 300; } function examples() external view { x + y + z; privateFunc(); internalFunc(); publicFunc(); // 外部函数可以使用this关键字访问,比较浪费gas this.externalFunc(); } } contract VisibilityChild is VisibilityBase { function examples2() external view { y + z; internalFunc(); publicFunc(); } } ``` ## 31. immutable ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // 必须部署的时候赋值 contract Immutable { // 45718 gass // address public owner = msg.sender; // 43585 gas 使用 immutable 关键字更节省gas // address public immutable owner = msg.sender; address public immutable owner; // 构造函数赋值 constructor() { owner = msg.sender; } uint public x; function foo() external { require(msg.sender == owner); x += 1; } } ``` ## 32. payable ``` solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // 向合约发送主币 contract Payable { address payable public owner; constructor() { owner = payable(msg.sender); } // 向合约发送主币 function deposit() external payable { } // 当前合约余额 function getBalance() external view returns(uint) { return address(this).balance; } } ``` ## 33. fallback ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; /** Fallback executed when - function does't exist - directly send ETH fallback() or receive()? eth is send to contract | is msg.data empty? / \ yes no / \ receive() exist? fallback() / \ yes no / \ receive() fallback() */ contract Fallback { event Log(string func, address sender, uint value, bytes data); fallback() external payable { emit Log("fallback", msg.sender, msg.value, msg.data); } receive() external payable { emit Log("receive", msg.sender, msg.value, ""); } } ``` ## 34. sentETH ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // 3 ways to send ETH // transfer - 2300 gas, reverts // send - 2300 gas, returns bool // call - all gas, returns bool and data contract SendETH { constructor() payable {} receive() external payable {} function sendViaTransfer(address payable _to) external payable { _to.transfer(9); } function sendViaSend(address payable _to) external payable { bool sent = _to.send(8); require(sent, "send failed"); } function sendViaCall(address payable _to) external payable { (bool success, ) = _to.call{value: 7}(""); require(success, "call failed"); } } contract EthReceiver { event Log(uint amount, uint gas); receive() external payable { emit Log(msg.value, gasleft()); } } ``` ## 35. EtherWalllet ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract EtherWallet { address payable public owner; constructor() { owner = payable(msg.sender); } receive() external payable { } function withdraw(uint _amount) external { require(msg.sender == owner, "caller is not owner"); payable(msg.sender).transfer(_amount); } function getBalance() external view returns (uint) { return address(this).balance; } } ``` ## 36. 调用其他合约 ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract CallTestContract { // function setX(address _test, uint _x) external { // TestContract(_test).setX(_x); // } function setX(TestContract _test, uint _x) external { _test.setX(_x); } function getX (address _test) external view returns (uint){ return TestContract(_test).getX(); } function setXandReceiveEther(address _test, uint _x) external payable { TestContract(_test).setXandReceiveEther{ value: msg.value }(_x); } function getXandValue(address _test) external view returns (uint, uint) { return TestContract(_test).getXandValue(); } } contract TestContract { uint public x; uint public value = 123; function setX(uint _x) external { x = _x; } function getX() external view returns (uint) { return x; } function setXandReceiveEther(uint _x) external payable { x = _x; value = msg.value; } function getXandValue() external view returns (uint, uint) { return (x, value); } } ``` ## 37. 接口合约 ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract Counter { uint public count; function inc() external { count += 1; } function dec() external { count -= 1; } } ``` ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; // 不知道合约代码或者合约代码太庞大,写接口进行调用 interface ICounter { function count() external view returns (uint); function inc() external; } contract CallInterface { uint public count; function examples(address _counter) external { ICounter(_counter).inc(); count = ICounter(_counter).count(); } } ``` ## 38. 低级call ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract TestCall { string public message; uint public x; event Log(string message); fallback() external payable { emit Log("fallback was called"); } function foo(string memory _message, uint _x) external payable returns (bool, uint){ message = _message; x = _x; return (true, 999); } receive() external payable{} } contract Call { bytes public data; function callFoo(address _test) external payable { (bool success, bytes memory _data) = _test.call{value: 111, gas: 5000}( abi.encodeWithSignature("foo(string, uint256)", "call foo", 123)); require(success, "called failed"); data = _data; } // 调用合约不存在的函数 function callDoesNotExitFunc(address _test) external { (bool success, ) = _test.call(abi.encodeWithSignature("DoesNotExit()")); require(success, "call failed"); } } ``` ## 39. 委托调用 ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; /** A calls B, sends 100 wei B calls C, sends 50 wei A --> B --> C msg.sender = B msg.value = 50 execute code on C's state variables use ETH in C A calls B, sends 100 wei B delegatecall C A --> B --> C msg.sender = A msg.value = 100 execute code on B's state variables use ETH in B */ contract TestDelegateCall { uint public num; address public sender; uint public value; address public owner; function setVars(uint _num) external payable { num = 2*_num; sender = msg.sender; value = msg.value; } } // 用于升级合约,代理合约状态变量要和被调用合约状态变量顺序要一致。 contract DelegateCall { uint public num; address public sender; uint public value; function setVars(address _test, uint _num) external payable{ // _test.delegatecall(abi.encodeWithSignature("setVars(uint256)", _num)); (bool success, bytes memory data) = _test.delegatecall( abi.encodeWithSelector(TestDelegateCall.setVars.selector, _num) ); require(success, "delegatecall failed"); } } ``` ## 40. 工厂合约 ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; contract Account { address public bank; address public owner; constructor(address _owner) payable { bank = msg.sender; owner = _owner; } } // 在工厂合约内创建账户合约 contract AccountFactory { Account[] public accounts; function createAccount(address _owner) external payable { // 创建账户合约并返回地址, value 传入主币 Account account = new Account{value: 123}(_owner); accounts.push(account); } } ``` ## 41. 库合约 ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; library Math { function max(uint x, uint y) internal pure returns (uint) { return x >= y ? x : y; } } contract Test { function testMax(uint _x, uint _y) external pure returns (uint) { return Math.max(_x, _y); } } library ArrayLib { function findIndex(uint[] storage arr, uint x) internal view returns (uint) { for (uint i = 0; i< arr.length; i++) { if (arr[i] == x) { return i; } } revert("not found"); } } contract TestArray { using ArrayLib for uint[]; uint[] public arr = [3, 2, 1]; // 查找数字在数组中的索引 function testFind() external view returns (uint i) { // return ArrayLib.find(arr, 2); return arr.findIndex(2); } } ```