diff --git "a/cn/00_blockchain\345\237\272\347\241\200/01_\344\273\200\344\271\210\346\230\257\345\214\272\345\235\227\351\223\276.html" "b/cn/00_blockchain\345\237\272\347\241\200/01_\344\273\200\344\271\210\346\230\257\345\214\272\345\235\227\351\223\276.html" index 1f9fb216..b7945939 100644 --- "a/cn/00_blockchain\345\237\272\347\241\200/01_\344\273\200\344\271\210\346\230\257\345\214\272\345\235\227\351\223\276.html" +++ "b/cn/00_blockchain\345\237\272\347\241\200/01_\344\273\200\344\271\210\346\230\257\345\214\272\345\235\227\351\223\276.html" @@ -36,10 +36,6 @@ - - - - @@ -3086,7 +3082,7 @@
比特币是永远的神,在币圈一般都称之为:大饼,各种牛鬼蛇神币种的涨跌基本完全看大饼的脸色,大饼每四年减产一次(即挖矿的奖励减半),大约在2140年时会全部挖完(2100w枚),届时就是另外一个世界了,你我应该看不着了。
我们的重心是以太坊开发,所以比特币部分就做个简短的介绍,比特币总量计算如下:
-package main
+package main
-import "fmt"
+import "fmt"
-func main() {
- total := 0.0 //总量
- interval := 21.0 //区块间隔,万单位
+func main() {
+ total := 0.0 //总量
+ interval := 21.0 //区块间隔,万单位
- reward := 50.0 //奖励数量,最初50个
+ reward := 50.0 //奖励数量,最初50个
- for reward != 0 {
- //累加挖矿值
+ for reward != 0 {
+ //累加挖矿值
amount := reward * interval
- total += amount //总量
+ total += amount //总量
- reward *= 0.5 //奖励减半
+ reward *= 0.5 //奖励减半
}
- fmt.Println("total : ", total)
- fmt.Printf("total : %f\n", total)
+ fmt.Println("total : ", total)
+ fmt.Printf("total : %f\n", total)
}
加V入群:dukeweb3,公众号:阿杜在新加坡,一起抱团拥抱web3,下期见!
@@ -3099,7 +3095,7 @@ 小结
@@ -3185,14 +3181,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/03_\344\273\200\344\271\210\346\230\257\344\273\245\345\244\252\345\235\212.html" "b/cn/00_blockchain\345\237\272\347\241\200/03_\344\273\200\344\271\210\346\230\257\344\273\245\345\244\252\345\235\212.html"
index 0a138932..d89f511a 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/03_\344\273\200\344\271\210\346\230\257\344\273\245\345\244\252\345\235\212.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/03_\344\273\200\344\271\210\346\230\257\344\273\245\345\244\252\345\235\212.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3038,10 +3034,10 @@ 智能合约
一旦被触发就会按照既定逻辑执行,无人能够作恶(这类似于智能)
关于这部分内容我们会在后续注重讲解,先睹为快,我们打印个:"helloworld"
-pragma solidity ^0.8.13;
+pragma solidity ^0.8.13;
contract HelloWorld {
- string public greet = "Hello World!";
+ string public greet = "Hello World!";
}
这些程序一旦部署到区块链网络上,我们就可以读取到链上数据:Hello World,而这句问候,将会永远的存在于区块链上。
@@ -3088,7 +3084,7 @@ 小结
@@ -3174,14 +3170,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/04_App\344\270\216Dapp.html" "b/cn/00_blockchain\345\237\272\347\241\200/04_App\344\270\216Dapp.html"
index 5b218025..e68dee9f 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/04_App\344\270\216Dapp.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/04_App\344\270\216Dapp.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3077,7 +3073,7 @@ 小结
@@ -3163,14 +3159,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/05_DAPP\346\212\200\346\234\257\346\240\210.html" "b/cn/00_blockchain\345\237\272\347\241\200/05_DAPP\346\212\200\346\234\257\346\240\210.html"
index 9c0d63f6..b573f1da 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/05_DAPP\346\212\200\346\234\257\346\240\210.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/05_DAPP\346\212\200\346\234\257\346\240\210.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3123,7 +3119,7 @@ 设计原则
@@ -3209,14 +3205,6 @@ 设计原则
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/06_web3\344\274\230\350\264\250\345\255\246\344\271\240\350\265\204\346\272\220.html" "b/cn/00_blockchain\345\237\272\347\241\200/06_web3\344\274\230\350\264\250\345\255\246\344\271\240\350\265\204\346\272\220.html"
index b53e4773..82c8d0e4 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/06_web3\344\274\230\350\264\250\345\255\246\344\271\240\350\265\204\346\272\220.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/06_web3\344\274\230\350\264\250\345\255\246\344\271\240\350\265\204\346\272\220.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3088,7 +3084,7 @@ 日常资讯
@@ -3174,14 +3170,6 @@ 日常资讯
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/07_\351\222\261\345\214\205\344\270\216\345\234\260\345\235\200.html" "b/cn/00_blockchain\345\237\272\347\241\200/07_\351\222\261\345\214\205\344\270\216\345\234\260\345\235\200.html"
index 11028199..f345f1ac 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/07_\351\222\261\345\214\205\344\270\216\345\234\260\345\235\200.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/07_\351\222\261\345\214\205\344\270\216\345\234\260\345\235\200.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3208,7 +3204,7 @@ 小结
@@ -3294,14 +3290,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/08_Remix\346\225\231\347\250\213.html" "b/cn/00_blockchain\345\237\272\347\241\200/08_Remix\346\225\231\347\250\213.html"
index 7aef67bf..4b89090f 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/08_Remix\346\225\231\347\250\213.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/08_Remix\346\225\231\347\250\213.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3040,12 +3036,12 @@ 常用功能
Helloworld
编写第一极简智能合约,在区块链上存储并且打印:Helloworld,代码如下:
-// SPDX-License-Identifier: MIT
-// compiler version must be greater than or equal to 0.8.13 and less than 0.9.0
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+// compiler version must be greater than or equal to 0.8.13 and less than 0.9.0
+pragma solidity ^0.8.13;
contract HelloWorld {
- string public greet = "Hello World!";
+ string public greet = "Hello World!";
}
@@ -3066,8 +3062,8 @@ 部署
启动后台
npm install -g @remix-project/remixd
-remixd -s
-
+remixd -s <contract_folder>
+
Vscode插件
- Ethereum Remix
@@ -3122,7 +3118,7 @@ 小结
@@ -3208,14 +3204,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/09_\345\214\272\345\235\227\351\223\276\345\260\261\344\270\232.html" "b/cn/00_blockchain\345\237\272\347\241\200/09_\345\214\272\345\235\227\351\223\276\345\260\261\344\270\232.html"
index ed142cab..b5edb0dd 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/09_\345\214\272\345\235\227\351\223\276\345\260\261\344\270\232.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/09_\345\214\272\345\235\227\351\223\276\345\260\261\344\270\232.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3055,7 +3051,7 @@ 第9节:区块链就业
@@ -3141,14 +3137,6 @@ 第9节:区块链就业
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/10_solidity\345\262\227\344\275\215\350\246\201\346\261\202.html" "b/cn/00_blockchain\345\237\272\347\241\200/10_solidity\345\262\227\344\275\215\350\246\201\346\261\202.html"
index c903b659..5492984e 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/10_solidity\345\262\227\344\275\215\350\246\201\346\261\202.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/10_solidity\345\262\227\344\275\215\350\246\201\346\261\202.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3042,57 +3038,57 @@ 初级
中高级:
如何成为一名合格的智能合约高级工程师:
-1、熟悉 Solidity 语言,能使用 hardhat 完成智能合约的自动化测试、优化、部署、交互和 SDK 封装。
-2、熟悉 Openzipplin 的所有库文件,能够完成可升级合约的部署和升级。
-3、熟悉市面上主流的智能合约项目的实现:dex、lend、skating、oracle、bridge(每种至少两个)
-4、熟悉内联汇编,清楚智能合约执行过程的 GAS 消耗,能对智能合约做最大程度的 GAS 优化。
-5、熟悉主流的 EIP 标准,跟进 EIP 提案的进度,能撰写新提案的分析。
-6、熟悉 Solidity 各个版本之间的区别,能跟进 Solidity 版本更新。
-7、熟悉 Solidity 编译成字节码的步骤,能使用字节码反推出智能合约的执行步骤。
-8、熟悉智能合约在 EVM 上执行的原理,能对区块链做 EVM 兼容。
-9、熟悉其他主流的智能合约语言和机制(比如 rust 和 move)。
+1、熟悉 Solidity 语言,能使用 hardhat 完成智能合约的自动化测试、优化、部署、交互和 SDK 封装。
+2、熟悉 Openzipplin 的所有库文件,能够完成可升级合约的部署和升级。
+3、熟悉市面上主流的智能合约项目的实现:dex、lend、skating、oracle、bridge(每种至少两个)
+4、熟悉内联汇编,清楚智能合约执行过程的 GAS 消耗,能对智能合约做最大程度的 GAS 优化。
+5、熟悉主流的 EIP 标准,跟进 EIP 提案的进度,能撰写新提案的分析。
+6、熟悉 Solidity 各个版本之间的区别,能跟进 Solidity 版本更新。
+7、熟悉 Solidity 编译成字节码的步骤,能使用字节码反推出智能合约的执行步骤。
+8、熟悉智能合约在 EVM 上执行的原理,能对区块链做 EVM 兼容。
+9、熟悉其他主流的智能合约语言和机制(比如 rust 和 move)。
Expert
What is your responsibility?
1. Implement EVM-based smart contracts
-2. Review EVM-based smart contracts and audit for security issues
+2. Review EVM-based smart contracts and audit for security issues
3. Support front-end developers to build and maintain web apps that call smart contracts
4. Deploy and maintain existing smart contracts
5. Manage wallets used to deploy and operate smart contracts
-6. Proactively make suggestions for improving our products and internal processes
+6. Proactively make suggestions for improving our products and internal processes
What are the requirements?
-1. You are an expert in smart contracts and Solidity
+1. You are an expert in smart contracts and Solidity
2. You have a deep understanding of the EVM and understand smart contracts down to the opcode-level
-3. You are familiar with the source code of at least one Ethereum node implementation
+3. You are familiar with the source code of at least one Ethereum node implementation
4. You have a strong understanding of JavaScript fundamentals, modern tooling, and ecosystem
-5. You are proficient in using web3.js and ethers.js libraries
+5. You are proficient in using web3.js and ethers.js libraries
6. You have some front-end development knowledge and have some knowledge of React
What will be great to have?
1. You are passionate about blockchain technology and a decentralized future
2. You are contributing to the open-source community
-3. You have considered and feel comfortable, or have experience working in a startup environment
+3. You have considered and feel comfortable, or have experience working in a startup environment
Expert
You are:
-● A proven Solidity dev with 2+ years of experience building prominent products in the DeFi space
-● Deep understanding of DeFi, decentralized protocols, DAOs, cryptocurrencies and blockchain in general and EVM
-● Experience with web3 frameworks and interfacing with Ethereum nodes and services (Geth, Hardhat, Alchemy, Infura, Tenderly, etc.)
-● Experience in deploying complex systems to Ethereum
+● A proven Solidity dev with 2+ years of experience building prominent products in the DeFi space
+● Deep understanding of DeFi, decentralized protocols, DAOs, cryptocurrencies and blockchain in general and EVM
+● Experience with web3 frameworks and interfacing with Ethereum nodes and services (Geth, Hardhat, Alchemy, Infura, Tenderly, etc.)
+● Experience in deploying complex systems to Ethereum
● Fast learner and independent
● Ability to work and communicate efficiently remotely
Nice To Have
-● Graduate in computer science, math or engineering
-● Experience in implementing complex math with smart contract constraints
+● Graduate in computer science, math or engineering
+● Experience in implementing complex math with smart contract constraints
You will:
-● Participate in the entire development lifecycle, focusing on design, coding and debugging
-● Build reusable code and libraries for future use
-● Optimize the application for performance and scalability
+● Participate in the entire development lifecycle, focusing on design, coding and debugging
+● Build reusable code and libraries for future use
+● Optimize the application for performance and scalability
● Implement security and data access
-● Provide clean API for different clients
+● Provide clean API for different clients
@@ -3121,7 +3117,7 @@ Expert
@@ -3207,14 +3203,6 @@ Expert
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/11_rpc\350\212\202\347\202\271.html" "b/cn/00_blockchain\345\237\272\347\241\200/11_rpc\350\212\202\347\202\271.html"
index 4b76eaf8..bf87030f 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/11_rpc\350\212\202\347\202\271.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/11_rpc\350\212\202\347\202\271.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3057,7 +3053,7 @@ 第11节:rpc节点
@@ -3143,14 +3139,6 @@ 第11节:rpc节点
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/12_etherscan.html" "b/cn/00_blockchain\345\237\272\347\241\200/12_etherscan.html"
index c0822387..eb56ba80 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/12_etherscan.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/12_etherscan.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第14节:Etherscan使用
@@ -3136,14 +3132,6 @@ 第14节:Etherscan使用
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/13_\345\214\272\345\235\227\351\223\276\351\273\221\350\257\235.html" "b/cn/00_blockchain\345\237\272\347\241\200/13_\345\214\272\345\235\227\351\223\276\351\273\221\350\257\235.html"
index ea99453e..413b4748 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/13_\345\214\272\345\235\227\351\223\276\351\273\221\350\257\235.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/13_\345\214\272\345\235\227\351\223\276\351\273\221\350\257\235.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3059,7 +3055,7 @@ CA
@@ -3145,14 +3141,6 @@ CA
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/20_\345\246\202\344\275\225\345\217\202\344\270\216.html" "b/cn/00_blockchain\345\237\272\347\241\200/20_\345\246\202\344\275\225\345\217\202\344\270\216.html"
index ce5e5cc5..6f184ee9 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/20_\345\246\202\344\275\225\345\217\202\344\270\216.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/20_\345\246\202\344\275\225\345\217\202\344\270\216.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3138,7 +3134,7 @@ 如何参与
@@ -3224,14 +3220,6 @@ 如何参与
-
-
-
-
-
-
-
-
diff --git "a/cn/00_blockchain\345\237\272\347\241\200/index.html" "b/cn/00_blockchain\345\237\272\347\241\200/index.html"
index 56a7db6b..72f58516 100644
--- "a/cn/00_blockchain\345\237\272\347\241\200/index.html"
+++ "b/cn/00_blockchain\345\237\272\347\241\200/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3093,7 +3089,7 @@ 4. 实用工具
@@ -3179,14 +3175,6 @@ 4. 实用工具
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/01_helloworld.html" "b/cn/01_solidity\345\237\272\347\241\200/01_helloworld.html"
index a1580e0c..05bc418e 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/01_helloworld.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/01_helloworld.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3025,12 +3021,12 @@ 第1节:Hello world
- pragma solidity ^0.8.13; 表明当前编译合约的版本号,向上兼容至0.9
-// SPDX-License-Identifier: MIT
-// compiler version must be greater than or equal to 0.8.13 and less than 0.9.0
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+// compiler version must be greater than or equal to 0.8.13 and less than 0.9.0
+pragma solidity ^0.8.13;
contract HelloWorld {
- string public greet = "Hello World!";
+ string public greet = "Hello World!";
}
@@ -3060,7 +3056,7 @@ 第1节:Hello world
@@ -3146,14 +3142,6 @@ 第1节:Hello world
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/02_\347\254\254\344\270\200\344\270\252dapp.html" "b/cn/01_solidity\345\237\272\347\241\200/02_\347\254\254\344\270\200\344\270\252dapp.html"
index 4de9f867..9f95f6a6 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/02_\347\254\254\344\270\200\344\270\252dapp.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/02_\347\254\254\344\270\200\344\270\252dapp.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3022,30 +3018,30 @@ 第2节:第一个dapp
小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。
职场进阶: https://dukeweb3.com
-// 指定编译器版本,版本标识符
-pragma solidity ^0.8.13;
+// 指定编译器版本,版本标识符
+pragma solidity ^0.8.13;
-// 关键字 contract 跟java的class一样 智能合约是Inbox
+// 关键字 contract 跟java的class一样 智能合约是Inbox
contract Inbox{
- // 状态变量,存在链上
+ // 状态变量,存在链上
string public message;
- // 构造函数
- constructor(string memory initMessage) {
- // 本地变量
+ // 构造函数
+ constructor(string memory initMessage) {
+ // 本地变量
string memory tmp = initMessage;
message = tmp;
}
- // 写操作,需要支付手续费
- function setMessage(string memory _newMessage) public {
+ // 写操作,需要支付手续费
+ function setMessage(string memory _newMessage) public {
message = _newMessage;
}
- // 读操作,不需要支付手续费
- function getMessage() public view returns(string memory) {
- return message;
+ // 读操作,不需要支付手续费
+ function getMessage() public view returns(string memory) {
+ return message;
}
}
@@ -3076,7 +3072,7 @@ 第2节:第一个dapp
@@ -3162,14 +3158,6 @@ 第2节:第一个dapp
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/03_\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213.html" "b/cn/01_solidity\345\237\272\347\241\200/03_\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213.html"
index 0aeafd6d..90ce42c8 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/03_\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/03_\345\237\272\347\241\200\346\225\260\346\215\256\347\261\273\345\236\213.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,42 +3026,42 @@ 第3节 基础数据类型
定长字节:bytes1~bytes32
地址:address(20个字节,40个16进制字符,共160位),如:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Primitives {
- bool public flag = true;
+ bool public flag = true;
- /*
+ /*
uint stands for unsigned integer, meaning non negative integers
different sizes are available
uint8 ranges from 0` to 2 ** 8 - 1
uint16 ranges from 0 to 2 ** 16 - 1
...
uint256 ranges from 0 to 2 ** 256 - 1
- */
- uint8 public u8 = 1;
- uint public u256 = 456;
- uint public u = 123; // uint is an alias for uint256
+ */
+ uint8 public u8 = 1;
+ uint public u256 = 456;
+ uint public u = 123; // uint is an alias for uint256
- /*
+ /*
Negative numbers are allowed for int types.
Like uint, different ranges are available from int8 to int256
int256 ranges from -2 ** 255 to 2 ** 255 - 1
int128 ranges from -2 ** 127 to 2 ** 127 - 1
- */
- int8 public i8 = -1;
- int public i256 = 456;
- int public i = -123; // int is same as int256
+ */
+ int8 public i8 = -1;
+ int public i256 = 456;
+ int public i = -123; // int is same as int256
- // minimum and maximum of int
+ // minimum and maximum of int
int public minInt = type(int).min;
int public maxInt = type(int).max;
- address public addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
+ address public addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
- /*
+ /*
In Solidity, the data type byte represent a sequence of bytes.
Solidity presents two type of bytes types :
@@ -3074,16 +3070,16 @@ 第3节 基础数据类型
The term bytes in Solidity represents a dynamic array of bytes.
It’s a shorthand for byte[] .
- */
- bytes1 a = 0xb5; // [10110101]
- bytes1 b = 0x56; // [01010110]
-
- // Default values
- // Unassigned variables have a default value
- bool public defaultBoo; // false
- uint public defaultUint; // 0
- int public defaultInt; // 0
- address public defaultAddr; // 0x0000000000000000000000000000000000000000
+ */
+ bytes1 a = 0xb5; // [10110101]
+ bytes1 b = 0x56; // [01010110]
+
+ // Default values
+ // Unassigned variables have a default value
+ bool public defaultBoo; // false
+ uint public defaultUint; // 0
+ int public defaultInt; // 0
+ address public defaultAddr; // 0x0000000000000000000000000000000000000000
}
@@ -3113,7 +3109,7 @@ 第3节 基础数据类型
@@ -3199,14 +3195,6 @@ 第3节 基础数据类型
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/04_\345\217\230\351\207\217variables.html" "b/cn/01_solidity\345\237\272\347\241\200/04_\345\217\230\351\207\217variables.html"
index 189a8b61..35f465c2 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/04_\345\217\230\351\207\217variables.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/04_\345\217\230\351\207\217variables.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3039,21 +3035,21 @@ 第4节:语法-变量
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Variables {
- // State variables are stored on the blockchain.
- string public msg = "Hello";
- uint public age = 26;
+ // State variables are stored on the blockchain.
+ string public msg = "Hello";
+ uint public age = 26;
- function test() public {
- // Local variables are not saved to the blockchain.
- uint i = 456;
+ function test() public {
+ // Local variables are not saved to the blockchain.
+ uint i = 456;
- // Here are some global variables
- uint height = block.blocks; // Current block height
- address sender = msg.sender; // address of the caller
+ // Here are some global variables
+ uint height = block.blocks; // Current block height
+ address sender = msg.sender; // address of the caller
}
}
@@ -3084,7 +3080,7 @@ 第4节:语法-变量
@@ -3170,14 +3166,6 @@ 第4节:语法-变量
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/05_\345\270\270\351\207\217constant.html" "b/cn/01_solidity\345\237\272\347\241\200/05_\345\270\270\351\207\217constant.html"
index 2f040f4c..31660a0b 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/05_\345\270\270\351\207\217constant.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/05_\345\270\270\351\207\217constant.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,13 +3023,13 @@ 第5节:常量
常量更加节约gas,一般用大写来代表常量。
高阶用法:clone合约时,如果合约内有初始值,必须使用constant,否则clone的新合约初始值为空值。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Constants {
- // coding convention to uppercase constant variables
- address public constant MY_ADDRESS = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
- uint public constant MY_UINT = 123;
+ // coding convention to uppercase constant variables
+ address public constant MY_ADDRESS = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
+ uint public constant MY_UINT = 123;
}
@@ -3063,7 +3059,7 @@ 第5节:常量
@@ -3149,14 +3145,6 @@ 第5节:常量
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/06_\344\270\215\345\217\257\345\217\230\351\207\217immutable.html" "b/cn/01_solidity\345\237\272\347\241\200/06_\344\270\215\345\217\257\345\217\230\351\207\217immutable.html"
index 4b3822ad..679a8b87 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/06_\344\270\215\345\217\257\345\217\230\351\207\217immutable.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/06_\344\270\215\345\217\257\345\217\230\351\207\217immutable.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,17 +3022,17 @@ 第6节:不可变量immutable
与常量类似,但是不必硬编码,可以在构造函数时传值,部署后无法改变。
immutable仅支持值类型(如:int,address,bytes8),不支持非值类型(如:string,bytes)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Immutable {
- // coding convention to uppercase constant variables
+ // coding convention to uppercase constant variables
address public immutable MY_ADDRESS;
uint public immutable MY_UINT;
- bytes1 public immutable MY_BYTES1 = 0xff;
- // string public immutable greetings = "hello"; // error
+ bytes1 public immutable MY_BYTES1 = 0xff;
+ // string public immutable greetings = "hello"; // error
- constructor(uint _myUint) {
+ constructor(uint _myUint) {
MY_ADDRESS = msg.sender;
MY_UINT = _myUint;
}
@@ -3069,7 +3065,7 @@ 第6节:不可变量immutable
@@ -3155,14 +3151,6 @@ 第6节:不可变量immutable
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/07_\350\257\273\345\206\231\347\212\266\346\200\201\345\217\230\351\207\217.html" "b/cn/01_solidity\345\237\272\347\241\200/07_\350\257\273\345\206\231\347\212\266\346\200\201\345\217\230\351\207\217.html"
index 27bdb004..e8ca21d6 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/07_\350\257\273\345\206\231\347\212\266\346\200\201\345\217\230\351\207\217.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/07_\350\257\273\345\206\231\347\212\266\346\200\201\345\217\230\351\207\217.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,21 +3022,21 @@ 第7节:读写状态变量
写状态变量(上链)是一笔交易(tx),需要矿工打包,所以需要花费资金;
读取状态变量,是从账本中获取数据,不是一笔交易,所以免费。(必须加上view)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract SimpleStorage {
- // State variable to store a number
+ // State variable to store a number
uint public num;
- // You need to send a transaction to write to a state variable.
- function set(uint _num) public {
+ // You need to send a transaction to write to a state variable.
+ function set(uint _num) public {
num = _num;
}
- // You can read from a state variable without sending a transaction.
- function get() public view returns (uint) {
- return num;
+ // You can read from a state variable without sending a transaction.
+ function get() public view returns (uint) {
+ return num;
}
}
@@ -3071,7 +3067,7 @@ 第7节:读写状态变量
@@ -3157,14 +3153,6 @@ 第7节:读写状态变量
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/08_ether\345\222\214wei.html" "b/cn/01_solidity\345\237\272\347\241\200/08_ether\345\222\214wei.html"
index d5440203..48188aee 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/08_ether\345\222\214wei.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/08_ether\345\222\214wei.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,17 +3023,17 @@ 第8节:ether和wei
不含任何后缀的默认单位是 wei
1 gwei = 10^9 wei
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract EtherUnits {
- uint public oneWei = 1 wei;
- // 1 wei is equal to 1
- bool public isOneWei = 1 wei == 1;
+ uint public oneWei = 1 wei;
+ // 1 wei is equal to 1
+ bool public isOneWei = 1 wei == 1;
- uint public oneEther = 1 ether;
- // 1 ether is equal to 10^18 wei
- bool public isOneEther = 1 ether == 1e18;
+ uint public oneEther = 1 ether;
+ // 1 ether is equal to 10^18 wei
+ bool public isOneEther = 1 ether == 1e18;
}
@@ -3067,7 +3063,7 @@ 第8节:ether和wei
@@ -3153,14 +3149,6 @@ 第8节:ether和wei
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/09_gas\345\222\214gasprice.html" "b/cn/01_solidity\345\237\272\347\241\200/09_gas\345\222\214gasprice.html"
index ecf1574c..85c14524 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/09_gas\345\222\214gasprice.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/09_gas\345\222\214gasprice.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3040,7 +3036,7 @@ 第9节:Gas相关
验证:
gas_used: 197083
gas_price: 0.000000078489891145
-cost = gas_used * gas_price = 197083 * 0.000000078489891145 = 0.015469023216530034,#与上图一致
+cost = gas_used * gas_price = 197083 * 0.000000078489891145 = 0.015469023216530034,#与上图一致
@@ -3069,7 +3065,7 @@ 第9节:Gas相关
@@ -3155,14 +3151,6 @@ 第9节:Gas相关
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/10_ifElse.html" "b/cn/01_solidity\345\237\272\347\241\200/10_ifElse.html"
index 337f2713..075bdcba 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/10_ifElse.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/10_ifElse.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,28 +3019,28 @@ 第10节:if / else
职场进阶: https://dukeweb3.com
支持常规的if , else if, else
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract IfElse {
- function foo(uint x) public pure returns (uint) {
- if (x < 10) {
- return 0;
- } else if (x < 20) {
- return 1;
- } else {
- return 2;
+ function foo(uint x) public pure returns (uint) {
+ if (x < 10) {
+ return 0;
+ } else if (x < 20) {
+ return 1;
+ } else {
+ return 2;
}
}
- function ternary(uint _x) public pure returns (uint) {
- // if (_x < 10) {
- // return 1;
- // }
- // return 2;
+ function ternary(uint _x) public pure returns (uint) {
+ // if (_x < 10) {
+ // return 1;
+ // }
+ // return 2;
- // shorthand way to write if / else statement
- return _x < 10 ? 1 : 2;
+ // shorthand way to write if / else statement
+ return _x < 10 ? 1 : 2;
}
}
@@ -3075,7 +3071,7 @@ 第10节:if / else
@@ -3161,14 +3157,6 @@ 第10节:if / else
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/11_forWhileLoop.html" "b/cn/01_solidity\345\237\272\347\241\200/11_forWhileLoop.html"
index 295f3616..31d13e93 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/11_forWhileLoop.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/11_forWhileLoop.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,26 +3022,26 @@ 第11节:for and while
solidity支持for, while, do while循环;
尽量不要使用没有边界的循环,因为会导致达到gas limit,进而导致交易执行失败,因此很少使用while和do while
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Loop {
- function loop() public {
- // for loop
- for (uint i = 0; i < 10; i++) {
- if (i == 3) {
- // Skip to next iteration with continue
- continue;
+ function loop() public {
+ // for loop
+ for (uint i = 0; i < 10; i++) {
+ if (i == 3) {
+ // Skip to next iteration with continue
+ continue;
}
- if (i == 5) {
- // Exit loop with break
- break;
+ if (i == 5) {
+ // Exit loop with break
+ break;
}
}
- // while loop
+ // while loop
uint j;
- while (j < 10) {
+ while (j < 10) {
j++;
}
}
@@ -3078,7 +3074,7 @@ 第11节:for and while
@@ -3164,14 +3160,6 @@ 第11节:for and while
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/12_bytes\345\222\214string.html" "b/cn/01_solidity\345\237\272\347\241\200/12_bytes\345\222\214string.html"
index c11caf9d..92519f47 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/12_bytes\345\222\214string.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/12_bytes\345\222\214string.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,25 +3026,25 @@ 第12节:bytes和string
支持push方法添加
可以与string相互转换
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Bytes {
bytes public name;
- //1. 获取字节长度
- function getLen() public view returns(uint256) {
- return name.length;
+ //1. 获取字节长度
+ function getLen() public view returns(uint256) {
+ return name.length;
}
- //2. 可以不分空间,直接进行字符串赋值,会自动分配空间
- function setValue(bytes memory input) public {
+ //2. 可以不分空间,直接进行字符串赋值,会自动分配空间
+ function setValue(bytes memory input) public {
name = input;
}
- //3. 支持push操作,在bytes最后面追加元素
- function pushData() public {
- name.push("h");
+ //3. 支持push操作,在bytes最后面追加元素
+ function pushData() public {
+ name.push("h");
}
}
@@ -3058,18 +3054,18 @@ 第12节:bytes和string
string 不支持下标索引、不支持length、push方法
string 可以修改(需通过bytes转换)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-contract String {
- string public name = "lily";
+contract String {
+ string public name = "lily";
- function setName() public {
- bytes(name)[0] = "L";
+ function setName() public {
+ bytes(name)[0] = "L";
}
- function getLength() public view returns(uint256) {
- return bytes(name).length;
+ function getLength() public view returns(uint256) {
+ return bytes(name).length;
}
}
@@ -3100,7 +3096,7 @@ 第12节:bytes和string
@@ -3186,14 +3182,6 @@ 第12节:bytes和string
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/13_\346\230\240\345\260\204Mapping.html" "b/cn/01_solidity\345\237\272\347\241\200/13_\346\230\240\345\260\204Mapping.html"
index a7410640..3c0f93e7 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/13_\346\230\240\345\260\204Mapping.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/13_\346\230\240\345\260\204Mapping.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,50 +3024,50 @@ 第13节:Mapping
mapping不支持迭代器
不需要实例化等,定义后直接可以使用
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Mapping {
- // Mapping from address to uint
- mapping(address => uint) public myMap;
+ // Mapping from address to uint
+ mapping(address => uint) public myMap;
- function get(address _addr) public view returns (uint) {
- // Mapping always returns a value.
- // If the value was never set, it will return the default value.
- return myMap[_addr];
+ function get(address _addr) public view returns (uint) {
+ // Mapping always returns a value.
+ // If the value was never set, it will return the default value.
+ return myMap[_addr];
}
- function set(address _addr, uint _i) public {
- // Update the value at this address
+ function set(address _addr, uint _i) public {
+ // Update the value at this address
myMap[_addr] = _i;
}
- function remove(address _addr) public {
- // Reset the value to the default value.
- delete myMap[_addr];
+ function remove(address _addr) public {
+ // Reset the value to the default value.
+ delete myMap[_addr];
}
}
contract NestedMapping {
- // Nested mapping (mapping from address to another mapping)
- mapping(address => mapping(uint => bool)) public nested;
+ // Nested mapping (mapping from address to another mapping)
+ mapping(address => mapping(uint => bool)) public nested;
- function get(address _addr1, uint _i) public view returns (bool) {
- // You can get values from a nested mapping
- // even when it is not initialized
- return nested[_addr1][_i];
+ function get(address _addr1, uint _i) public view returns (bool) {
+ // You can get values from a nested mapping
+ // even when it is not initialized
+ return nested[_addr1][_i];
}
- function set(
+ function set(
address _addr1,
uint _i,
bool _boo
- ) public {
+ ) public {
nested[_addr1][_i] = _boo;
}
- function remove(address _addr1, uint _i) public {
- delete nested[_addr1][_i];
+ function remove(address _addr1, uint _i) public {
+ delete nested[_addr1][_i];
}
}
@@ -3102,7 +3098,7 @@ 第13节:Mapping
@@ -3188,14 +3184,6 @@ 第13节:Mapping
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/14_\346\225\260\347\273\204Array.html" "b/cn/01_solidity\345\237\272\347\241\200/14_\346\225\260\347\273\204Array.html"
index 5280a220..2b8dc059 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/14_\346\225\260\347\273\204Array.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/14_\346\225\260\347\273\204Array.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,127 +3025,127 @@ 第14节:数组
new uint256[] (len) 语法用于对memory进行修饰,storage不需要使用new
仅状态变量数组和storage支持动态扩容:push
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-contract Array {
- // Several ways to initialize an array
+contract Array {
+ // Several ways to initialize an array
uint[] public arr;
- uint[] public arr2 = [1, 2, 3];
- // Fixed sized array, all elements initialize to 0
- uint[10] public myFixedSizeArr;
+ uint[] public arr2 = [1, 2, 3];
+ // Fixed sized array, all elements initialize to 0
+ uint[10] public myFixedSizeArr;
- function get(uint i) public view returns (uint) {
- return arr[i];
+ function get(uint i) public view returns (uint) {
+ return arr[i];
}
- // Solidity can return the entire array.
- // But this function should be avoided for
- // arrays that can grow indefinitely in length.
- function getArr() public view returns (uint[] memory) {
- return arr;
+ // Solidity can return the entire array.
+ // But this function should be avoided for
+ // arrays that can grow indefinitely in length.
+ function getArr() public view returns (uint[] memory) {
+ return arr;
}
- function push(uint i) public {
- // Append to array
- // This will increase the array length by 1.
+ function push(uint i) public {
+ // Append to array
+ // This will increase the array length by 1.
arr.push(i);
}
- function pop() public {
- // Remove last element from array
- // This will decrease the array length by 1
+ function pop() public {
+ // Remove last element from array
+ // This will decrease the array length by 1
arr.pop();
}
- function getLength() public view returns (uint) {
- return arr.length;
+ function getLength() public view returns (uint) {
+ return arr.length;
}
- function remove(uint index) public {
- // Delete does not change the array length.
- // It resets the value at index to it's default value,
- // in this case 0
- delete arr[index];
+ function remove(uint index) public {
+ // Delete does not change the array length.
+ // It resets the value at index to it's default value,
+ // in this case 0
+ delete arr[index];
}
- function examples() pure external returns(uint[] memory) {
- // create array in memory, only fixed size can be created
- uint[] memory a = new uint[](5);
- return a;
+ function examples() pure external returns(uint[] memory) {
+ // create array in memory, only fixed size can be created
+ uint[] memory a = new uint[](5);
+ return a;
}
}
demo1:删除某个位置的元素,剩余元素向左移动(保留顺序)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract ArrayRemoveByShifting {
- // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
- // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
- // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
- // [1] -- remove(0) --> [1] --> []
+ // [1, 2, 3] -- remove(1) --> [1, 3, 3] --> [1, 3]
+ // [1, 2, 3, 4, 5, 6] -- remove(2) --> [1, 2, 4, 5, 6, 6] --> [1, 2, 4, 5, 6]
+ // [1, 2, 3, 4, 5, 6] -- remove(0) --> [2, 3, 4, 5, 6, 6] --> [2, 3, 4, 5, 6]
+ // [1] -- remove(0) --> [1] --> []
uint[] public arr;
- function remove(uint _index) public {
- require(_index < arr.length, "index out of bound");
+ function remove(uint _index) public {
+ require(_index < arr.length, "index out of bound");
- for (uint i = _index; i < arr.length - 1; i++) {
- arr[i] = arr[i + 1];
+ for (uint i = _index; i < arr.length - 1; i++) {
+ arr[i] = arr[i + 1];
}
arr.pop();
}
- function test() external {
- arr = [1, 2, 3, 4, 5];
- remove(2);
- // [1, 2, 4, 5]
- assert(arr[0] == 1);
- assert(arr[1] == 2);
- assert(arr[2] == 4);
- assert(arr[3] == 5);
- assert(arr.length == 4);
-
- arr = [1];
- remove(0);
- // []
- assert(arr.length == 0);
+ function test() external {
+ arr = [1, 2, 3, 4, 5];
+ remove(2);
+ // [1, 2, 4, 5]
+ assert(arr[0] == 1);
+ assert(arr[1] == 2);
+ assert(arr[2] == 4);
+ assert(arr[3] == 5);
+ assert(arr.length == 4);
+
+ arr = [1];
+ remove(0);
+ // []
+ assert(arr.length == 0);
}
}
demo2:删除某位置元素,使用最后一个元素填充(不考虑顺序)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract ArrayReplaceFromEnd {
uint[] public arr;
- // Deleting an element creates a gap in the array.
- // One trick to keep the array compact is to
- // move the last element into the place to delete.
- function remove(uint index) public {
- // Move the last element into the place to delete
- arr[index] = arr[arr.length - 1];
- // Remove the last element
+ // Deleting an element creates a gap in the array.
+ // One trick to keep the array compact is to
+ // move the last element into the place to delete.
+ function remove(uint index) public {
+ // Move the last element into the place to delete
+ arr[index] = arr[arr.length - 1];
+ // Remove the last element
arr.pop();
}
- function test() public {
- arr = [1, 2, 3, 4];
-
- remove(1);
- // [1, 4, 3]
- assert(arr.length == 3);
- assert(arr[0] == 1);
- assert(arr[1] == 4);
- assert(arr[2] == 3);
-
- remove(2);
- // [1, 4]
- assert(arr.length == 2);
- assert(arr[0] == 1);
- assert(arr[1] == 4);
+ function test() public {
+ arr = [1, 2, 3, 4];
+
+ remove(1);
+ // [1, 4, 3]
+ assert(arr.length == 3);
+ assert(arr[0] == 1);
+ assert(arr[1] == 4);
+ assert(arr[2] == 3);
+
+ remove(2);
+ // [1, 4]
+ assert(arr.length == 2);
+ assert(arr[0] == 1);
+ assert(arr[1] == 4);
}
}
@@ -3180,7 +3176,7 @@ 第14节:数组
@@ -3266,14 +3262,6 @@ 第14节:数组
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/15_\346\236\232\344\270\276Enum.html" "b/cn/01_solidity\345\237\272\347\241\200/15_\346\236\232\344\270\276Enum.html"
index 8e993878..c62b151e 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/15_\346\236\232\344\270\276Enum.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/15_\346\236\232\344\270\276Enum.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,11 +3019,11 @@ 第15节:枚举Enum
职场进阶: https://dukeweb3.com
枚举可以避免魔数,让程序更加易读,更好的进行状态管理,默认第一个值是:0
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Enum {
- // Enum representing shipping status
+ // Enum representing shipping status
enum Status {
Pending,
Shipped,
@@ -3036,33 +3032,33 @@ 第15节:枚举Enum
Canceled
}
- // Default value is the first element listed in
- // definition of the type, in this case "Pending"
+ // Default value is the first element listed in
+ // definition of the type, in this case "Pending"
Status public status;
- // Returns uint
- // Pending - 0
- // Shipped - 1
- // Accepted - 2
- // Rejected - 3
- // Canceled - 4
- function get() public view returns (Status) {
- return status;
+ // Returns uint
+ // Pending - 0
+ // Shipped - 1
+ // Accepted - 2
+ // Rejected - 3
+ // Canceled - 4
+ function get() public view returns (Status) {
+ return status;
}
- // Update status by passing uint into input
- function set(Status _status) public {
+ // Update status by passing uint into input
+ function set(Status _status) public {
status = _status;
}
- // You can update to a specific enum like this
- function cancel() public {
+ // You can update to a specific enum like this
+ function cancel() public {
status = Status.Canceled;
}
- // delete resets the enum to its first value, 0
- function reset() public {
- delete status;
+ // delete resets the enum to its first value, 0
+ function reset() public {
+ delete status;
}
}
@@ -3093,7 +3089,7 @@ 第15节:枚举Enum
@@ -3179,14 +3175,6 @@ 第15节:枚举Enum
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/16_\347\273\223\346\236\204\344\275\223Struct.html" "b/cn/01_solidity\345\237\272\347\241\200/16_\347\273\223\346\236\204\344\275\223Struct.html"
index 809508b6..4b7b20ae 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/16_\347\273\223\346\236\204\344\275\223Struct.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/16_\347\273\223\346\236\204\344\275\223Struct.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,8 +3020,8 @@ 第16节:结构体Struct
自定义结构类型,将不同的数据类型组合到一个结构中,目前支持参数传递结构体。
枚举和结构体都可以定义在另外一个文件中,进行import后使用
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Todos {
struct Todo {
@@ -3033,48 +3029,48 @@ 第16节:结构体Struct
bool completed;
}
- // An array of 'Todo' structs
+ // An array of 'Todo' structs
Todo[] public todos;
- function create(string memory _text) public {
- // 3 ways to initialize a struct
- // - calling it like a function
- todos.push(Todo(_text, false));
+ function create(string memory _text) public {
+ // 3 ways to initialize a struct
+ // - calling it like a function
+ todos.push(Todo(_text, false));
- // key value mapping
- todos.push(Todo({text: _text, completed: false}));
+ // key value mapping
+ todos.push(Todo({text: _text, completed: false}));
- // initialize an empty struct and then update it
+ // initialize an empty struct and then update it
Todo memory todo;
todo.text = _text;
- // todo.completed initialized to false
+ // todo.completed initialized to false
todos.push(todo);
}
- // Solidity automatically created a getter for 'todos' so
- // you don't actually need this function.
- function get(uint _index) public view returns (string memory text, bool completed) {
+ // Solidity automatically created a getter for 'todos' so
+ // you don't actually need this function.
+ function get(uint _index) public view returns (string memory text, bool completed) {
Todo storage todo = todos[_index];
- return (todo.text, todo.completed);
+ return (todo.text, todo.completed);
}
- // update text
- function update(uint _index, string memory _text) public {
+ // update text
+ function update(uint _index, string memory _text) public {
Todo storage todo = todos[_index];
todo.text = _text;
}
- // update completed
- function toggleCompleted(uint _index) public {
+ // update completed
+ function toggleCompleted(uint _index) public {
Todo storage todo = todos[_index];
todo.completed = !todo.completed;
}
}
在外面定义结构体:StructDeclaration.sol(说明并不是所有数据都必须写在合约之内的)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
struct Todo {
string text;
@@ -3082,13 +3078,13 @@ 第16节:结构体Struct
}
在主合约中引用:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-import "./StructDeclaration.sol";
+import "./StructDeclaration.sol";
contract Todos {
- // An array of 'Todo' structs
+ // An array of 'Todo' structs
Todo[] public todos;
}
@@ -3100,8 +3096,8 @@ 第16节:结构体Struct
示例:
函数原型:
-function createOffer(Token[] memory _tokens, GeneralInfo memory _general)
-
+function createOffer(Token[] memory _tokens, GeneralInfo memory _general)
+
结构定义:
enum TokenType {
ERC20,
@@ -3118,19 +3114,19 @@ 第16节:结构体Struct
}
struct GeneralInfo {
- address loanToken; //token to borrow
- uint256 ltv; //8000 means 80%
- bool featuredFlag; //true, false
- uint256 loanAmount; //amounts to borrow
- uint256 interestRate; //1100 means 11%
- uint256 collateralThreshold; //8000 means 80%
- uint256 repaymentDate; //timestamp + 30days
- uint256 offerAvailable; //timestamp + 7days
+ address loanToken; //token to borrow
+ uint256 ltv; //8000 means 80%
+ bool featuredFlag; //true, false
+ uint256 loanAmount; //amounts to borrow
+ uint256 interestRate; //1100 means 11%
+ uint256 collateralThreshold; //8000 means 80%
+ uint256 repaymentDate; //timestamp + 30days
+ uint256 offerAvailable; //timestamp + 7days
}
正确传递参数方式如下:
-tokens: [[0, '0x749B1c911170A5aFEb68d4B278cD5405C718fc7F',1000,0,0]],
-general: ["0x749B1c911170A5aFEb68d4B278cD5405C718fc7F", 8000, false, 1000, 1100, 8000, 10000, 10000]
+tokens: [[0, '0x749B1c911170A5aFEb68d4B278cD5405C718fc7F',1000,0,0]],
+general: ["0x749B1c911170A5aFEb68d4B278cD5405C718fc7F", 8000, false, 1000, 1100, 8000, 10000, 10000]
@@ -3161,7 +3157,7 @@ 第16节:结构体Struct
@@ -3247,14 +3243,6 @@ 第16节:结构体Struct
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/17_memory_storage_calldata.html" "b/cn/01_solidity\345\237\272\347\241\200/17_memory_storage_calldata.html"
index 2949f728..7ca6e007 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/17_memory_storage_calldata.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/17_memory_storage_calldata.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3034,69 +3030,69 @@ 第17节:存储
其他:Solidity 变量中 memory 、calldata 2 个表示作用非常类似,都是函数内部临时变量,它们最大的区别就是 calldata 是不可修改的,在某些只读的情况比较省 Gas.
局部变量(此处指引用类型)默认是Storage类型的,只能将使用storage类型赋值,不能使用memory类型来赋值。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract DataLocations {
- uint[] public arr = [1, 2, 3];
- mapping(uint => address) public map;
+ uint[] public arr = [1, 2, 3];
+ mapping(uint => address) public map;
struct MyStruct {
uint foo;
}
- mapping(uint => MyStruct) public myStructs;
+ mapping(uint => MyStruct) public myStructs;
- function test() public {
- // get a struct from a mapping
- MyStruct storage myStruct = myStructs[1];
+ function test() public {
+ // get a struct from a mapping
+ MyStruct storage myStruct = myStructs[1];
- // create a struct in memory
- MyStruct memory myMemStruct = MyStruct(0);
+ // create a struct in memory
+ MyStruct memory myMemStruct = MyStruct(0);
- // call _f with state variables
+ // call _f with state variables
_f(arr, map, myStruct);
- //invalid convertion, failed to call
- // _f(arr, map, myMemStruct);
+ //invalid convertion, failed to call
+ // _f(arr, map, myMemStruct);
_g(arr);
- this._h(arr);
+ this._h(arr);
}
- function _f(
+ function _f(
uint[] storage _arr,
mapping(uint => address) storage _map,
MyStruct storage _myStruct
- ) internal {
- // do something with storage variables
- _arr.push(100);
- _map[20] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
- _myStruct.foo = 20;
+ ) internal {
+ // do something with storage variables
+ _arr.push(100);
+ _map[20] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
+ _myStruct.foo = 20;
}
- // You can return memory variables
- function _g(uint[] memory _arr) public returns (uint[] memory) {
- // do something with memory array
- _arr[0] = 100;
+ // You can return memory variables
+ function _g(uint[] memory _arr) public returns (uint[] memory) {
+ // do something with memory array
+ _arr[0] = 100;
}
- function _h(uint[] calldata _arr) external {
- // do something with calldata array
- // calldata is read-only
- // _arr[2] = 200;
+ function _h(uint[] calldata _arr) external {
+ // do something with calldata array
+ // calldata is read-only
+ // _arr[2] = 200;
}
}
memory和storage进阶
-// SPDX-License-Identifier: MIT
-pragma solidity =0.8.10;
+// SPDX-License-Identifier: MIT
+pragma solidity =0.8.10;
-import "hardhat/console.sol";
+import "hardhat/console.sol";
contract Test{
- constructor() {
- personArrayGlobal.push(Person("Lily", 20, true));
- personArrayGlobal.push(Person("James", 30, false));
+ constructor() {
+ personArrayGlobal.push(Person("Lily", 20, true));
+ personArrayGlobal.push(Person("James", 30, false));
}
struct Person {
@@ -3107,65 +3103,65 @@ memory和storage进阶
Person[] public personArrayGlobal;
- // remix: [["Lily", 20, true]]
- function changeTestMemory(Person[] memory _psersonArray) public {
- Person memory pTmp = _psersonArray[0];
+ // remix: [["Lily", 20, true]]
+ function changeTestMemory(Person[] memory _psersonArray) public {
+ Person memory pTmp = _psersonArray[0];
- // error, 不能基于memory对象创建storage对象
- // Person storage pTmp = _psersonArray[0];
+ // error, 不能基于memory对象创建storage对象
+ // Person storage pTmp = _psersonArray[0];
_innerChangeMemory(pTmp);
- console.log(pTmp.name); // David memory
- console.log(_psersonArray[0].name); // David memory,居然改变了!虽然在memory中,但是实际上也是传递的指针
+ console.log(pTmp.name); // David memory
+ console.log(_psersonArray[0].name); // David memory,居然改变了!虽然在memory中,但是实际上也是传递的指针
- uint256 tmpInt = 200;
+ uint256 tmpInt = 200;
_innerChangeInt(tmpInt);
- console.log(tmpInt); // 200,指类型的变量,总是直接复制一份
+ console.log(tmpInt); // 200,指类型的变量,总是直接复制一份
}
- function _innerChangeInt(uint _newValue) internal pure {
- _newValue = 100;
+ function _innerChangeInt(uint _newValue) internal pure {
+ _newValue = 100;
}
- function _innerChangeMemory(Person memory _p) internal pure {
- _p.name = "David memory";
- _p.age = 30;
- _p.married = false;
+ function _innerChangeMemory(Person memory _p) internal pure {
+ _p.name = "David memory";
+ _p.age = 30;
+ _p.married = false;
}
- function _innerChangeStorage(Person storage _p) internal {
- _p.name = "David Storage";
- _p.age = 30;
- _p.married = false;
+ function _innerChangeStorage(Person storage _p) internal {
+ _p.name = "David Storage";
+ _p.age = 30;
+ _p.married = false;
}
- // run before changeTestGlobalWithStorage
- function changeTestGlobalWithMemory() public {
- Person memory pTmp = personArrayGlobal[0];
+ // run before changeTestGlobalWithStorage
+ function changeTestGlobalWithMemory() public {
+ Person memory pTmp = personArrayGlobal[0];
_innerChangeMemory(pTmp);
- // error,memory 不能赋值给storage
- // _innerChangeStorage(pTmp);
+ // error,memory 不能赋值给storage
+ // _innerChangeStorage(pTmp);
- console.log(pTmp.name); // David memory,memory中的变量改变了
- console.log(personArrayGlobal[0].name); // Lily,原storage中数据未改变
+ console.log(pTmp.name); // David memory,memory中的变量改变了
+ console.log(personArrayGlobal[0].name); // Lily,原storage中数据未改变
}
- // run after changeTestGlobalWithMemory
- function changeTestGlobalWithStorage() public {
- Person storage pTmp = personArrayGlobal[0];
+ // run after changeTestGlobalWithMemory
+ function changeTestGlobalWithStorage() public {
+ Person storage pTmp = personArrayGlobal[0];
- // storage 赋值给memory,完全拷贝
+ // storage 赋值给memory,完全拷贝
_innerChangeMemory(pTmp);
- console.log(pTmp.name); // Lily
- console.log(personArrayGlobal[0].name); // Lily
+ console.log(pTmp.name); // Lily
+ console.log(personArrayGlobal[0].name); // Lily
- // storage 赋值给storage,指针传递
+ // storage 赋值给storage,指针传递
_innerChangeStorage(pTmp);
- console.log(pTmp.name); // David
- console.log(personArrayGlobal[0].name); // David
+ console.log(pTmp.name); // David
+ console.log(personArrayGlobal[0].name); // David
}
}
@@ -3197,7 +3193,7 @@ memory和storage进阶
@@ -3283,14 +3279,6 @@ memory和storage进阶
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/18_\345\207\275\346\225\260Function.html" "b/cn/01_solidity\345\237\272\347\241\200/18_\345\207\275\346\225\260Function.html"
index 139e8ead..e46572db 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/18_\345\207\275\346\225\260Function.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/18_\345\207\275\346\225\260Function.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,83 +3024,83 @@ 第18节:函数Function
可以解构返回值,对忽略的返回值直接使用逗号留空
map无法作为参数或返回值
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
-
-contract Function {
- // Functions can return multiple values.
- function returnMany()
- public
- pure
- returns (
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+contract Function {
+ // Functions can return multiple values.
+ function returnMany()
+ public
+ pure
+ returns (
uint,
bool,
uint
- )
- {
- return (1, true, 2);
+ )
+ {
+ return (1, true, 2);
}
- // Return values can be named.
- function named()
- public
- pure
- returns (
+ // Return values can be named.
+ function named()
+ public
+ pure
+ returns (
uint x,
bool b,
uint y
- )
- {
- return (1, true, 2);
+ )
+ {
+ return (1, true, 2);
}
- // Return values can be assigned to their name.
- // In this case the return statement can be omitted.
- function assigned()
- public
- pure
- returns (
+ // Return values can be assigned to their name.
+ // In this case the return statement can be omitted.
+ function assigned()
+ public
+ pure
+ returns (
uint x,
bool b,
uint y
- )
- {
- x = 1;
- b = true;
- y = 2;
+ )
+ {
+ x = 1;
+ b = true;
+ y = 2;
}
- // Use destructuring assignment when calling another
- // function that returns multiple values.
- function destructuringAssignments()
- public
- pure
- returns (
+ // Use destructuring assignment when calling another
+ // function that returns multiple values.
+ function destructuringAssignments()
+ public
+ pure
+ returns (
uint,
bool,
uint,
uint,
uint
- )
- {
+ )
+ {
(uint i, bool b, uint j) = returnMany();
- // Values can be left out.
- (uint x, , uint y) = (4, 5, 6);
+ // Values can be left out.
+ (uint x, , uint y) = (4, 5, 6);
- return (i, b, j, x, y);
+ return (i, b, j, x, y);
}
- // Cannot use map for either input or output
+ // Cannot use map for either input or output
- // Can use array for input
- function arrayInput(uint[] memory _arr) public {}
+ // Can use array for input
+ function arrayInput(uint[] memory _arr) public {}
- // Can use array for output
+ // Can use array for output
uint[] public arr;
- function arrayOutput() public view returns (uint[] memory) {
- return arr;
+ function arrayOutput() public view returns (uint[] memory) {
+ return arr;
}
}
@@ -3135,7 +3131,7 @@ 第18节:函数Function
@@ -3221,14 +3217,6 @@ 第18节:函数Function
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/19_view\345\222\214pure.html" "b/cn/01_solidity\345\237\272\347\241\200/19_view\345\222\214pure.html"
index 1a5df6f2..6fb35428 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/19_view\345\222\214pure.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/19_view\345\222\214pure.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,20 +3023,20 @@ 第19节:view和pure
view:表示函数中不会修改状态变量,只是读取;
pure:表示函数中不会使用状态变量,既不修改也不读取。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract ViewAndPure {
- uint public x = 1;
+ uint public x = 1;
- // Promise not to modify the state.
- function addToX(uint y) public view returns (uint) {
- return x + y;
+ // Promise not to modify the state.
+ function addToX(uint y) public view returns (uint) {
+ return x + y;
}
- // Promise not to modify or read from the state.
- function add(uint i, uint j) public pure returns (uint) {
- return i + j;
+ // Promise not to modify or read from the state.
+ function add(uint i, uint j) public pure returns (uint) {
+ return i + j;
}
}
@@ -3071,7 +3067,7 @@ 第19节:view和pure
@@ -3157,14 +3153,6 @@ 第19节:view和pure
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/20_error.html" "b/cn/01_solidity\345\237\272\347\241\200/20_error.html"
index 8a38826c..81a4dede 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/20_error.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/20_error.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3046,50 +3042,78 @@ 第20节:错误Error
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
+
+contract Error {
+ function testRequire(uint _i) public pure {
+ // 期待_i > 10,如果i <= 10,则会抛出错误
+ require(_i > 10, "Input must be greater than 10");
+ }
-contract Error {
- function testRequire(uint _i) public pure {
- // 期待_i > 10,如果i <= 10,则会抛出错误 require(_i> 10, "Input must be greater than 10");
+ function testRevert(uint _i) public pure {
+ // 如果校验条件过于复杂,则可以使用revert
+ if (_i <= 10) {
+ revert("Input must be greater than 10");
+ }
}
- function testRevert(uint _i) public pure {
- // 如果校验条件过于复杂,则可以使用revert
- if (_i =>
+ uint public num;
+
+ function testAssert() public view {
+ // assert用于校验不可变量,一般用于校验内部错误
+ // num == 0 为true时继续向下执行
+ // 不提供错误信息
+ assert(num == 0);
+ }
+
+ // custom error
+ error InsufficientBalance(uint balance, uint withdrawAmount);
+
+ function testCustomError(uint _withdrawAmount) public view {
+ uint bal = address(this).balance;
+ if (bal < _withdrawAmount) {
+ revert InsufficientBalance({balance: bal, withdrawAmount: _withdrawAmount});
+ }
+ }
+}
+
另一个示例:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Account {
uint public balance;
- uint public constant MAX_UINT = 2**256 - 1;
+ uint public constant MAX_UINT = 2**256 - 1;
- function deposit(uint _amount) public {
+ function deposit(uint _amount) public {
uint oldBalance = balance;
uint newBalance = balance + _amount;
- // balance + _amount does not overflow if balance + _amount >= balance
- require(newBalance >= oldBalance, "Overflow");
+ // balance + _amount does not overflow if balance + _amount >= balance
+ require(newBalance >= oldBalance, "Overflow");
balance = newBalance;
assert(balance >= oldBalance);
}
- function withdraw(uint _amount) public {
+ function withdraw(uint _amount) public {
uint oldBalance = balance;
- // balance - _amount does not underflow if balance >= _amount
- require(balance >= _amount, "Underflow");
+ // balance - _amount does not underflow if balance >= _amount
+ require(balance >= _amount, "Underflow");
- if (balance < _amount) {
- revert("Underflow");
+ if (balance < _amount) {
+ revert("Underflow");
}
balance -= _amount;
- assert(balance
+ assert(balance <= oldBalance);
+ }
+}
+
@@ -3117,7 +3141,7 @@ 第20节:错误Error
@@ -3203,14 +3227,6 @@ 第20节:错误Error
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/21_\344\277\256\351\245\260\345\231\250modifier.html" "b/cn/01_solidity\345\237\272\347\241\200/21_\344\277\256\351\245\260\345\231\250modifier.html"
index 96b5af88..f1259deb 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/21_\344\277\256\351\245\260\345\231\250modifier.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/21_\344\277\256\351\245\260\345\231\250modifier.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,56 +3024,56 @@ 第21节:修饰器modifier
参数校验
防止重入攻击等
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract FunctionModifier {
- // We will use these variables to demonstrate how to use modifiers.
+ // We will use these variables to demonstrate how to use modifiers.
address public owner;
- uint public x = 10;
+ uint public x = 10;
bool public locked;
- constructor() {
- // Set the transaction sender as the owner of the contract.
+ constructor() {
+ // Set the transaction sender as the owner of the contract.
owner = msg.sender;
}
- // 1. Modifier to check that the caller is the owner of the contract.
- modifier onlyOwner() {
- require(msg.sender == owner, "Not owner");
- // Underscore is a special character only used inside
- // a function modifier and it tells Solidity to
- // execute the rest of the code.
+ // 1. Modifier to check that the caller is the owner of the contract.
+ modifier onlyOwner() {
+ require(msg.sender == owner, "Not owner");
+ // Underscore is a special character only used inside
+ // a function modifier and it tells Solidity to
+ // execute the rest of the code.
_;
}
- // 2. Modifiers can take inputs. This modifier checks that the
- // address passed in is not the zero address.
- modifier validAddress(address _addr) {
- require(_addr != address(0), "Not valid address");
+ // 2. Modifiers can take inputs. This modifier checks that the
+ // address passed in is not the zero address.
+ modifier validAddress(address _addr) {
+ require(_addr != address(0), "Not valid address");
_;
}
- function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
+ function changeOwner(address _newOwner) public onlyOwner validAddress(_newOwner) {
owner = _newOwner;
}
- // Modifiers can be called before and / or after a function.
- // This modifier prevents a function from being called while
- // it is still executing.
- modifier noReentrancy() {
- require(!locked, "No reentrancy");
+ // Modifiers can be called before and / or after a function.
+ // This modifier prevents a function from being called while
+ // it is still executing.
+ modifier noReentrancy() {
+ require(!locked, "No reentrancy");
- locked = true;
+ locked = true;
_;
- locked = false;
+ locked = false;
}
- function decrement(uint i) public noReentrancy {
+ function decrement(uint i) public noReentrancy {
x -= i;
- if (i > 1) {
- decrement(i - 1);
+ if (i > 1) {
+ decrement(i - 1);
}
}
}
@@ -3109,7 +3105,7 @@ 第21节:修饰器modifier
@@ -3195,14 +3191,6 @@ 第21节:修饰器modifier
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/22_\344\272\213\344\273\266Event.html" "b/cn/01_solidity\345\237\272\347\241\200/22_\344\272\213\344\273\266Event.html"
index ba6cc0d4..9178f0b3 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/22_\344\272\213\344\273\266Event.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/22_\344\272\213\344\273\266Event.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3033,20 +3029,20 @@ 第22节:事件Event
subgraph对合约事件进行监听,计算(如:统计用户数量)
前端程序直接访问subgraph的服务,获得统计数据(这避免了在合约层面统计数据的费用,并且获取速度更快)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Event {
- // Event declaration
- // Up to 3 parameters can be indexed.
- // Indexed parameters helps you filter the logs by the indexed parameter
- event Log(address indexed sender, string message); // 修饰为indexed
- event AnotherLog(); // 无参数的事件
- event TestAnonymous(address indexed sender, uint256 num) anonymous; // 匿名事件
-
- function test() public {
- emit Log(msg.sender, "Hello World!");
- emit Log(msg.sender, "Hello EVM!");
+ // Event declaration
+ // Up to 3 parameters can be indexed.
+ // Indexed parameters helps you filter the logs by the indexed parameter
+ event Log(address indexed sender, string message); // 修饰为indexed
+ event AnotherLog(); // 无参数的事件
+ event TestAnonymous(address indexed sender, uint256 num) anonymous; // 匿名事件
+
+ function test() public {
+ emit Log(msg.sender, "Hello World!");
+ emit Log(msg.sender, "Hello EVM!");
emit AnotherLog();
}
}
@@ -3099,7 +3095,7 @@ 第22节:事件Event
@@ -3185,14 +3181,6 @@ 第22节:事件Event
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/23_\347\273\247\346\211\277\347\232\204\346\226\271\346\263\225_\351\207\215\345\206\231.html" "b/cn/01_solidity\345\237\272\347\241\200/23_\347\273\247\346\211\277\347\232\204\346\226\271\346\263\225_\351\207\215\345\206\231.html"
index 6cd29e2a..23c5181e 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/23_\347\273\247\346\211\277\347\232\204\346\226\271\346\263\225_\351\207\215\345\206\231.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/23_\347\273\247\346\211\277\347\232\204\346\226\271\346\263\225_\351\207\215\345\206\231.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,72 +3026,72 @@ 第23节:Inheritance、vi
继承的顺序很重要,遵循最远继承,即后面继承的合约会覆盖前面父合约的方法
super会调用继承链条上的每一个合约的相关函数,而不仅仅是最近的父合约
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.10;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.10;
-/* Inheritance tree
+/* Inheritance tree
A
/ \
B C
\ /
D
-*/
+*/
contract A {
- // This is called an event. You can emit events from your function
- // and they are logged into the transaction log.
- // In our case, this will be useful for tracing function calls.
+ // This is called an event. You can emit events from your function
+ // and they are logged into the transaction log.
+ // In our case, this will be useful for tracing function calls.
event Log(string message);
- function foo() public virtual {
- emit Log("A.foo called");
+ function foo() public virtual {
+ emit Log("A.foo called");
}
- function bar() public virtual {
- emit Log("A.bar called");
+ function bar() public virtual {
+ emit Log("A.bar called");
}
}
contract B is A {
- function foo() public virtual override {
- emit Log("B.foo called");
+ function foo() public virtual override {
+ emit Log("B.foo called");
A.foo();
}
- function bar() public virtual override {
- emit Log("B.bar called");
- super.bar();
+ function bar() public virtual override {
+ emit Log("B.bar called");
+ super.bar();
}
}
contract C is A {
- function foo() public virtual override {
- emit Log("C.foo called");
+ function foo() public virtual override {
+ emit Log("C.foo called");
A.foo();
}
- function bar() public virtual override {
- emit Log("C.bar called");
- super.bar();
+ function bar() public virtual override {
+ emit Log("C.bar called");
+ super.bar();
}
}
contract D is B, C {
- // Try:
- // - Call D.foo and check the transaction logs.
- // Although D inherits A, B and C, it only called C and then A.
+ // Try:
+ // - Call D.foo and check the transaction logs.
+ // Although D inherits A, B and C, it only called C and then A.
- function foo() public override(B, C) {
- super.foo();
+ function foo() public override(B, C) {
+ super.foo();
}
- // Try:
- // - Call D.bar and check the transaction logs
- // D called C, then B, and finally A.
- // Although super was called twice (by B and C) it only called A once.
- function bar() public override(B, C) {
- super.bar();
+ // Try:
+ // - Call D.bar and check the transaction logs
+ // D called C, then B, and finally A.
+ // Although super was called twice (by B and C) it only called A once.
+ function bar() public override(B, C) {
+ super.bar();
}
}
@@ -3132,7 +3128,7 @@ 第23节:Inheritance、vi
@@ -3218,14 +3214,6 @@ 第23节:Inheritance、vi
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/24_\347\273\247\346\211\277\347\212\266\346\200\201\345\217\230\351\207\217_\350\246\206\347\233\226.html" "b/cn/01_solidity\345\237\272\347\241\200/24_\347\273\247\346\211\277\347\212\266\346\200\201\345\217\230\351\207\217_\350\246\206\347\233\226.html"
index 887e2cea..00433051 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/24_\347\273\247\346\211\277\347\212\266\346\200\201\345\217\230\351\207\217_\350\246\206\347\233\226.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/24_\347\273\247\346\211\277\347\212\266\346\200\201\345\217\230\351\207\217_\350\246\206\347\233\226.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,30 +3022,30 @@ 第24节:继承
状态变量无法进行重写,只能继承父类状态变量
但是可以通过在子合约的构造函数中进行覆盖,从而达到重写目的
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract A {
- string public name = "Contract A";
+ string public name = "Contract A";
- function getName() public view returns (string memory) {
- return name;
+ function getName() public view returns (string memory) {
+ return name;
}
}
-// Shadowing is disallowed in Solidity 0.6
-// This will not compile
-// contract B is A {
-// string public name = "Contract B";
-// }
+// Shadowing is disallowed in Solidity 0.6
+// This will not compile
+// contract B is A {
+// string public name = "Contract B";
+// }
contract C is A {
- // This is the correct way to override inherited state variables.
- constructor() {
- name = "Contract C";
+ // This is the correct way to override inherited state variables.
+ constructor() {
+ name = "Contract C";
}
- // C.getName returns "Contract C"
+ // C.getName returns "Contract C"
}
@@ -3079,7 +3075,7 @@ 第24节:继承
@@ -3165,14 +3161,6 @@ 第24节:继承
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/25_\346\236\204\351\200\240\345\207\275\346\225\260.html" "b/cn/01_solidity\345\237\272\347\241\200/25_\346\236\204\351\200\240\345\207\275\346\225\260.html"
index 96b89258..b8acfbbb 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/25_\346\236\204\351\200\240\345\207\275\346\225\260.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/25_\346\236\204\351\200\240\345\207\275\346\225\260.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,59 +3022,59 @@ 第25节:构造函数constructor
构造函数是可选的,在部署合约时会自动被调用
在合约继承的时候,如果父合约有构造函数,则需要显示的对父合约进行构造
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// Base contract X
+// Base contract X
contract X {
string public name;
- constructor(string memory _name) {
+ constructor(string memory _name) {
name = _name;
}
}
-// Base contract Y
+// Base contract Y
contract Y {
string public text;
- constructor(string memory _text) {
+ constructor(string memory _text) {
text = _text;
}
}
-// There are 2 ways to initialize parent contract with parameters.
+// There are 2 ways to initialize parent contract with parameters.
-// 1. Pass the parameters here in the inheritance list.
-contract B is X("Input to X"), Y("Input to Y") {
+// 1. Pass the parameters here in the inheritance list.
+contract B is X("Input to X"), Y("Input to Y") {
}
contract C is X, Y {
- // 2. Pass the parameters here in the constructor,
- // similar to function modifiers.
- constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
+ // 2. Pass the parameters here in the constructor,
+ // similar to function modifiers.
+ constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}
-// Parent constructors are always called in the order of inheritance
-// regardless of the order of parent contracts listed in the
-// constructor of the child contract.
+// Parent constructors are always called in the order of inheritance
+// regardless of the order of parent contracts listed in the
+// constructor of the child contract.
-// 构造顺序取决于继承顺序(由左至右),而不是实例化顺序
-// Order of constructors called:
-// 1. X
-// 2. Y
-// 3. D
+// 构造顺序取决于继承顺序(由左至右),而不是实例化顺序
+// Order of constructors called:
+// 1. X
+// 2. Y
+// 3. D
contract D is X, Y {
- constructor() X("X was called") Y("Y was called") {}
+ constructor() X("X was called") Y("Y was called") {}
}
-// Order of constructors called:
-// 1. X
-// 2. Y
-// 3. E
+// Order of constructors called:
+// 1. X
+// 2. Y
+// 3. E
contract E is X, Y {
- constructor() Y("Y was called") X("X was called") {}
+ constructor() Y("Y was called") X("X was called") {}
}
@@ -3108,7 +3104,7 @@ 第25节:构造函数constructor
@@ -3194,14 +3190,6 @@ 第25节:构造函数constructor
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/26_\345\217\257\350\247\201\346\200\247visibility.html" "b/cn/01_solidity\345\237\272\347\241\200/26_\345\217\257\350\247\201\346\200\247visibility.html"
index 0cec78a4..26a59108 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/26_\345\217\257\350\247\201\346\200\247visibility.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/26_\345\217\257\350\247\201\346\200\247visibility.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,70 +3026,70 @@ 第26节:可见性visibility
external:仅允许外部地址(EOA或CA)调用,合约内部及子合约都不能调用;(早期版本可以使用this调用external方法)
另,状态变量可以被修饰为:publi, private, internal,但是无法修饰为external
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Base {
- // Private function can only be called
- // - inside this contract
- // Contracts that inherit this contract cannot call this function.
- function privateFunc() private pure returns (string memory) {
- return "private function called";
+ // Private function can only be called
+ // - inside this contract
+ // Contracts that inherit this contract cannot call this function.
+ function privateFunc() private pure returns (string memory) {
+ return "private function called";
}
- function testPrivateFunc() public pure returns (string memory) {
- return privateFunc();
+ function testPrivateFunc() public pure returns (string memory) {
+ return privateFunc();
}
- // Internal function can be called
- // - inside this contract
- // - inside contracts that inherit this contract
- function internalFunc() internal pure returns (string memory) {
- return "internal function called";
+ // Internal function can be called
+ // - inside this contract
+ // - inside contracts that inherit this contract
+ function internalFunc() internal pure returns (string memory) {
+ return "internal function called";
}
- function testInternalFunc() public pure virtual returns (string memory) {
- return internalFunc();
+ function testInternalFunc() public pure virtual returns (string memory) {
+ return internalFunc();
}
- // Public functions can be called
- // - inside this contract
- // - inside contracts that inherit this contract
- // - by other contracts and accounts
- function publicFunc() public pure returns (string memory) {
- return "public function called";
+ // Public functions can be called
+ // - inside this contract
+ // - inside contracts that inherit this contract
+ // - by other contracts and accounts
+ function publicFunc() public pure returns (string memory) {
+ return "public function called";
}
- // External functions can only be called
- // - by other contracts and accounts
- function externalFunc() external pure returns (string memory) {
- return "external function called";
+ // External functions can only be called
+ // - by other contracts and accounts
+ function externalFunc() external pure returns (string memory) {
+ return "external function called";
}
- // This function will not compile since we're trying to call
- // an external function here.
- // function testExternalFunc() public pure returns (string memory) {
- // return externalFunc();
- // }
-
- // State variables
- string private privateVar = "my private variable";
- string internal internalVar = "my internal variable";
- string public publicVar = "my public variable";
- // State variables cannot be external so this code won't compile.
- // string external externalVar = "my external variable";
+ // This function will not compile since we're trying to call
+ // an external function here.
+ // function testExternalFunc() public pure returns (string memory) {
+ // return externalFunc();
+ // }
+
+ // State variables
+ string private privateVar = "my private variable";
+ string internal internalVar = "my internal variable";
+ string public publicVar = "my public variable";
+ // State variables cannot be external so this code won't compile.
+ // string external externalVar = "my external variable";
}
contract Child is Base {
- // Inherited contracts do not have access to private functions
- // and state variables.
- // function testPrivateFunc() public pure returns (string memory) {
- // return privateFunc();
- // }
-
- // Internal function call be called inside child contracts.
- function testInternalFunc() public pure override returns (string memory) {
- return internalFunc();
+ // Inherited contracts do not have access to private functions
+ // and state variables.
+ // function testPrivateFunc() public pure returns (string memory) {
+ // return privateFunc();
+ // }
+
+ // Internal function call be called inside child contracts.
+ function testInternalFunc() public pure override returns (string memory) {
+ return internalFunc();
}
}
@@ -3124,7 +3120,7 @@ 第26节:可见性visibility
@@ -3210,14 +3206,6 @@ 第26节:可见性visibility
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/27_abstract.html" "b/cn/01_solidity\345\237\272\347\241\200/27_abstract.html"
index 8fe67c8c..d7e375e0 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/27_abstract.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/27_abstract.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,8 +3025,39 @@ 第27节:abstract
abstract合约中未实现的函数必须在子合约中实现,即所有在abstract中定义的函数都必须有实现;
abstract合约不能单独部署,必须被继承后才能部署;
-// SPDX-License-Identifier: GPL-3.0
-pragma solidity >=0.6.0
+// SPDX-License-Identifier: GPL-3.0
+pragma solidity >=0.6.0 <0.9.0;
+
+abstract contract Animal {
+ string public species;
+ constructor(string memory _base) {
+ species = _base;
+ }
+}
+
+abstract contract Feline {
+ uint public num;
+ function utterance() public pure virtual returns (bytes32);
+
+ function base(uint _num) public returns(uint, string memory) {
+ num = _num;
+ return (num, "hello world!");
+ }
+}
+
+// 由于Animal中的构造函数没有进行初始化,所以必须修饰为abstract
+abstract contract Cat1 is Feline, Animal {
+ function utterance() public pure override returns (bytes32) {
+ return "miaow";
+ }
+}
+
+contract Cat2 is Feline, Animal("Animal") {
+ function utterance() public pure override returns (bytes32) {
+ return "miaow";
+ }
+}
+
手册:https://docs.soliditylang.org/en/v0.8.14/contracts.html?highlight=abstract#abstract-contracts
@@ -3059,7 +3086,7 @@ 第27节:abstract
@@ -3145,14 +3172,6 @@ 第27节:abstract
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/28_interface.html" "b/cn/01_solidity\345\237\272\347\241\200/28_interface.html"
index 67c6364e..336bb2e9 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/28_interface.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/28_interface.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3031,64 +3027,64 @@ 第28节:Interface
接口中不能定义状态变量。
abstract和interface的区别
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Counter {
uint public count;
- function increment() external {
- count += 1;
+ function increment() external {
+ count += 1;
}
}
interface IBase {
- function count() external view returns (uint);
+ function count() external view returns (uint);
}
interface ICounter is IBase {
- function increment() external;
+ function increment() external;
}
contract MyContract {
- function incrementCounter(address _counter) external {
+ function incrementCounter(address _counter) external {
ICounter(_counter).increment();
}
- function getCount(address _counter) external view returns (uint) {
- return ICounter(_counter).count();
+ function getCount(address _counter) external view returns (uint) {
+ return ICounter(_counter).count();
}
}
uniswap demo:
-// Uniswap example
+// Uniswap example
interface UniswapV2Factory {
- function getPair(address tokenA, address tokenB)
- external
- view
- returns (address pair);
+ function getPair(address tokenA, address tokenB)
+ external
+ view
+ returns (address pair);
}
interface UniswapV2Pair {
- function getReserves()
- external
- view
- returns (
+ function getReserves()
+ external
+ view
+ returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
- );
+ );
}
contract UniswapExample {
- address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
- address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
- address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
+ address private factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
+ address private dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
+ address private weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
- function getTokenReserves() external view returns (uint, uint) {
+ function getTokenReserves() external view returns (uint, uint) {
address pair = UniswapV2Factory(factory).getPair(dai, weth);
(uint reserve0, uint reserve1, ) = UniswapV2Pair(pair).getReserves();
- return (reserve0, reserve1);
+ return (reserve0, reserve1);
}
}
@@ -3119,7 +3115,7 @@ 第28节:Interface
@@ -3205,14 +3201,6 @@ 第28节:Interface
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/29_library.html" "b/cn/01_solidity\345\237\272\347\241\200/29_library.html"
index f0e2457a..cb1f8f57 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/29_library.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/29_library.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3033,80 +3029,80 @@ 第29节:library
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// 1. 只有internal方法,会内嵌到调用合约中
+// 1. 只有internal方法,会内嵌到调用合约中
library SafeMath {
- function add(uint x, uint y) internal pure returns (uint) {
+ function add(uint x, uint y) internal pure returns (uint) {
uint z = x + y;
- require(z >= x, "uint overflow");
+ require(z >= x, "uint overflow");
- return z;
+ return z;
}
}
-library Math {
- function sqrt(uint y) internal pure returns (uint z) {
- if (y > 3) {
+library Math {
+ function sqrt(uint y) internal pure returns (uint z) {
+ if (y > 3) {
z = y;
- uint x = y / 2 + 1;
- while (x < z) {
+ uint x = y / 2 + 1;
+ while (x < z) {
z = x;
- x = (y / x + x) / 2;
+ x = (y / x + x) / 2;
}
- } else if (y != 0) {
- z = 1;
+ } else if (y != 0) {
+ z = 1;
}
- // else z = 0 (default value)
+ // else z = 0 (default value)
}
}
contract TestSafeMath {
- // 对uint类型增加SafeMath的方法,
- // 1. 后续定义的uint变量就会自动绑定SafeMath提供的方法: uint x;
- // 2. 这个变量会作为第一个参数传递给函数: x.add(y);
- using SafeMath for uint;
+ // 对uint类型增加SafeMath的方法,
+ // 1. 后续定义的uint变量就会自动绑定SafeMath提供的方法: uint x;
+ // 2. 这个变量会作为第一个参数传递给函数: x.add(y);
+ using SafeMath for uint;
- uint public MAX_UINT = 2**256 - 1;
+ uint public MAX_UINT = 2**256 - 1;
- // 用法1:x.方法(y)
- function testAdd(uint x, uint y) public pure returns (uint) {
- return x.add(y);
+ // 用法1:x.方法(y)
+ function testAdd(uint x, uint y) public pure returns (uint) {
+ return x.add(y);
}
- // 用法2:库.方法(x)
- function testSquareRoot(uint x) public pure returns (uint) {
- return Math.sqrt(x);
+ // 用法2:库.方法(x)
+ function testSquareRoot(uint x) public pure returns (uint) {
+ return Math.sqrt(x);
}
}
-// 2. 存在public方法时,会单独部署库合约,并且第一个参数是状态变量类型
-library Array {
- // 修改调用者状态变量的方式,第一个参数是状态变量本身
- function remove(uint[] storage arr, uint index) public {
- // Move the last element into the place to delete
- require(arr.length > 0, "Can't remove from empty array");
- arr[index] = arr[arr.length - 1];
+// 2. 存在public方法时,会单独部署库合约,并且第一个参数是状态变量类型
+library Array {
+ // 修改调用者状态变量的方式,第一个参数是状态变量本身
+ function remove(uint[] storage arr, uint index) public {
+ // Move the last element into the place to delete
+ require(arr.length > 0, "Can't remove from empty array");
+ arr[index] = arr[arr.length - 1];
arr.pop();
}
}
contract TestArray {
- using Array for uint[];
+ using Array for uint[];
uint[] public arr;
- function testArrayRemove() public {
- for (uint i = 0; i < 3; i++) {
+ function testArrayRemove() public {
+ for (uint i = 0; i < 3; i++) {
arr.push(i);
}
- arr.remove(1);
+ arr.remove(1);
- assert(arr.length == 2);
- assert(arr[0] == 0);
- assert(arr[1] == 2);
+ assert(arr.length == 2);
+ assert(arr[0] == 0);
+ assert(arr[1] == 2);
}
}
@@ -3161,7 +3157,7 @@ 参考link:
@@ -3247,14 +3243,6 @@ 参考link:
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/30_\344\274\240\351\200\222\347\273\223\346\236\204\344\275\223.html" "b/cn/01_solidity\345\237\272\347\241\200/30_\344\274\240\351\200\222\347\273\223\346\236\204\344\275\223.html"
index 512b6c93..c6bb31fc 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/30_\344\274\240\351\200\222\347\273\223\346\236\204\344\275\223.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/30_\344\274\240\351\200\222\347\273\223\346\236\204\344\275\223.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3025,7 +3021,7 @@ 第30节:传递结构体
如何正确传值:
案例一:
https://ethereum.stackexchange.com/questions/72435/how-to-pass-struct-params-in-remix-ide
-pragma solidity ^0.7;
+pragma solidity ^0.7;
pragma experimental ABIEncoderV2;
contract TestStruct {
@@ -3035,11 +3031,11 @@ 案例一:
uint256 age;
}
- mapping (bytes32 => User) users;
+ mapping (bytes32 => User) users;
- function addUsers (User [] memory _users) public {
+ function addUsers (User [] memory _users) public {
- for (uint i = 0; i < _users.length; i++) {
+ for (uint i = 0; i < _users.length; i++) {
bytes32 hash = keccak256(abi.encode(_users[i].name));
users[hash] = _users[i];
@@ -3047,21 +3043,21 @@ 案例一:
}
}
- function getUser (string memory username) public view returns (User memory) {
+ function getUser (string memory username) public view returns (User memory) {
bytes32 hash = keccak256(abi.encode(username));
- return users[hash];
+ return users[hash];
}
}
输入参数:
-[["Duke", 20], ["Linda", 21]]
+[["Duke", 20], ["Linda", 21]]
测试成功!
案例二:
-// SPDX-License-Identifier: MIT
-pragma solidity =0.8.10;
+// SPDX-License-Identifier: MIT
+pragma solidity =0.8.10;
pragma abicoder v2;
contract StructTest {
@@ -3072,8 +3068,8 @@ 案例二:
address tokenAddress;
}
- function call(Token [] memory tokens) external {
- for (uint8 i = 0; i< tokens.length; i++) {
+ function call(Token [] memory tokens) external {
+ for (uint8 i = 0; i< tokens.length; i++) {
(uint8 t, address addr) = (tokens[i].tokenType, tokens[i].tokenAddress);
emit Info(t, addr);
}
@@ -3085,7 +3081,7 @@ 案例二:
但是一直失败!
原因:地址需要使用双引号包裹传递,正确参数:
-[[0,"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"],[1,"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"]]
+[[0,"0x5B38Da6a701c568545dCfcB03FcB875f56beddC4"],[1,"0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2"]]
当结构体中有数组
正确做法:
@@ -3125,7 +3121,7 @@ 错误做法:
@@ -3211,14 +3207,6 @@ 错误做法:
-
-
-
-
-
-
-
-
diff --git "a/cn/01_solidity\345\237\272\347\241\200/index.html" "b/cn/01_solidity\345\237\272\347\241\200/index.html"
index ba61583a..eef23637 100644
--- "a/cn/01_solidity\345\237\272\347\241\200/index.html"
+++ "b/cn/01_solidity\345\237\272\347\241\200/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3056,7 +3052,7 @@ 第一章:solidity基础
@@ -3142,14 +3138,6 @@ 第一章:solidity基础
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/01_\345\205\250\345\261\200\345\217\230\351\207\217.html" "b/cn/02_solidity\350\277\233\351\230\266/01_\345\205\250\345\261\200\345\217\230\351\207\217.html"
index 273cc449..ae6752cc 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/01_\345\205\250\345\261\200\345\217\230\351\207\217.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/01_\345\205\250\345\261\200\345\217\230\351\207\217.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3131,7 +3127,7 @@ 区块和交易的属性
@@ -3217,14 +3213,6 @@ 区块和交易的属性
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/02_msg\344\270\211\344\272\272\347\273\204.html" "b/cn/02_solidity\350\277\233\351\230\266/02_msg\344\270\211\344\272\272\347\273\204.html"
index dc01ac3b..46cd9b67 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/02_msg\344\270\211\344\272\272\347\273\204.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/02_msg\344\270\211\344\272\272\347\273\204.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3032,8 +3028,8 @@ 第2节:msg.sender、msg.val
msg.data:表示调用这笔交易的信息,由函数签名和函数参数(16进制字符串),组成代理模式时常用msg.data(后续讲解)。
msg.sender
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract MsgSender {
@@ -3041,69 +3037,69 @@ msg.sender
uint256 public value;
address public caller;
- constructor() {
- //在部署合约的时候,设置一个全局唯一的合约所有者,后面可以使用权限控制
+ constructor() {
+ //在部署合约的时候,设置一个全局唯一的合约所有者,后面可以使用权限控制
owner = msg.sender;
}
- //1. 对与合约而言,msg.sender是一个可以改变的值,并不一定是合约的创造者
- //2. 任何人调用了合约的方法,那么这笔交易中的from就是当前合约中的msg.sender
- function setValue(uint256 input) public {
+ //1. 对与合约而言,msg.sender是一个可以改变的值,并不一定是合约的创造者
+ //2. 任何人调用了合约的方法,那么这笔交易中的from就是当前合约中的msg.sender
+ function setValue(uint256 input) public {
value = input;
caller = msg.sender;
}
}
msg.value
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract MsgValue {
- // uint256 public money;
+ // uint256 public money;
- mapping(address=> uint256) public personToMoney;
+ mapping(address=> uint256) public personToMoney;
- // 函数里面使用了msg.value,那么函数要修饰为payable
- function play() public payable {
+ // 函数里面使用了msg.value,那么函数要修饰为payable
+ function play() public payable {
- // 如果转账不是100wei,那么参与失败
- // 否则成功,并且添加到维护的mapping中
- require(msg.value == 100, "should equal to 100!");
+ // 如果转账不是100wei,那么参与失败
+ // 否则成功,并且添加到维护的mapping中
+ require(msg.value == 100, "should equal to 100!");
personToMoney[msg.sender] = msg.value;
}
- // 查询当前合约的余额
- function getBalance() public view returns(uint256) {
- return address(this).balance;
+ // 查询当前合约的余额
+ function getBalance() public view returns(uint256) {
+ return address(this).balance;
}
}
msg.data
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract MsgData {
event Data(bytes data, bytes4 sig);
- // input0: addr: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
- // input1: amt : 1
- function transfer(address addr, uint256 amt) public {
+ // input0: addr: 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
+ // input1: amt : 1
+ function transfer(address addr, uint256 amt) public {
bytes memory data = msg.data;
- // msg.sig 表示当前方法函数签名(4字节)
- // msg.sig 等价于 this.transfer.selector
+ // msg.sig 表示当前方法函数签名(4字节)
+ // msg.sig 等价于 this.transfer.selector
emit Data(data, msg.sig);
}
- //output:
- // - data: 0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001
- // - sig: 0xa9059cbb
+ //output:
+ // - data: 0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000001
+ // - sig: 0xa9059cbb
- // 对data进行分析:
- // 0xa9059cbb //前四字节
- // 0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 //第一个参数占位符(32字节)
- // 0000000000000000000000000000000000000000000000000000000000000001 //第二个参数占位符(32字节)
+ // 对data进行分析:
+ // 0xa9059cbb //前四字节
+ // 0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc4 //第一个参数占位符(32字节)
+ // 0000000000000000000000000000000000000000000000000000000000000001 //第二个参数占位符(32字节)
}
@@ -3133,7 +3129,7 @@ msg.data
@@ -3219,14 +3215,6 @@ msg.data
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/03_payable.html" "b/cn/02_solidity\350\277\233\351\230\266/03_payable.html"
index 4e1fded0..ec876619 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/03_payable.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/03_payable.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3031,33 +3027,33 @@ 第3节:payable
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Payable {
- // 1. Payable address can receive Ether
+ // 1. Payable address can receive Ether
address payable public owner;
- // 2. Payable constructor can receive Ether
- constructor() payable {
+ // 2. Payable constructor can receive Ether
+ constructor() payable {
owner = payable(msg.sender);
}
- // 3. Function to deposit Ether into this contract.
- function deposit() public payable {}
+ // 3. Function to deposit Ether into this contract.
+ function deposit() public payable {}
- // 4. Call this function along with some Ether.
- // The function will throw an error since this function is not payable.
- function notPayable() public {}
+ // 4. Call this function along with some Ether.
+ // The function will throw an error since this function is not payable.
+ function notPayable() public {}
- // 5. Function to withdraw all Ether from this contract.
- function withdraw() public {
- uint amount = address(this).balance;
+ // 5. Function to withdraw all Ether from this contract.
+ function withdraw() public {
+ uint amount = address(this).balance;
owner.transfer(amount);
}
- // 6. Function to transfer Ether from this contract to address from input
- function transfer(address payable _to, uint _amount) public {
+ // 6. Function to transfer Ether from this contract to address from input
+ function transfer(address payable _to, uint _amount) public {
_to.transfer(_amount);
}
}
@@ -3089,7 +3085,7 @@ 第3节:payable
@@ -3175,14 +3171,6 @@ 第3节:payable
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/04_encode_encodePacked.html" "b/cn/02_solidity\350\277\233\351\230\266/04_encode_encodePacked.html"
index 7788844b..09902774 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/04_encode_encodePacked.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/04_encode_encodePacked.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,129 +3024,129 @@ 第4节:abi.encode
abi.encodePacked:与abi.encode类似,但是生成的bytes是压缩过的(有些类型不会自动填充,无法传递给合约调用)。
手册:https://docs.soliditylang.org/en/v0.8.13/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-mode
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract AbiDecode {
struct MyStruct {
string name;
- uint[2] nums;
+ uint[2] nums;
}
- // input: 10, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, [1,"2",3], ["duke", [10,20]]
- // output: 0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000064756b6500000000000000000000000000000000000000000000000000000000
- // output长度:832位16进制字符(去除0x),832 / 32 = 26 (一定是32字节的整数倍,不足填0)
- function encode(
+ // input: 10, 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, [1,"2",3], ["duke", [10,20]]
+ // output: 0x000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000064756b6500000000000000000000000000000000000000000000000000000000
+ // output长度:832位16进制字符(去除0x),832 / 32 = 26 (一定是32字节的整数倍,不足填0)
+ function encode(
uint x,
address addr,
uint[] calldata arr,
MyStruct calldata myStruct
- ) external pure returns (bytes memory) {
- return abi.encode(x, addr, arr, myStruct);
+ ) external pure returns (bytes memory) {
+ return abi.encode(x, addr, arr, myStruct);
}
- function decode(bytes calldata data)
- external
- pure
- returns (
+ function decode(bytes calldata data)
+ external
+ pure
+ returns (
uint x,
address addr,
uint[] memory arr,
MyStruct memory myStruct
- )
- {
+ )
+ {
(x, addr, arr, myStruct) = abi.decode(data, (uint, address, uint[], MyStruct));
- /* decode output:
+ /* decode output:
0: uint256: x 10
1: address: addr 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
2: uint256[]: arr 1,2,3
3: tuple(string,uint256[2]): myStruct ,10,20
- */
+ */
}
- // 可以只decode其中部分字段,而不用全部decode,当前案例中,只有第一个字段被解析了,其余为默认值
- function decodeLess(bytes calldata data)
- external
- pure
- returns (
+ // 可以只decode其中部分字段,而不用全部decode,当前案例中,只有第一个字段被解析了,其余为默认值
+ function decodeLess(bytes calldata data)
+ external
+ pure
+ returns (
uint x,
address addr,
uint[] memory arr,
MyStruct memory myStruct
- )
- {
+ )
+ {
(x) = abi.decode(data, (uint));
- /* decode output:
+ /* decode output:
0: uint256: x 10
1: address: addr 0x0000000000000000000000000000000000000000
2: uint256[]: arr
3: tuple(string,uint256[2]): myStruct ,0,0
- */
+ */
}
- // input: -1, 0x42, 0x03, "Hello, world!"
- function encodePacked(
+ // input: -1, 0x42, 0x03, "Hello, world!"
+ function encodePacked(
int16 x,
bytes1 y,
uint16 z,
string memory s
- ) external view returns (bytes memory) {
+ ) external view returns (bytes memory) {
- // encodePacked 不支持struct和mapping
- return abi.encodePacked(x, y, z, s);
+ // encodePacked 不支持struct和mapping
+ return abi.encodePacked(x, y, z, s);
- /*
+ /*
0xffff42000348656c6c6f2c20776f726c6421
^^^^ int16(-1)
^^ bytes1(0x42)
^^^^ uint16(0x03)
^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field
- */
+ */
}
- // 可以用encodePacked来拼接字符串
- // output string: ipfs://bafybeidmrsvehl4ehipm5qqvgegi33r6/100.json
- function encodePackedTest() public pure returns (string memory) {
- string memory uri = "ipfs://bafybeidmrsvehl4ehipm5qqvgegi33r6/";
- return string(abi.encodePacked(uri, "100", ".json"));
+ // 可以用encodePacked来拼接字符串
+ // output string: ipfs://bafybeidmrsvehl4ehipm5qqvgegi33r6/100.json
+ function encodePackedTest() public pure returns (string memory) {
+ string memory uri = "ipfs://bafybeidmrsvehl4ehipm5qqvgegi33r6/";
+ return string(abi.encodePacked(uri, "100", ".json"));
}
}
使用三方库:
web3js编码:
-// encodeFunctionCall( abi ,参数 ) 得到编码
+// encodeFunctionCall( abi ,参数 ) 得到编码
web3.eth.abi.encodeFunctionCall({
- name: 'myMethod',
- type: 'function',
- inputs: [{
- type: 'uint256',
- name: 'myNumber'
+ name: 'myMethod',
+ type: 'function',
+ inputs: [{
+ type: 'uint256',
+ name: 'myNumber'
},{
- type: 'string',
- name: 'myString'
+ type: 'string',
+ name: 'myString'
}]
-}, ['2345675643', 'Hello!%']);
-> "0x24ee0097000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000"
+}, ['2345675643', 'Hello!%']);
+> "0x24ee0097000000000000000000000000000000000000000000000000000000008bd02b7b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000748656c6c6f212500000000000000000000000000000000000000000000000000"
web3js解码:
解码时,要将得到的calldata的前四字节去掉,那是函数的selector,不应该参与参数解析。
-// let res = web3.eth.abi.decodeParameters(abi, calldata)
+// let res = web3.eth.abi.decodeParameters(abi, calldata)
-async function main() {
- let calldata = '' //太大了,删除,去下面线上链接中获取
- let abi = [{ "internalType": "string", "name": "_id", "type": "string" }, { "internalType": "string", "name": "_uniqueId", "type": "string" }, { "internalType": "uint8", "name": "_assetFrom", "type": "uint8" }, { "internalType": "uint8", "name": "_action", "type": "uint8" }, { "internalType": "address", "name": "_srcToken", "type": "address" }, { "internalType": "address", "name": "_dstToken", "type": "address" }, { "internalType": "uint256", "name": "_srcAmount", "type": "uint256" }, { "internalType": "uint256", "name": "_srcFeeAmount", "type": "uint256" }, { "internalType": "bytes", "name": "_data", "type": "bytes" }]
+async function main() {
+ let calldata = '' //太大了,删除,去下面线上链接中获取
+ let abi = [{ "internalType": "string", "name": "_id", "type": "string" }, { "internalType": "string", "name": "_uniqueId", "type": "string" }, { "internalType": "uint8", "name": "_assetFrom", "type": "uint8" }, { "internalType": "uint8", "name": "_action", "type": "uint8" }, { "internalType": "address", "name": "_srcToken", "type": "address" }, { "internalType": "address", "name": "_dstToken", "type": "address" }, { "internalType": "uint256", "name": "_srcAmount", "type": "uint256" }, { "internalType": "uint256", "name": "_srcFeeAmount", "type": "uint256" }, { "internalType": "bytes", "name": "_data", "type": "bytes" }]
- let res = web3.eth.abi.decodeParameters(abi, calldata)
- console.log('res:', res)
+ let res = web3.eth.abi.decodeParameters(abi, calldata)
+ console.log('res:', res)
}
在线案例:https://web3playground.io/QmSeHtJPLFxweiGB8ocFDXkCZao6Mt5oEJ4Ej66iY3RL1R
ethersjs
-let bytes2 = mock1Inch.interface.encodeFunctionData("swap", [ETH_ADDR, daiToken.address, 90])
+let bytes2 = mock1Inch.interface.encodeFunctionData("swap", [ETH_ADDR, daiToken.address, 90])
@@ -3179,7 +3175,7 @@ ethersjs
@@ -3265,14 +3261,6 @@ ethersjs
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/05_call&staticcall.html" "b/cn/02_solidity\350\277\233\351\230\266/05_call&staticcall.html"
index 56cfe98c..d9035121 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/05_call&staticcall.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/05_call&staticcall.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,62 +3019,62 @@ 第5节:call&staticcall
职场进阶: https://dukeweb3.com
call是一种底层调用合约的方式,可以在合约内调用其他合约,call语法为:
-//(bool success, bytes memory data) = addr.call{value: valueAmt, gas: gasAmt}(abi.encodeWithSignature("foo(string,uint256)", 参数1, 参数2)
+//(bool success, bytes memory data) = addr.call{value: valueAmt, gas: gasAmt}(abi.encodeWithSignature("foo(string,uint256)", 参数1, 参数2)
其中:
-1. success:执行结果,一定要校验success是否成功,失败务必要回滚
-2. data:执行调用的返回值,是打包的字节序,需要解析才能得到调用函数的返回值(后续encode_decode详解)
+1. success:执行结果,一定要校验success是否成功,失败务必要回滚
+2. data:执行调用的返回值,是打包的字节序,需要解析才能得到调用函数的返回值(后续encode_decode详解)
当调用fallback方式给合约转ether的时候,建议使用call,而不是使用transfer或send方法
-(bool success, bytes memory data) = addr.call{value: 10}("")
+(bool success, bytes memory data) = addr.call{value: 10}("")
对于存在的方法,不建议使用call方式调用。
-(bool success, bytes memory data) = _addr.call(abi.encodeWithSignature("doesNotExist()"));
+(bool success, bytes memory data) = _addr.call(abi.encodeWithSignature("doesNotExist()"));
注意,当调用的方法不存在,且合约又未实现fallback时,交易会调用成功,但是第一个参数为:false,所以使用call调用后一定要检查success状态
完整demo:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Receiver {
event Received(address caller, uint amount, string message);
fallback() external payable {
- emit Received(msg.sender, msg.value, "Fallback was called");
+ emit Received(msg.sender, msg.value, "Fallback was called");
}
- function foo(string memory _message, uint _x) public payable returns (uint) {
+ function foo(string memory _message, uint _x) public payable returns (uint) {
emit Received(msg.sender, msg.value, _message);
- return _x + 1;
+ return _x + 1;
}
}
contract ReceiverWithOutFallback {
event Received(address caller, uint amount, string message);
- function foo(string memory _message, uint _x) public payable returns (uint) {
+ function foo(string memory _message, uint _x) public payable returns (uint) {
emit Received(msg.sender, msg.value, _message);
- return _x + 1;
+ return _x + 1;
}
}
contract Caller {
event Response(bool success, bytes data);
- function testCallFoo(address payable _addr) public payable {
- // You can send ether and specify a custom gas amount
- (bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(
- abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)
+ function testCallFoo(address payable _addr) public payable {
+ // You can send ether and specify a custom gas amount
+ (bool success, bytes memory data) = _addr.call{value: msg.value, gas: 5000}(
+ abi.encodeWithSignature("foo(string,uint256)", "call foo", 123)
);
emit Response(success, data);
}
- // Calling a function that does not exist triggers the fallback function.
- function testCallDoesNotExist(address _addr) public {
+ // Calling a function that does not exist triggers the fallback function.
+ function testCallDoesNotExist(address _addr) public {
(bool success, bytes memory data) = _addr.call(
- abi.encodeWithSignature("doesNotExist()")
+ abi.encodeWithSignature("doesNotExist()")
);
emit Response(success, data);
@@ -3094,12 +3090,12 @@ STATICCALL:
与CALL相同,但是不允许修改任何状态变量,是为了安全🔐考虑而新增的OPCODE
在Transparent模式的代理合约逻辑中,就使用了staticcall,从而让proxyAmin能够免费的调用父合约的admin函数,从而从slot中返回代理合约的管理员。这部分会在合约升级章节介绍。
- function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
- // We need to manually run the static call since the getter cannot be flagged as view
- // bytes4(keccak256("admin()")) == 0xf851a440
- (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
- require(success);
- return abi.decode(returndata, (address));
+ function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {
+ // We need to manually run the static call since the getter cannot be flagged as view
+ // bytes4(keccak256("admin()")) == 0xf851a440
+ (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
+ require(success);
+ return abi.decode(returndata, (address));
}
@@ -3131,7 +3127,7 @@ STATICCALL:
@@ -3217,14 +3213,6 @@ STATICCALL:
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/06_keccak256.html" "b/cn/02_solidity\350\277\233\351\230\266/06_keccak256.html"
index 2e5c209c..bdbe1bf5 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/06_keccak256.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/06_keccak256.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,39 +3023,39 @@ 第6节:keccak256哈希算法
用于生成唯一id;
生成数据指纹;
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract HashFunction {
- function hash(
+ function hash(
string memory _text,
uint _num,
address _addr
- ) public pure returns (bytes32) {
- return keccak256(abi.encodePacked(_text, _num, _addr));
+ ) public pure returns (bytes32) {
+ return keccak256(abi.encodePacked(_text, _num, _addr));
}
- // Example of hash collision
- // Hash collision can occur when you pass more than one dynamic data type
- // to abi.encodePacked. In such case, you should use abi.encode instead.
- function collision(string memory _text, string memory _anotherText)
- public
- pure
- returns (bytes32)
- {
- // encodePacked(AAA, BBB) -> AAABBB
- // encodePacked(AA, ABBB) -> AAABBB
- return keccak256(abi.encodePacked(_text, _anotherText));
+ // Example of hash collision
+ // Hash collision can occur when you pass more than one dynamic data type
+ // to abi.encodePacked. In such case, you should use abi.encode instead.
+ function collision(string memory _text, string memory _anotherText)
+ public
+ pure
+ returns (bytes32)
+ {
+ // encodePacked(AAA, BBB) -> AAABBB
+ // encodePacked(AA, ABBB) -> AAABBB
+ return keccak256(abi.encodePacked(_text, _anotherText));
}
}
contract GuessTheMagicWord {
bytes32 public answer =
- 0x60298f78cc0b47170ba79c10aa3851d7648bd96f2f8e46a19dbc777c36fb0c00;
+ 0x60298f78cc0b47170ba79c10aa3851d7648bd96f2f8e46a19dbc777c36fb0c00;
- // Magic word is "Solidity"
- function guess(string memory _word) public view returns (bool) {
- return keccak256(abi.encodePacked(_word)) == answer;
+ // Magic word is "Solidity"
+ function guess(string memory _word) public view returns (bool) {
+ return keccak256(abi.encodePacked(_word)) == answer;
}
}
@@ -3090,7 +3086,7 @@ 第6节:keccak256哈希算法
@@ -3176,14 +3172,6 @@ 第6节:keccak256哈希算法
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/07_selector.html" "b/cn/02_solidity\350\277\233\351\230\266/07_selector.html"
index 32aa4e1f..38a29139 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/07_selector.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/07_selector.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,119 +3023,119 @@ 四种方式计算selecor
方式1:abi.encodeWithSignature
拼装selector和函数参数,我们可以在A合约中得到calldata,并在A合约中通过call方法去调用B合约中的方法,从而实现合约间的调用。举例,下面的代码功能是:在当前合约中使用call调用addr地址中的transfer方法:
-// 在合约中,一个function的4字节selector可以通过abi.encodeWithSignature(...)来获取
-// "0xa9059cbb"
-bytes memory transferSelector = abi.encodeWithSignature("transfer(address,uint256)");
+// 在合约中,一个function的4字节selector可以通过abi.encodeWithSignature(...)来获取
+// "0xa9059cbb"
+bytes memory transferSelector = abi.encodeWithSignature("transfer(address,uint256)");
-// 调用合约
-addr.call(transferSelector, 0xSomeAddress, 100);
+// 调用合约
+addr.call(transferSelector, 0xSomeAddress, 100);
-// 一般会写成一行,简写如下:
-// addr.call(abi.encodeWithSignature("transfer(address,uint256)"), 0xSomeAddress, 100);
+// 一般会写成一行,简写如下:
+// addr.call(abi.encodeWithSignature("transfer(address,uint256)"), 0xSomeAddress, 100);
方式2:keccak256方法(sha3哈希算法)
-//注意我们这里做hash时,仅处理函数名与参数,并不会计算函数返回值
-bytes4(keccak256(bytes("transfer(address,uint256)")))
+//注意我们这里做hash时,仅处理函数名与参数,并不会计算函数返回值
+bytes4(keccak256(bytes("transfer(address,uint256)")))
方式3:在合约内部也可以直接获取selector
-// 假设当前合约内有transfer函数
-this.transfer.selector
+// 假设当前合约内有transfer函数
+this.transfer.selector
方式4:也可以使用abi.encodeCall方式获取
interface IERC20 {
- function transfer(address, uint) external;
+ function transfer(address, uint) external;
}
-function encodeCall(address to, uint amount) external pure returns (bytes memory) {
- // Typo and type errors will not compile
- return abi.encodeCall(IERC20.transfer, (to, amount));
+function encodeCall(address to, uint amount) external pure returns (bytes memory) {
+ // Typo and type errors will not compile
+ return abi.encodeCall(IERC20.transfer, (to, amount));
}
完整demo
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
interface IERC20 {
- function transfer(address, uint) external;
+ function transfer(address, uint) external;
}
contract FunctionSelector {
event Selector(bytes4 s1, bytes4 s2);
- //_func示例值: "transfer(address,uint256)"
- function getSelector(string calldata _func) external pure returns (bytes4, bytes memory) {
+ //_func示例值: "transfer(address,uint256)"
+ function getSelector(string calldata _func) external pure returns (bytes4, bytes memory) {
bytes4 selector1 = bytes4(keccak256(bytes(_func)));
bytes memory selector2 = abi.encodeWithSignature(_func);
- // 两者相同
- // 0: bytes4: 0xa9059cbb
- // 1: bytes: 0xa9059cbb
- return (selector1, selector2);
+ // 两者相同
+ // 0: bytes4: 0xa9059cbb
+ // 1: bytes: 0xa9059cbb
+ return (selector1, selector2);
}
- function encodeWithSignature(address to, uint amount)
- external
- pure
- returns (bytes memory)
- {
- // Typo is not checked - "transfer(address, uint)",不会检查参数类型
- return abi.encodeWithSignature("transfer(address,uint256)", to, amount);
+ function encodeWithSignature(address to, uint amount)
+ external
+ pure
+ returns (bytes memory)
+ {
+ // Typo is not checked - "transfer(address, uint)",不会检查参数类型
+ return abi.encodeWithSignature("transfer(address,uint256)", to, amount);
}
- function encodeWithSelector(address to, uint amount)
- external
- pure
- returns (bytes memory)
- {
- // Type is not checked - (IERC20.transfer.selector, true, amount) ,不会检查to, amount类型
- return abi.encodeWithSelector(IERC20.transfer.selector, to, amount);
+ function encodeWithSelector(address to, uint amount)
+ external
+ pure
+ returns (bytes memory)
+ {
+ // Type is not checked - (IERC20.transfer.selector, true, amount) ,不会检查to, amount类型
+ return abi.encodeWithSelector(IERC20.transfer.selector, to, amount);
}
- function encodeCall(address to, uint amount) external pure returns (bytes memory) {
- // Typo and type errors will not compile,校验最严格
- return abi.encodeCall(IERC20.transfer, (to, amount));
+ function encodeCall(address to, uint amount) external pure returns (bytes memory) {
+ // Typo and type errors will not compile,校验最严格
+ return abi.encodeCall(IERC20.transfer, (to, amount));
}
}
其他知识点
一般提到signature的方法指的是函数原型:
-"transfer(address,uint256)"
+"transfer(address,uint256)"
一般提到selector指的是前4字节
-// bytes4(keccak256(bytes("transfer(address,uint256)")))
-"Function sig:" "0xa9059cbb"
+// bytes4(keccak256(bytes("transfer(address,uint256)")))
+"Function sig:" "0xa9059cbb"
链下方式计算selector(详见在web3.js和ether.js章节),点击执行demo
-async function main() {
- let transferEvent = "Transfer(address,address,uint256)"
- let sig1 = web3.eth.abi.encodeEventSignature(transferEvent)
- let sig2 = web3.eth.abi.encodeFunctionSignature(transferEvent)
+async function main() {
+ let transferEvent = "Transfer(address,address,uint256)"
+ let sig1 = web3.eth.abi.encodeEventSignature(transferEvent)
+ let sig2 = web3.eth.abi.encodeFunctionSignature(transferEvent)
- // 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
- console.log('event sig1:', sig1)
+ // 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
+ console.log('event sig1:', sig1)
- // 0xddf252ad
- console.log('event sig2:', sig2)
+ // 0xddf252ad
+ console.log('event sig2:', sig2)
- let transferFun = "transfer(address,uint256)"
- let sig3 = web3.eth.abi.encodeFunctionSignature(transferFun)
+ let transferFun = "transfer(address,uint256)"
+ let sig3 = web3.eth.abi.encodeFunctionSignature(transferFun)
- // 0xa9059cbb,差一个字母千差万别
- console.log('Function sig:', sig3)
+ // 0xa9059cbb,差一个字母千差万别
+ console.log('Function sig:', sig3)
}
哈希算法:
-// keccak256与sha3和算法相同 ==》 brew install sha3sum
-// sha256属于sha2系列(与sha3不同)。
+// keccak256与sha3和算法相同 ==》 brew install sha3sum
+// sha256属于sha2系列(与sha3不同)。
@@ -3170,7 +3166,7 @@ 其他知识点
@@ -3256,14 +3252,6 @@ 其他知识点
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/08_send_ether.html" "b/cn/02_solidity\350\277\233\351\230\266/08_send_ether.html"
index 44e706e8..05e164f5 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/08_send_ether.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/08_send_ether.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3036,11 +3032,11 @@ 如何接收ether?
receive() external payable:msg.data为空时调用(为接收ether而生,仅solidity 0.6版本之后)
fallback() external payable:msg.data非空时调用(为执行default逻辑而生,顺便支持接收ether)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract ReceiveEther {
- /*
+ /*
Which function is called, fallback() or receive()?
sender ether
@@ -3054,54 +3050,54 @@ 如何接收ether?
yes no
/ \
receive() fallback()
- */
+ */
string public message;
- // Function to receive Ether. msg.data must be empty
+ // Function to receive Ether. msg.data must be empty
receive() external payable {
- message = "receive called!";
+ message = "receive called!";
}
- // Fallback function is called when msg.data is not empty
+ // Fallback function is called when msg.data is not empty
fallback() external payable {
- message = "fallback called!";
+ message = "fallback called!";
}
- function getBalance() public view returns (uint) {
- return address(this).balance;
+ function getBalance() public view returns (uint) {
+ return address(this).balance;
}
- function setMsg(string memory _msg) public {
+ function setMsg(string memory _msg) public {
message = _msg;
}
}
contract SendEther {
- function sendViaTransfer(address payable _to) public payable {
- // This function is no longer recommended for sending Ether. (不建议使用)
+ function sendViaTransfer(address payable _to) public payable {
+ // This function is no longer recommended for sending Ether. (不建议使用)
_to.transfer(msg.value);
}
- function sendViaSend(address payable _to) public payable {
- // Send returns a boolean value indicating success or failure.
- // This function is not recommended for sending Ether. (不建议使用)
+ function sendViaSend(address payable _to) public payable {
+ // Send returns a boolean value indicating success or failure.
+ // This function is not recommended for sending Ether. (不建议使用)
bool sent = _to.send(msg.value);
- require(sent, "Failed to send Ether");
+ require(sent, "Failed to send Ether");
}
- function sendViaCallFallback(address payable _to) public payable {
- // Call returns a boolean value indicating success or failure.
- // This is the current recommended method to use. (推荐使用)
- (bool sent, bytes memory data) = _to.call{value: msg.value}(abi.encodeWithSignature("noExistFuncTest()"));
- require(sent, "Failed to send Ether");
+ function sendViaCallFallback(address payable _to) public payable {
+ // Call returns a boolean value indicating success or failure.
+ // This is the current recommended method to use. (推荐使用)
+ (bool sent, bytes memory data) = _to.call{value: msg.value}(abi.encodeWithSignature("noExistFuncTest()"));
+ require(sent, "Failed to send Ether");
}
- function sendViaCallReceive(address payable _to) public payable {
- // Call returns a boolean value indicating success or failure.
- // This is the current recommended method to use.(推荐使用)
- (bool sent, bytes memory data) = _to.call{value: msg.value}("");
- require(sent, "Failed to send Ether");
+ function sendViaCallReceive(address payable _to) public payable {
+ // Call returns a boolean value indicating success or failure.
+ // This is the current recommended method to use.(推荐使用)
+ (bool sent, bytes memory data) = _to.call{value: msg.value}("");
+ require(sent, "Failed to send Ether");
}
}
@@ -3142,7 +3138,7 @@ 如何接收ether?
@@ -3228,14 +3224,6 @@ 如何接收ether?
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/09_delegatecall.html" "b/cn/02_solidity\350\277\233\351\230\266/09_delegatecall.html"
index 4e41d3fb..2df2e736 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/09_delegatecall.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/09_delegatecall.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,32 +3024,32 @@ 第9节: delegatecall
使用delegatecall的前提是:A合约和B合约有相同的状态变量。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Implementation {
- // NOTE: storage layout must be the same as contract A
+ // NOTE: storage layout must be the same as contract A
uint public num;
address public sender;
uint public value;
- function setVars(uint _num) public payable {
+ function setVars(uint _num) public payable {
num = _num;
sender = msg.sender;
value = msg.value;
}
}
-// 注意:执行后,Proxy中的sender值为EOA的地址,而不是A合约的地址 (调用链EOA-> Proxy::setVars -> Implementation::setVars)
-contract Proxy {
+// 注意:执行后,Proxy中的sender值为EOA的地址,而不是A合约的地址 (调用链EOA-> Proxy::setVars -> Implementation::setVars)
+contract Proxy {
uint public num;
address public sender;
uint public value;
- function setVars(address _impl, uint _num) public payable {
- // Proxy's storage is set, Implementation is not modified.
+ function setVars(address _impl, uint _num) public payable {
+ // Proxy's storage is set, Implementation is not modified.
(bool success, bytes memory data) = _impl.delegatecall(
- abi.encodeWithSignature("setVars(uint256)", _num)
+ abi.encodeWithSignature("setVars(uint256)", _num)
);
}
}
@@ -3085,7 +3081,7 @@ 第9节: delegatecall
@@ -3171,14 +3167,6 @@ 第9节: delegatecall
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/10_fallback.html" "b/cn/02_solidity\350\277\233\351\230\266/10_fallback.html"
index 74a842d4..39163472 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/10_fallback.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/10_fallback.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3032,42 +3028,42 @@ 第10节:fallback
当使用transfer或者send对合约进行转账时,fallback函数的gaslimit限定为2300 gas
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Fallback {
event Log(uint gas);
- // Fallback function must be declared as external.
+ // Fallback function must be declared as external.
fallback() external payable {
- // send / transfer (forwards 2300 gas to this fallback function)
- // call (forwards all of the gas)
+ // send / transfer (forwards 2300 gas to this fallback function)
+ // call (forwards all of the gas)
emit Log(gasleft());
}
- // Helper function to check the balance of this contract
- function getBalance() public view returns (uint) {
- return address(this).balance;
+ // Helper function to check the balance of this contract
+ function getBalance() public view returns (uint) {
+ return address(this).balance;
}
}
contract SendToFallback {
- function transferToFallback(address payable _to) public payable {
- // Log event: "gas": "2254"
+ function transferToFallback(address payable _to) public payable {
+ // Log event: "gas": "2254"
_to.transfer(msg.value);
}
- function callFallback(address payable _to) public payable {
- // Log event: "gas": "6110"
- (bool sent, ) = _to.call{value: msg.value}("");
- require(sent, "Failed to send Ether");
+ function callFallback(address payable _to) public payable {
+ // Log event: "gas": "6110"
+ (bool sent, ) = _to.call{value: msg.value}("");
+ require(sent, "Failed to send Ether");
}
- function callNoExistFunc(address payable _to) public payable {
- // call no exist funtion will call fallback by default
- // Log event: "gas": "5146"
- (bool sent, ) = _to.call{value: msg.value}(abi.encodeWithSignature("noExistFunc()"));
- require(sent, "Failed to call");
+ function callNoExistFunc(address payable _to) public payable {
+ // call no exist funtion will call fallback by default
+ // Log event: "gas": "5146"
+ (bool sent, ) = _to.call{value: msg.value}(abi.encodeWithSignature("noExistFunc()"));
+ require(sent, "Failed to call");
}
}
@@ -3098,7 +3094,7 @@ 第10节:fallback
@@ -3184,14 +3180,6 @@ 第10节:fallback
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/11_\345\220\210\347\272\246\351\227\264\350\260\203\347\224\250.html" "b/cn/02_solidity\350\277\233\351\230\266/11_\345\220\210\347\272\246\351\227\264\350\260\203\347\224\250.html"
index c027c086..1b522e01 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/11_\345\220\210\347\272\246\351\227\264\350\260\203\347\224\250.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/11_\345\220\210\347\272\246\351\227\264\350\260\203\347\224\250.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,41 +3026,41 @@ 第11节:合约间调用
使用call调用合约: A.call(calldata)
使用delegate调用合约:A.delegatecall(calldata)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Callee {
uint public x;
uint public value;
- function setX(uint _x) public returns (uint) {
+ function setX(uint _x) public returns (uint) {
x = _x;
- return x;
+ return x;
}
- function setXandSendEther(uint _x) public payable returns (uint, uint) {
+ function setXandSendEther(uint _x) public payable returns (uint, uint) {
x = _x;
value = msg.value;
- return (x, value);
+ return (x, value);
}
}
contract Caller {
- // 直接在参数中进行实例化合约
- function setX(Callee _callee, uint _x) public {
+ // 直接在参数中进行实例化合约
+ function setX(Callee _callee, uint _x) public {
uint x = _callee.setX(_x);
}
- // 传递地址,在内部实例化callee合约
- function setXFromAddress(address _addr, uint _x) public {
+ // 传递地址,在内部实例化callee合约
+ function setXFromAddress(address _addr, uint _x) public {
Callee callee = Callee(_addr);
callee.setX(_x);
}
- // 调用方法,并转ether
- function setXandSendEther(Callee _callee, uint _x) public payable {
- (uint x, uint value) = _callee.setXandSendEther{value: msg.value}(_x);
+ // 调用方法,并转ether
+ function setXandSendEther(Callee _callee, uint _x) public payable {
+ (uint x, uint value) = _callee.setXandSendEther{value: msg.value}(_x);
}
}
@@ -3095,7 +3091,7 @@ 第11节:合约间调用
@@ -3181,14 +3177,6 @@ 第11节:合约间调用
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/12_new_create2.html" "b/cn/02_solidity\350\277\233\351\230\266/12_new_create2.html"
index c6d0382c..c03b414e 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/12_new_create2.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/12_new_create2.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3053,73 +3049,73 @@ 第12节:new合约
- 使用new方式创建:
-// 内部调用create
-new ContractName(参数...)
+// 内部调用create
+new ContractName(参数...)
-// 内部调用create2
-// 在0.8.0版本之后,new增加了salt选项,从而支持了create2的特性(通过salt可以计算出创建合约的地址)。
-new ContractName{salt: _salt}(参数...)
+// 内部调用create2
+// 在0.8.0版本之后,new增加了salt选项,从而支持了create2的特性(通过salt可以计算出创建合约的地址)。
+new ContractName{salt: _salt}(参数...)
demo验证
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Car {
address public owner;
string public model;
address public carAddr;
- constructor(address _owner, string memory _model) payable {
+ constructor(address _owner, string memory _model) payable {
owner = _owner;
model = _model;
- carAddr = address(this);
+ carAddr = address(this);
}
}
contract CarFactory {
Car[] public cars;
- function create(address _owner, string memory _model) public {
- Car car = new Car(_owner, _model);
+ function create(address _owner, string memory _model) public {
+ Car car = new Car(_owner, _model);
cars.push(car);
}
- function createAndSendEther(address _owner, string memory _model) public payable {
- Car car = (new Car){value: msg.value}(_owner, _model);
+ function createAndSendEther(address _owner, string memory _model) public payable {
+ Car car = (new Car){value: msg.value}(_owner, _model);
cars.push(car);
}
- function create2(
+ function create2(
address _owner,
string memory _model,
bytes32 _salt
- ) public {
- Car car = (new Car){salt: _salt}(_owner, _model);
+ ) public {
+ Car car = (new Car){salt: _salt}(_owner, _model);
cars.push(car);
}
- function create2AndSendEther(
+ function create2AndSendEther(
address _owner,
string memory _model,
bytes32 _salt
- ) public payable {
- Car car = (new Car){value: msg.value, salt: _salt}(_owner, _model);
+ ) public payable {
+ Car car = (new Car){value: msg.value, salt: _salt}(_owner, _model);
cars.push(car);
}
- function getCar(uint _index)
- public
- view
- returns (
+ function getCar(uint _index)
+ public
+ view
+ returns (
address owner,
string memory model,
address carAddr,
uint balance
- )
- {
+ )
+ {
Car car = cars[_index];
- return (car.owner(), car.model(), car.carAddr(), address(car).balance);
+ return (car.owner(), car.model(), car.carAddr(), address(car).balance);
}
}
@@ -3150,7 +3146,7 @@ demo验证
@@ -3236,14 +3232,6 @@ demo验证
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/13_delete.html" "b/cn/02_solidity\350\277\233\351\230\266/13_delete.html"
index 6abbd884..6e5f066e 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/13_delete.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/13_delete.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,60 +3025,60 @@ 第13节:delete
如果对map类型使用delete,什么都不会发生;
但如果对map类型中的一个键使用delete,则会删除与该键相关的值。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract Delete {
- //01. string
- string public str1 = "hello";
+ //01. string
+ string public str1 = "hello";
- function deleteStr() public {
- delete str1;
+ function deleteStr() public {
+ delete str1;
}
- function setStr(string memory input) public {
+ function setStr(string memory input) public {
str1 = input;
}
- //02. array 对于固定长度的数组,会删除每个元素的值,但是数组长度不变
- uint256[10] public arry1 = [1,2,3,4,5];
+ //02. array 对于固定长度的数组,会删除每个元素的值,但是数组长度不变
+ uint256[10] public arry1 = [1,2,3,4,5];
- function deleteFiexedArry() public {
- delete arry1;
+ function deleteFiexedArry() public {
+ delete arry1;
}
- //03. array new
+ //03. array new
uint256[] arry2 ;
- function setArray2() public {
- arry2 = new uint256[](10);
- for (uint256 i = 0; i< arry2.length; i++) {
+ function setArray2() public {
+ arry2 = new uint256[](10);
+ for (uint256 i = 0; i< arry2.length; i++) {
arry2[i] = i;
}
}
- function getArray2() public view returns(uint256[] memory) {
- return arry2;
+ function getArray2() public view returns(uint256[] memory) {
+ return arry2;
}
- function deleteArray2() public {
- delete arry2;
+ function deleteArray2() public {
+ delete arry2;
}
- //04. mapping
- mapping(uint256 => string) public m1;
+ //04. mapping
+ mapping(uint256 => string) public m1;
- function setMap() public {
- m1[0] = "hello";
- m1[1] = "world";
+ function setMap() public {
+ m1[0] = "hello";
+ m1[1] = "world";
}
- //Mapping不允许直接使用delete,但是可以对mapping的元素进行指定删除
- // function deleteM1() public {
- // delete m1;
- // }
+ //Mapping不允许直接使用delete,但是可以对mapping的元素进行指定删除
+ // function deleteM1() public {
+ // delete m1;
+ // }
- function deleteMapping(uint256 i) public {
- delete m1[i];
+ function deleteMapping(uint256 i) public {
+ delete m1[i];
}
}
@@ -3113,7 +3109,7 @@ 第13节:delete
@@ -3199,14 +3195,6 @@ 第13节:delete
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/14_try_catch.html" "b/cn/02_solidity\350\277\233\351\230\266/14_try_catch.html"
index 1414c626..764dcca2 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/14_try_catch.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/14_try_catch.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,22 +3019,22 @@ 第14节:try/catch
职场进阶: https://dukeweb3.com
try/catch仅可以捕捉在调用external函数或创建合约中抛出的异常信息。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// External contract used for try / catch examples
+// External contract used for try / catch examples
contract Foo {
address public owner;
- constructor(address _owner) {
- require(_owner != address(0), "invalid address");
- assert(_owner != 0x0000000000000000000000000000000000000001);
+ constructor(address _owner) {
+ require(_owner != address(0), "invalid address");
+ assert(_owner != 0x0000000000000000000000000000000000000001);
owner = _owner;
}
- function myFunc(uint x) public pure returns (string memory) {
- require(x != 0, "require failed");
- return "my func was called";
+ function myFunc(uint x) public pure returns (string memory) {
+ require(x != 0, "require failed");
+ return "my func was called";
}
}
@@ -3048,35 +3044,35 @@ 第14节:try/catch
Foo public foo;
- constructor() {
- // This Foo contract is used for example of try catch with external call
- foo = new Foo(msg.sender);
+ constructor() {
+ // This Foo contract is used for example of try catch with external call
+ foo = new Foo(msg.sender);
}
- // Example of try / catch with external call
- // tryCatchExternalCall(0) => Log("external call failed")
- // tryCatchExternalCall(1) => Log("my func was called")
- function tryCatchExternalCall(uint _i) public {
- try foo.myFunc(_i) returns (string memory result) {
+ // Example of try / catch with external call
+ // tryCatchExternalCall(0) => Log("external call failed")
+ // tryCatchExternalCall(1) => Log("my func was called")
+ function tryCatchExternalCall(uint _i) public {
+ try foo.myFunc(_i) returns (string memory result) {
emit Log(result);
- } catch {
- emit Log("external call failed");
+ } catch {
+ emit Log("external call failed");
}
}
- // Example of try / catch with contract creation
- // tryCatchNewContract(0x0000000000000000000000000000000000000000) => Log("invalid address")
- // tryCatchNewContract(0x0000000000000000000000000000000000000001) => LogBytes("")
- // tryCatchNewContract(0x0000000000000000000000000000000000000002) => Log("Foo created")
- function tryCatchNewContract(address _owner) public {
- try new Foo(_owner) returns (Foo foo) {
- // you can use variable foo here
- emit Log("Foo created");
- } catch Error(string memory reason) {
- // catch failing revert() and require()
+ // Example of try / catch with contract creation
+ // tryCatchNewContract(0x0000000000000000000000000000000000000000) => Log("invalid address")
+ // tryCatchNewContract(0x0000000000000000000000000000000000000001) => LogBytes("")
+ // tryCatchNewContract(0x0000000000000000000000000000000000000002) => Log("Foo created")
+ function tryCatchNewContract(address _owner) public {
+ try new Foo(_owner) returns (Foo foo) {
+ // you can use variable foo here
+ emit Log("Foo created");
+ } catch Error(string memory reason) {
+ // catch failing revert() and require()
emit Log(reason);
- } catch (bytes memory reason) {
- // catch failing assert()
+ } catch (bytes memory reason) {
+ // catch failing assert()
emit LogBytes(reason);
}
}
@@ -3110,7 +3106,7 @@ 第14节:try/catch
@@ -3196,14 +3192,6 @@ 第14节:try/catch
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/15_import.html" "b/cn/02_solidity\350\277\233\351\230\266/15_import.html"
index e3386d19..c08ef1ed 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/15_import.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/15_import.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,64 +3024,64 @@ 第15节:import
Fool.sol
常量、函数、枚举、结构体、Error可以定义在合约之外;事件、变量不允许定义在合约之外。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
struct Point {
uint x;
uint y;
}
-// 事件不允许定义在合约之外
-// event Greeting(string);
+// 事件不允许定义在合约之外
+// event Greeting(string);
error Unauthorized(address caller);
-string constant greeting = "hell world";
+string constant greeting = "hell world";
-function add(uint x, uint y) pure returns (uint) {
- return x + y;
+function add(uint x, uint y) pure returns (uint) {
+ return x + y;
}
contract Foo {
- string public name = "Foo";
+ string public name = "Foo";
}
Import.sol
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// import Foo.sol from current directory
-import "./Foo.sol";
+// import Foo.sol from current directory
+import "./Foo.sol";
-// import {symbol1 as alias, symbol2} from "filename";
-import {Unauthorized, add as func, Point} from "./Foo.sol";
+// import {symbol1 as alias, symbol2} from "filename";
+import {Unauthorized, add as func, Point} from "./Foo.sol";
contract Import {
- // Initialize Foo.sol
- Foo public foo = new Foo();
+ // Initialize Foo.sol
+ Foo public foo = new Foo();
- // Test Foo.sol by getting it's name.
- function getFooName() public view returns (string memory) {
- return foo.name();
+ // Test Foo.sol by getting it's name.
+ function getFooName() public view returns (string memory) {
+ return foo.name();
}
- function myAdd() public pure returns(uint) {
- return func(1,2);
+ function myAdd() public pure returns(uint) {
+ return func(1,2);
}
- function greetingCall() public pure returns(string memory) {
- return greeting;
+ function greetingCall() public pure returns(string memory) {
+ return greeting;
}
}
导入外部文件:
-// https://github.com/owner/repo/blob/branch/path/to/Contract.sol
-import "https://github.com/owner/repo/blob/branch/path/to/Contract.sol";
+// https://github.com/owner/repo/blob/branch/path/to/Contract.sol
+import "https://github.com/owner/repo/blob/branch/path/to/Contract.sol";
-// Example import ECDSA.sol from openzeppelin-contract repo, release-v4.5 branch
-// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol
-import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol";
+// Example import ECDSA.sol from openzeppelin-contract repo, release-v4.5 branch
+// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol
+import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol";
@@ -3114,7 +3110,7 @@ 第15节:import
@@ -3200,14 +3196,6 @@ 第15节:import
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/16_\350\212\202\347\272\246gas.html" "b/cn/02_solidity\350\277\233\351\230\266/16_\350\212\202\347\272\246gas.html"
index 047e53ca..cad88141 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/16_\350\212\202\347\272\246gas.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/16_\350\212\202\347\272\246gas.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,41 +3025,41 @@ 第16节:节约gas
对变量进行缓存
短路效应
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// gas golf
+// gas golf
contract GasGolf {
- // start - 50908 gas
- // use calldata - 49163 gas
- // load state variables to memory - 48952 gas
- // short circuit - 48634 gas
- // loop increments - 48244 gas
- // cache array length - 48209 gas
- // load array elements to memory - 48047 gas
+ // start - 50908 gas
+ // use calldata - 49163 gas
+ // load state variables to memory - 48952 gas
+ // short circuit - 48634 gas
+ // loop increments - 48244 gas
+ // cache array length - 48209 gas
+ // load array elements to memory - 48047 gas
uint public total;
- // start - not gas optimized
- // function sumIfEvenAndLessThan99(uint[] memory nums) external {
- // for (uint i = 0; i < nums.length; i += 1) {
- // bool isEven = nums[i] % 2 == 0;
- // bool isLessThan99 = nums[i] < 99;
- // if (isEven && isLessThan99) {
- // total += nums[i];
- // }
- // }
- // }
-
- // gas optimized
- // [1, 2, 3, 4, 5, 100]
- function sumIfEvenAndLessThan99(uint[] calldata nums) external {
+ // start - not gas optimized
+ // function sumIfEvenAndLessThan99(uint[] memory nums) external {
+ // for (uint i = 0; i < nums.length; i += 1) {
+ // bool isEven = nums[i] % 2 == 0;
+ // bool isLessThan99 = nums[i] < 99;
+ // if (isEven && isLessThan99) {
+ // total += nums[i];
+ // }
+ // }
+ // }
+
+ // gas optimized
+ // [1, 2, 3, 4, 5, 100]
+ function sumIfEvenAndLessThan99(uint[] calldata nums) external {
uint _total = total;
uint len = nums.length;
- for (uint i = 0; i < len; ++i) {
+ for (uint i = 0; i < len; ++i) {
uint num = nums[i];
- if (num % 2 == 0 && num < 99) {
+ if (num % 2 == 0 && num < 99) {
_total += num;
}
}
@@ -3160,11 +3156,11 @@ Solidity Gas Optimizations Tricks
1. 使用最新版本solidity
EVM的升级会带来gas的优化
2. for循环优化
-// 避免重复计算长度
+// 避免重复计算长度
uint length = arr.length;
-// ++i 可以减少一次赋值,初始化i放在for里面
-for (uint i; i < length;) {
- // unchecked可以减少溢出校验
+// ++i 可以减少一次赋值,初始化i放在for里面
+for (uint i; i < length;) {
+ // unchecked可以减少溢出校验
unchecked { ++i; }
}
@@ -3174,122 +3170,122 @@ 3. 使用calldata代替memory
4. 尽量使用immutable代替state variable
EIP-2929 对slot的操作引入了cold slot(消耗2100 gas)和warm slot(消耗100 gas)的概念,但是依然很贵,而修饰为immutable的变量会保存在bytecode中,使用时使用的是push,仅消耗3gas
5. 使用immutable代替constant(计算keccak时)
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.9;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.9;
contract Immutables is AccessControl {
uint256 public gas;
bytes32 public immutable MANAGER_ROLE_IMMUT;
- // 每次使用都需要重新计算hash值
- bytes32 public constant MANAGER_ROLE_CONST = keccak256('MANAGER_ROLE');
+ // 每次使用都需要重新计算hash值
+ bytes32 public constant MANAGER_ROLE_CONST = keccak256('MANAGER_ROLE');
- constructor(){
- // 仅在构造时计算hash一次
- MANAGER_ROLE_IMMUT = keccak256('MANAGER_ROLE');
+ constructor(){
+ // 仅在构造时计算hash一次
+ MANAGER_ROLE_IMMUT = keccak256('MANAGER_ROLE');
_setupRole(MANAGER_ROLE_CONST, msg.sender);
_setupRole(MANAGER_ROLE_IMMUT, msg.sender);
}
- function immutableCheck() external {
+ function immutableCheck() external {
gas = gasleft();
- require(hasRole(MANAGER_ROLE_IMMUT, msg.sender), 'Caller is not in manager role'); // 24408 gas
+ require(hasRole(MANAGER_ROLE_IMMUT, msg.sender), 'Caller is not in manager role'); // 24408 gas
gas -= gasleft();
}
- function constantCheck() external {
+ function constantCheck() external {
gas = gasleft();
- require(hasRole(MANAGER_ROLE_CONST, msg.sender), 'Caller is not in manager role'); // 24419 gas
+ require(hasRole(MANAGER_ROLE_CONST, msg.sender), 'Caller is not in manager role'); // 24419 gas
gas -= gasleft();
}
}
6. 使用modifier代替function
经过对比,modifier合约的foo方法更加节约gas
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.9;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.9;
contract Inlined {
- function isNotExpired(bool _true) internal view {
- require(_true == true, "Exchange: EXPIRED");
+ function isNotExpired(bool _true) internal view {
+ require(_true == true, "Exchange: EXPIRED");
}
- function foo(bool _test) public returns(uint) {
+ function foo(bool _test) public returns(uint) {
isNotExpired(_test);
- return 1;
+ return 1;
}
}
contract Modifier {
- modifier isNotExpired(bool _true) {
- require(_true == true, "Exchange: EXPIRED");
+ modifier isNotExpired(bool _true) {
+ require(_true == true, "Exchange: EXPIRED");
_;
}
- function foo(bool _test) public isNotExpired(_test)returns(uint) {
- return 1;
+ function foo(bool _test) public isNotExpired(_test)returns(uint) {
+ return 1;
}
}
7. modifier中使用internal函数减少合约size
尽量将modifier中的条件判断逻辑写在单独的internal view函数中,可以减少合约size,从而减少gas消耗。
以下代码中,onlyOwner在多处使用,使用越多节约效果越明显
-pragma solidity ^0.8.10;
+pragma solidity ^0.8.10;
contract Context {
- function _msgSender() internal view returns(address) {
- return msg.sender;
+ function _msgSender() internal view returns(address) {
+ return msg.sender;
}
}
contract Ownable is Context {
address public owner = _msgSender();
- modifier onlyOwner() {
- require(owner == _msgSender(), "Ownable: caller is not the owner");
+ modifier onlyOwner() {
+ require(owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
}
contract Ownable2 is Context {
address public owner = _msgSender();
- modifier onlyOwner() {
+ modifier onlyOwner() {
_checkOwner();
_;
}
- function _checkOwner() internal view virtual {
- require(owner == _msgSender(), "Ownable: caller is not the owner");
+ function _checkOwner() internal view virtual {
+ require(owner == _msgSender(), "Ownable: caller is not the owner");
}
}
-// This is deployment gas cost for each function
-// 0: 107172
-// 1: 145772
-// 2: 181610
-// 3: 198170
-// 4: 214532
-// 5: 241059
+// This is deployment gas cost for each function
+// 0: 107172
+// 1: 145772
+// 2: 181610
+// 3: 198170
+// 4: 214532
+// 5: 241059
contract T1 is Ownable {
event Call(bytes4 selector);
- function f0() external onlyOwner() { emit Call(this.f0.selector); }
- function f1() external onlyOwner() { emit Call(this.f1.selector); }
- function f2() external onlyOwner() { emit Call(this.f2.selector); }
- function f3() external onlyOwner() { emit Call(this.f3.selector); }
- function f4() external onlyOwner() { emit Call(this.f4.selector); }
+ function f0() external onlyOwner() { emit Call(this.f0.selector); }
+ function f1() external onlyOwner() { emit Call(this.f1.selector); }
+ function f2() external onlyOwner() { emit Call(this.f2.selector); }
+ function f3() external onlyOwner() { emit Call(this.f3.selector); }
+ function f4() external onlyOwner() { emit Call(this.f4.selector); }
}
-// 0: 107172
-// 1: 147908
-// 2: 165818
-// 3: 183506
-// 4: 192500
-// 5: 211682
+// 0: 107172
+// 1: 147908
+// 2: 165818
+// 3: 183506
+// 4: 192500
+// 5: 211682
contract T2 is Ownable2 {
event Call(bytes4 selector);
- function f0() external onlyOwner() { emit Call(this.f0.selector); }
- function f1() external onlyOwner() { emit Call(this.f1.selector); }
- function f2() external onlyOwner() { emit Call(this.f2.selector); }
- function f3() external onlyOwner() { emit Call(this.f3.selector); }
- function f4() external onlyOwner() { emit Call(this.f4.selector); }
+ function f0() external onlyOwner() { emit Call(this.f0.selector); }
+ function f1() external onlyOwner() { emit Call(this.f1.selector); }
+ function f2() external onlyOwner() { emit Call(this.f2.selector); }
+ function f3() external onlyOwner() { emit Call(this.f3.selector); }
+ function f4() external onlyOwner() { emit Call(this.f4.selector); }
}
验证结果:
@@ -3299,41 +3295,41 @@ 8. >= is cheaper than >
Non-strict inequalities (>=
) are cheaper than strict ones (>
). This is due to some supplementary checks (ISZERO
, 3 gas)).
uint256 public gas;
-function checkStrict() external {
+function checkStrict() external {
gas = gasleft();
- require(999999999999999999 > 1); // gas 5017
+ require(999999999999999999 > 1); // gas 5017
gas -= gasleft();
}
-function checkNonStrict() external {
+function checkNonStrict() external {
gas = gasleft();
- require(999999999999999999 >= 1); // gas 5006
+ require(999999999999999999 >= 1); // gas 5006
gas -= gasleft();
}
9. 使用SHR和SHL来代替乘除法
DIV使用5gas,SHR使用3gas,而且后者不用额外校验除数为0的逻辑,更加节约gas
10. 使用多个require代替使用&&
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.9;
-import "hardhat/console.sol";
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.9;
+import "hardhat/console.sol";
contract Requires {
uint256 public gas;
- function check1(uint x) public {
+ function check1(uint x) public {
gas = gasleft();
- console.log(gasleft());
- require(x == 0 && x < 1 ); // gas cost 22156
- console.log(gasleft());
+ console.log(gasleft());
+ require(x == 0 && x < 1 ); // gas cost 22156
+ console.log(gasleft());
gas -= gasleft();
}
- function check2(uint x) public {
+ function check2(uint x) public {
gas = gasleft();
- console.log(gasleft());
- require(x == 0); // gas cost 22148
- require(x < 1);
- console.log(gasleft());
+ console.log(gasleft());
+ require(x == 0); // gas cost 22148
+ require(x < 1);
+ console.log(gasleft());
gas -= gasleft();
}
}
@@ -3349,15 +3345,15 @@ 13. 对状态变量进行cachi
SLOAD:100gas
MLOAD:3 gas
14. 对结构体格外注意
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.9;
-import "hardhat/console.sol";
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.9;
+import "hardhat/console.sol";
contract Requires {
event Unlock(address _sender, uint256 _nftIndex, uint256 _amt);
- // struct LockPosition use 3 slots
- mapping(uint256 => LockPosition) positions;
+ // struct LockPosition use 3 slots
+ mapping(uint256 => LockPosition) positions;
struct LockPosition {
address owner;
@@ -3365,18 +3361,41 @@ 14. 对结构体格外注意
uint256 lockAmount;
}
- function unlock(uint256 _nftIndex) external {
- // 3 SLOADs and 3 MSTORES
- // 从storage读取三个变量,并且复制到memory中
- LockPosition memory position = positions[_nftIndex]; // gas: costing 3 SLOADs while only lockAmount is needed twice.
+ function unlock(uint256 _nftIndex) external {
+ // 3 SLOADs and 3 MSTORES
+ // 从storage读取三个变量,并且复制到memory中
+ LockPosition memory position = positions[_nftIndex]; // gas: costing 3 SLOADs while only lockAmount is needed twice.
+
+ //Replace "memory" with "storage" and cache only position.lockAmount
+ require(position.owner == msg.sender, "unauthorized");
+ require(position.unlockAt <= block.timestamp, "locked");
+
+ delete positions[_nftIndex];
+ payable(msg.sender).transfer(position.lockAmount);
+
+ emit Unlock(msg.sender, _nftIndex, position.lockAmount);
+ }
+
+ function unlockOptimize(uint256 _nftIndex) external {
+ LockPosition storage position = positions[_nftIndex];
+
+ uint256 amt = position.lockAmount;
+
+ //Replace "memory" with "storage" and cache only position.lockAmount
+ require(position.owner == msg.sender, "unauthorized");
+ require(position.unlockAt <= block.timestamp, "locked");
- //Replace "memory" with "storage" and cache only position.lockAmount
- require(position.owner == msg.sender, "unauthorized");
- require(position.unlockAt
+ delete positions[_nftIndex];
+ payable(msg.sender).transfer(amt);
+
+ emit Unlock(msg.sender, _nftIndex, amt);
+ }
+}
+
15. 复用非零值的状态变量更节约gas
Writing to an Existing Storage Slot Is Cheaper Than Using a New One
https://eips.ethereum.org/EIPS/eip-2200
-EIP — 2200 changed a lot with gas, and now if you hold 1 Wei of a token it’s cheaper to use the token than if you hold 0. There is a lot to unpack here so just google EIP 2200 and learn if you want, but in general, if you need to use a storage slot, don’t empty it if you plan to refill it later.
+EIP — 2200 changed a lot with gas, and now if you hold 1 Wei of a token it’s cheaper to use the token than if you hold 0. There is a lot to unpack here so just google EIP 2200 and learn if you want, but in general, if you need to use a storage slot, don’t empty it if you plan to refill it later.
参考链接
@@ -3411,7 +3430,7 @@ 参考链接
@@ -3497,14 +3516,6 @@ 参考链接
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/17_type.html" "b/cn/02_solidity\350\277\233\351\230\266/17_type.html"
index 668fe503..3125ebb2 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/17_type.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/17_type.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3028,9 +3024,61 @@ 第17节:type
- type(x).creattionCode: 合约部署时的bytecode;
- type(x).runtimeCode: 合约运行时的bytecode,一般是构造函数数据,但是当constructor中有汇编时会有不同(没有仔细了解)。
-// SPDX-License-Identifier: GPL-3.0
-
-pragma solidity >=0.7.0
+// SPDX-License-Identifier: GPL-3.0
+
+pragma solidity >=0.7.0 <0.9.0;
+
+/**
+ * @title Storage
+ * @dev Store & retrieve value in a variable
+ */
+contract Storage {
+ string public str;
+ constructor(string memory _str) {
+ str = _str;
+ }
+
+ 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;
+ }
+
+ function getInfo() public pure returns(string memory name) {
+ name = type(Storage).name;
+
+ // creationCode 和runtimeCode不能在这个合约自己内部使用,防止会出现循环调用问题
+ // creationCode = type(Storage).creationCode;
+ // runtimeCode = new type(Storage).runtimeCode;
+
+ }
+}
+
+contract TestStorage {
+ Storage s;
+ constructor(Storage _address) {
+ s = _address;
+ }
+
+ function getInfo() public view returns(bytes memory creationCode, bytes memory runtimeCode) {
+ creationCode = type(Storage).creationCode;
+ runtimeCode = type(Storage).runtimeCode;
+ }
+}
+
更多内容:https://docs.soliditylang.org/en/v0.6.5/units-and-global-variables.html#meta-type
@@ -3059,7 +3107,7 @@ 第17节:type
@@ -3145,14 +3193,6 @@ 第17节:type
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/18_assembly.html" "b/cn/02_solidity\350\277\233\351\230\266/18_assembly.html"
index d9f0c50b..d336371b 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/18_assembly.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/18_assembly.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3035,8 +3031,54 @@ 节约gas
- 23082 gas -> sumAsm(次之)
- 22895 gas -> sumPureAsm(最高效)
-// SPDX-License-Identifier: GPL-3.0
-pragma solidity >=0.4.16
+// SPDX-License-Identifier: GPL-3.0
+pragma solidity >=0.4.16 <0.9.0;
+
+library VectorSum {
+ // This function is less efficient because the optimizer currently fails to
+ // remove the bounds checks in array access.
+ function sumSolidity(uint[] memory data) public pure returns (uint sum) {
+ for (uint i = 0; i < data.length; ++i)
+ sum += data[i];
+ }
+
+ // We know that we only access the array in bounds, so we can avoid the check.
+ // 0x20 needs to be added to an array because the first slot contains the
+ // array length.
+ function sumAsm(uint[] memory data) public pure returns (uint sum) {
+ for (uint i = 0; i < data.length; ++i) {
+ assembly {
+ sum := add(sum, mload(add(add(data, 0x20), mul(i, 0x20))))
+ }
+ }
+ }
+
+ // Same as above, but accomplish the entire code within inline assembly.
+ function sumPureAsm(uint[] memory data) public pure returns (uint sum) {
+ assembly {
+ // Load the length (first 32 bytes)
+ let len := mload(data)
+
+ // Skip over the length field.
+ //
+ // Keep temporary variable so it can be incremented in place.
+ //
+ // NOTE: incrementing data would result in an unusable
+ // data variable after this assembly block
+ let dataElementLocation := add(data, 0x20)
+
+ // Iterate until the bound is not met.
+ for
+ { let end := add(dataElementLocation, mul(len, 0x20)) }
+ lt(dataElementLocation, end)
+ { dataElementLocation := add(dataElementLocation, 0x20) }
+ {
+ sum := add(sum, mload(dataElementLocation))
+ }
+ }
+ }
+}
+
其他文章
- https://jeancvllr.medium.com/solidity-tutorial-all-about-assembly-5acdfefde05c
@@ -3068,7 +3110,7 @@ 其他文章
@@ -3154,14 +3196,6 @@ 其他文章
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/19_merkleTree.html" "b/cn/02_solidity\350\277\233\351\230\266/19_merkleTree.html"
index 9f00a6b5..7867dbca 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/19_merkleTree.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/19_merkleTree.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3049,7 +3045,7 @@ 第19节:MerkleTreee
@@ -3135,14 +3131,6 @@ 第19节:MerkleTreee
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/20_signature.html" "b/cn/02_solidity\350\277\233\351\230\266/20_signature.html"
index e9f1f091..55ef2ba4 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/20_signature.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/20_signature.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3061,7 +3057,7 @@ 第20节:链下签名signatur
@@ -3147,14 +3143,6 @@ 第20节:链下签名signatur
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/21_permit.html" "b/cn/02_solidity\350\277\233\351\230\266/21_permit.html"
index 2032ffd6..6c40e4bb 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/21_permit.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/21_permit.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3026,27 +3022,27 @@ 第21节:permit
permit接口
根据EIP-2612的提议,对于ERC20的先授权,再transferFrom模式,用户EOA需要进行两笔交易操作,这样浪费资源,且交互不友好,解决方式是在原有的ERC20接口中,增加一个perimit接口,这个接口可以接收链下的签名(r, s, v),在内部进行校验,如果校验通过后可以进行approve操作。
这个签名中会描述授权给谁进行帮忙approve,被授权的人在合约中可以一次调用approve和transferFrom,从而完成:通过一笔交易而完成转账动作。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8;
interface IERC20Permit {
- function totalSupply() external view returns (uint);
+ function totalSupply() external view returns (uint);
- function balanceOf(address account) external view returns (uint);
+ function balanceOf(address account) external view returns (uint);
- function transfer(address recipient, uint amount) external returns (bool);
+ function transfer(address recipient, uint amount) external returns (bool);
- function allowance(address owner, address spender) external view returns (uint);
+ function allowance(address owner, address spender) external view returns (uint);
- function approve(address spender, uint amount) external returns (bool);
+ function approve(address spender, uint amount) external returns (bool);
- function transferFrom(
+ function transferFrom(
address sender,
address recipient,
uint amount
- ) external returns (bool);
+ ) external returns (bool);
- function permit(
+ function permit(
address owner,
address spender,
uint value,
@@ -3054,9 +3050,9 @@ permit接口
uint8 v,
bytes32 r,
bytes32 s
- ) external;
+ ) external;
- event Transfer(address indexed from, address indexed to, uint value);
+ event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
@@ -3077,46 +3073,46 @@ permit实现
- 调用approve(或者直接操作allowance)
下面是uniswapV2的LPToken中的permit实现:
- function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
+ function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
- // 第一部分
- // 1. 校验deadline
- require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
+ // 第一部分
+ // 1. 校验deadline
+ require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
- // 2. 获取签名的信息(一个hash值,我们对hash值签名,而不是对原内容签名)
+ // 2. 获取签名的信息(一个hash值,我们对hash值签名,而不是对原内容签名)
bytes32 digest = keccak256(
abi.encodePacked(
- '\x19\x01',
+ '\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
- // 3. 使用签名,对签名信息进行校验,能够解析出来签名地址。
+ // 3. 使用签名,对签名信息进行校验,能够解析出来签名地址。
address recoveredAddress = ecrecover(digest, v, r, s);
- // 4. 校验签名地址是否有效
- require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE'); // onwer入参,这个token的所有者,授权给spender花费
+ // 4. 校验签名地址是否有效
+ require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE'); // onwer入参,这个token的所有者,授权给spender花费
- // 第二部分
+ // 第二部分
_approve(owner, spender, value);
}
以上第一步是标准的实现,其中:
PERMIT_TYPEHASH:
-// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
-bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
+// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
+bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
DOMAIN_SEPARATOR:
DOMAIN_SEPARATOR = keccak256(
abi.encode(
- keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
- keccak256(bytes(name)), // name 为token的name字段
- keccak256(bytes('1')),
+ keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
+ keccak256(bytes(name)), // name 为token的name字段
+ keccak256(bytes('1')),
chainId,
- address(this)
+ address(this)
)
);
@@ -3149,7 +3145,7 @@ permit实现
@@ -3235,14 +3231,6 @@ permit实现
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/22_math.html" "b/cn/02_solidity\350\277\233\351\230\266/22_math.html"
index eb070c04..d75e1c0e 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/22_math.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/22_math.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第22节:数学操作
@@ -3136,14 +3132,6 @@ 第22节:数学操作
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/23_timelock.html" "b/cn/02_solidity\350\277\233\351\230\266/23_timelock.html"
index 8d885b05..a29f966e 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/23_timelock.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/23_timelock.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第23节:timelock时间锁
@@ -3136,14 +3132,6 @@ 第23节:timelock时间锁
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/24_upgrade.html" "b/cn/02_solidity\350\277\233\351\230\266/24_upgrade.html"
index 4abd3a65..f613cb99 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/24_upgrade.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/24_upgrade.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第24节:可升级合约upgrade
var gitbook = gitbook || [];
gitbook.push(function() {
- gitbook.page.hasChanged({"page":{"title":"第24节:upgrade","level":"1.9.24","depth":2,"next":{"title":"第25节:eventLog","level":"1.9.25","depth":2,"path":"02_solidity进阶/25_eventLog.md","ref":"02_solidity进阶/25_eventLog.md","articles":[]},"previous":{"title":"第23节:timelock","level":"1.9.23","depth":2,"path":"02_solidity进阶/23_timelock.md","ref":"02_solidity进阶/23_timelock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["heading-anchors@1.0.3","ga@1.0.1","richquotes@0.0.9","github@2.0.0","language-picker","toggle-chapters","codeblock-label","-search","-lunr","page-toc-button","include-codeblock","js-console","honkit-plugin-mermaid"],"root":"./","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"language-picker":{"grid-columns":3},"github":{"url":"https://github.com/dukedaily/solidity-expert"},"codeblock-label":{},"honkit-plugin-mermaid":{"theme":"default"},"fontsettings":{"theme":"white","family":"sans","size":2},"js-console":{},"richquotes":{"default":false},"heading-anchors":{},"highlight":{},"page-toc-button":{},"ga":{"configuration":"auto","token":"UA-51680040-3"},"include-codeblock":{"check":false,"edit":false,"lang":"","fixlang":false,"template":"default","theme":"chrome","unindent":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false},"toggle-chapters":{}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56},"embedFonts":false},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{"esversion":"2022","nodeversion":"18.12.0","npmversion":"8.19.2","triplebackticks":"```","console":""},"language":"cn","gitbook":"*"},"file":{"path":"02_solidity进阶/24_upgrade.md","mtime":"2023-09-23T13:15:24.502Z","type":"markdown"},"gitbook":{"version":"3.7.3","time":"2024-08-23T16:27:32.019Z"},"basePath":"..","book":{"language":"cn"}});
+ gitbook.page.hasChanged({"page":{"title":"第24节:upgrade","level":"1.9.24","depth":2,"next":{"title":"第25节:eventLog","level":"1.9.25","depth":2,"path":"02_solidity进阶/25_eventLog.md","ref":"02_solidity进阶/25_eventLog.md","articles":[]},"previous":{"title":"第23节:timelock","level":"1.9.23","depth":2,"path":"02_solidity进阶/23_timelock.md","ref":"02_solidity进阶/23_timelock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["heading-anchors@1.0.3","ga@1.0.1","richquotes@0.0.9","github@2.0.0","language-picker","toggle-chapters","codeblock-label","-search","-lunr","page-toc-button","include-codeblock","js-console"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"language-picker":{"grid-columns":3},"github":{"url":"https://github.com/dukedaily/solidity-expert"},"codeblock-label":{},"fontsettings":{"theme":"white","family":"sans","size":2},"js-console":{},"richquotes":{"default":false},"heading-anchors":{},"highlight":{},"page-toc-button":{},"ga":{"configuration":"auto","token":"UA-51680040-3"},"include-codeblock":{"check":false,"edit":false,"lang":"","fixlang":false,"template":"default","theme":"chrome","unindent":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false},"toggle-chapters":{}},"theme":"default","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56},"embedFonts":false},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"language":"cn","gitbook":"*"},"file":{"path":"02_solidity进阶/24_upgrade.md","mtime":"2023-09-23T13:15:24.502Z","type":"markdown"},"gitbook":{"version":"3.7.3","time":"2024-08-24T00:11:31.715Z"},"basePath":"..","book":{"language":"cn"}});
});
@@ -3136,14 +3132,6 @@ 第24节:可升级合约upgrade
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/25_eventLog.html" "b/cn/02_solidity\350\277\233\351\230\266/25_eventLog.html"
index ef8dfdf0..12b26f0e 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/25_eventLog.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/25_eventLog.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3045,7 +3041,7 @@ Event
描述
发送一笔交易之后,我们可以得到tx交易的返回值,在返回值中会包含TransactionRecept字段,进一步会发现Event,指定id可以获取这个事件的详情,进一步会获得每个字段的具体数值。
-console.log(transactionReceipt.events[0].args.oldNumber.toString())
+console.log(transactionReceipt.events[0].args.oldNumber.toString())
Bloom
以太坊的Header中有布隆过滤器,这里面存放的是什么?(是所有事件生成的信息),当有索引过来的时候,我们会去重新生成事件。
@@ -3082,7 +3078,7 @@ Bloom
@@ -3168,14 +3164,6 @@ Bloom
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/26_selfdestruct.html" "b/cn/02_solidity\350\277\233\351\230\266/26_selfdestruct.html"
index ad2780eb..de31311f 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/26_selfdestruct.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/26_selfdestruct.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3056,7 +3052,7 @@ 第26节:合约自杀(sel
@@ -3142,14 +3138,6 @@ 第26节:合约自杀(sel
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/27_permit2.html" "b/cn/02_solidity\350\277\233\351\230\266/27_permit2.html"
index 2416185e..3d963321 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/27_permit2.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/27_permit2.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第27节:Permit2
@@ -3136,14 +3132,6 @@ 第27节:Permit2
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/28_signature-eip712.html" "b/cn/02_solidity\350\277\233\351\230\266/28_signature-eip712.html"
index 656e2b7f..365a3ca9 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/28_signature-eip712.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/28_signature-eip712.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,59 +3025,59 @@ 第28节:signature-EIP712
- 手册:https://docs.ethers.org/v5/api/signer/#Signer-signTypedData
这是官方示例代码:
-// All properties on a domain are optional
-const domain = {
- name: 'Ether Mail',
- version: '1',
- chainId: 1,
- verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
+// All properties on a domain are optional
+const domain = {
+ name: 'Ether Mail',
+ version: '1',
+ chainId: 1,
+ verifyingContract: '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC'
};
-// The named list of all type definitions
-const types = {
- Person: [
- { name: 'name', type: 'string' },
- { name: 'wallet', type: 'address' }
+// The named list of all type definitions
+const types = {
+ Person: [
+ { name: 'name', type: 'string' },
+ { name: 'wallet', type: 'address' }
],
- Mail: [
- { name: 'from', type: 'Person' },
- { name: 'to', type: 'Person' },
- { name: 'contents', type: 'string' }
+ Mail: [
+ { name: 'from', type: 'Person' },
+ { name: 'to', type: 'Person' },
+ { name: 'contents', type: 'string' }
]
};
-// The data to sign
-const value = {
- from: {
- name: 'Cow',
- wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'
+// The data to sign
+const value = {
+ from: {
+ name: 'Cow',
+ wallet: '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826'
},
- to: {
- name: 'Bob',
- wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'
+ to: {
+ name: 'Bob',
+ wallet: '0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB'
},
- contents: 'Hello, Bob!'
+ contents: 'Hello, Bob!'
};
-signature = await signer._signTypedData(domain, types, value);
-// '0x463b9c9971d1a144507d2e905f4e98becd159139421a4bb8d3c9c2ed04eb401057dd0698d504fd6ca48829a3c8a7a98c1c961eae617096cb54264bbdd082e13d1c'
+signature = await signer._signTypedData(domain, types, value);
+// '0x463b9c9971d1a144507d2e905f4e98becd159139421a4bb8d3c9c2ed04eb401057dd0698d504fd6ca48829a3c8a7a98c1c961eae617096cb54264bbdd082e13d1c'
合约:使用openzeppelin的标准包,构造函数的时候,需要传递参数构造:ERC721(name,version),这两个值在后面的verify时会用到
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.11;
-import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.11;
+import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract TestEIP712 {
- // 其他...
- function verify(
+ // 其他...
+ function verify(
uint256 id,
uint256 amt,
address[] memory workers,
bytes memory signature
- ) external view returns (bool) {
+ ) external view returns (bool) {
bytes memory encoded = abi.encode(
keccak256(
- 'Message(uint256 id,uint256 amt,address[] workers)'
+ 'Message(uint256 id,uint256 amt,address[] workers)'
),
id,
amt,
@@ -3089,63 +3085,63 @@ 第28节:signature-EIP712
);
bytes32 structHash = keccak256(encoded);
- // 里面会拼接\x19\x01、domainHash、自定义结构hash
+ // 里面会拼接\x19\x01、domainHash、自定义结构hash
bytes32 digest = _hashTypedDataV4(structHash);
- return ECDSA.recover(digest, signature) == owner();
+ return ECDSA.recover(digest, signature) == owner();
}
}
testcase.ts
-import { verifyTypedData } from 'ethers/lib/utils'
-
-const typedData = {
- // 这里的domain 不需要再写了,因为ethers会自动帮忙添加
- types: {
- // EIP712Domain: [
- // { name: "name", type: "string" },
- // { name: "version", type: "string" },
- // { name: "chainId", type: "uint256" },
- // { name: "verifyingContract", type: "address" }
- // ],
- Message: [
- { name: 'id', type: 'uint256' },
- { name: 'amt', type: 'uint256' },
- { name: 'workers', type: 'address[]' },
+import { verifyTypedData } from 'ethers/lib/utils'
+
+const typedData = {
+ // 这里的domain 不需要再写了,因为ethers会自动帮忙添加
+ types: {
+ // EIP712Domain: [
+ // { name: "name", type: "string" },
+ // { name: "version", type: "string" },
+ // { name: "chainId", type: "uint256" },
+ // { name: "verifyingContract", type: "address" }
+ // ],
+ Message: [
+ { name: 'id', type: 'uint256' },
+ { name: 'amt', type: 'uint256' },
+ { name: 'workers', type: 'address[]' },
],
},
- // 这个就是上面的Message
- primaryType: 'Message',
- // 这是EIP712Domain的值
- domain: {
- name: name,
- version: version,
- chainId: chainId,
- verifyingContract: contractAddress,
+ // 这个就是上面的Message
+ primaryType: 'Message',
+ // 这是EIP712Domain的值
+ domain: {
+ name: name,
+ version: version,
+ chainId: chainId,
+ verifyingContract: contractAddress,
},
- // 这是message的值
- message: {
- "id": id,
- "amt": amt,
- "workers": workers
+ // 这是message的值
+ message: {
+ "id": id,
+ "amt": amt,
+ "workers": workers
},
};
- // 结构化签名
- const signature = await signer._signTypedData(
+ // 结构化签名
+ const signature = await signer._signTypedData(
typedData.domain,
typedData.types,
typedData.message,
);
- // 使用ethersjs验证:
- let res = verifyTypedData(
+ // 使用ethersjs验证:
+ let res = verifyTypedData(
typedData.domain, typedData.types, typedData.message, signature,
).toLowerCase() === signerAddress.toLowerCase()
- console.log("etherjs验证签名有效性:", res);
+ console.log("etherjs验证签名有效性:", res);
- // 使用合约验证:
- await instance.verify(
+ // 使用合约验证:
+ await instance.verify(
id, amt, workers, signature
)
@@ -3188,7 +3184,7 @@ 第28节:signature-EIP712
@@ -3274,14 +3270,6 @@ 第28节:signature-EIP712
-
-
-
-
-
-
-
-
diff --git "a/cn/02_solidity\350\277\233\351\230\266/index.html" "b/cn/02_solidity\350\277\233\351\230\266/index.html"
index 3f9ea025..ee17cabc 100644
--- "a/cn/02_solidity\350\277\233\351\230\266/index.html"
+++ "b/cn/02_solidity\350\277\233\351\230\266/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3050,7 +3046,7 @@ 第二章:solidity进阶
@@ -3136,14 +3132,6 @@ 第二章:solidity进阶
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/01_ERC20.html" "b/cn/03_EIP\345\215\217\350\256\256/01_ERC20.html"
index 4209a554..b02c5df3 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/01_ERC20.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/01_ERC20.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,142 +3019,142 @@ 第1节:ERC20(标准Token)
- 任何遵从EIP-20协议(ERC20标准)的Contract都属于ERC20 Token
标准接口
-// 6 REQUIRED FUNCTIONS
-function totalSupply() public view returns (uint256)
-function balanceOf(address _owner) public view returns (uint256 balance)
-function transfer(address _to, uint256 _value) public returns (bool success)
-function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
-function approve(address _spender, uint256 _value) public returns (bool success)
-function allowance(address _owner, address _spender) public view returns (uint256 remaining)
-
-// 2 REQUIRED EVENTS
-event Transfer(address indexed _from, address indexed _to, uint256 _value)
-event Approval(address indexed _owner, address indexed _spender, uint256 _value)
-
-// 3. OPTIONAL FUNCTIONS
-function name() public view returns (string)
-function symbol() public view returns (string)
-function decimals() public view returns (uint8)
-
+// 6 REQUIRED FUNCTIONS
+function totalSupply() public view returns (uint256)
+function balanceOf(address _owner) public view returns (uint256 balance)
+function transfer(address _to, uint256 _value) public returns (bool success)
+function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
+function approve(address _spender, uint256 _value) public returns (bool success)
+function allowance(address _owner, address _spender) public view returns (uint256 remaining)
+
+// 2 REQUIRED EVENTS
+event Transfer(address indexed _from, address indexed _to, uint256 _value)
+event Approval(address indexed _owner, address indexed _spender, uint256 _value)
+
+// 3. OPTIONAL FUNCTIONS
+function name() public view returns (string)
+function symbol() public view returns (string)
+function decimals() public view returns (uint8)
+
分析
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
+// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
- // 6 REQUIRED FUNCTIONS
- // 总发行量
- function totalSupply() external view returns (uint); // -> 总发行量 100000000 * 10**decimals
-
- // 标准decimal: 18
- // USDT: 6
- // WBTC: 8
- function balanceOf(address account) external view returns (uint); // 指定账户的余额
-
- // 币的持有人直接调用,进行转账
- function transfer(address recipient, uint amount) external returns (bool);
-
- // 最常用的!!
- // 1. 我这个owner对合约进行approve,此时approve内部会修改allowance变量
- // 2. 合约内部调用transferFrom来支配owner的token
- function transferFrom( // spender就是这个合约
- address sender, // owner
- address recipient, // 转给谁
- uint amount // 金额
- ) external returns (bool);
-
-
- // owner: 币的持有人
- // spender: 是指定帮助花费的代理人(被授权的人)
- function allowance(address owner, address spender) external view returns (uint); // 授权的额度
-
- // decimals view,这是一个public 的变量,自动提供了一个读取的方法 // 返回精度
- // 持有人对spender进行授权,在approve内部,会调用msg.sender来知道owner是谁
- function approve(address spender, uint amount) external returns (bool);
-
- // 2 REQUIRED EVENTS
- // 事件
- event Transfer(address indexed from, address indexed to, uint value);
+ // 6 REQUIRED FUNCTIONS
+ // 总发行量
+ function totalSupply() external view returns (uint); // -> 总发行量 100000000 * 10**decimals
+
+ // 标准decimal: 18
+ // USDT: 6
+ // WBTC: 8
+ function balanceOf(address account) external view returns (uint); // 指定账户的余额
+
+ // 币的持有人直接调用,进行转账
+ function transfer(address recipient, uint amount) external returns (bool);
+
+ // 最常用的!!
+ // 1. 我这个owner对合约进行approve,此时approve内部会修改allowance变量
+ // 2. 合约内部调用transferFrom来支配owner的token
+ function transferFrom( // spender就是这个合约
+ address sender, // owner
+ address recipient, // 转给谁
+ uint amount // 金额
+ ) external returns (bool);
+
+
+ // owner: 币的持有人
+ // spender: 是指定帮助花费的代理人(被授权的人)
+ function allowance(address owner, address spender) external view returns (uint); // 授权的额度
+
+ // decimals view,这是一个public 的变量,自动提供了一个读取的方法 // 返回精度
+ // 持有人对spender进行授权,在approve内部,会调用msg.sender来知道owner是谁
+ function approve(address spender, uint amount) external returns (bool);
+
+ // 2 REQUIRED EVENTS
+ // 事件
+ event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
- // 3. OPTIONAL FUNCTIONS
- function name() public view returns (string)
- function symbol() public view returns (string)
- function decimals() public view returns (uint8)
+ // 3. OPTIONAL FUNCTIONS
+ function name() public view returns (string)
+ function symbol() public view returns (string)
+ function decimals() public view returns (uint8)
}
-
+
以下是ERC20的案例:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-import "./IERC20.sol";
+import "./IERC20.sol";
contract ERC20 is IERC20 {
uint public totalSupply;
- mapping(address => uint) public balanceOf;
- mapping(address => mapping(address => uint)) public allowance;
- string public name = "Solidity by Example";
- string public symbol = "SOLBYEX";
- uint8 public decimals = 18;
+ mapping(address => uint) public balanceOf;
+ mapping(address => mapping(address => uint)) public allowance;
+ string public name = "Solidity by Example";
+ string public symbol = "SOLBYEX";
+ uint8 public decimals = 18;
- function transfer(address recipient, uint amount) external returns (bool) {
+ function transfer(address recipient, uint amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
- return true;
+ return true;
}
- function approve(address spender, uint amount) external returns (bool) {
+ function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
- return true;
+ return true;
}
- function transferFrom(
+ function transferFrom(
address sender,
address recipient,
uint amount
- ) external returns (bool) {
+ ) external returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
- return true;
+ return true;
}
- function mint(uint amount) external {
+ function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
- emit Transfer(address(0), msg.sender, amount);
+ emit Transfer(address(0), msg.sender, amount);
}
- function burn(uint amount) external {
+ function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
- emit Transfer(msg.sender, address(0), amount);
+ emit Transfer(msg.sender, address(0), amount);
}
}
可以使用openzeppelin库进行创建自己的token:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
-import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+// import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
+import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
- constructor(string memory name, string memory symbol) ERC20(name, symbol) {
- // Mint 100 tokens to msg.sender
- // Similar to how
- // 1 dollar = 100 cents
- // 1 token = 1 * (10 ** decimals)
- _mint(msg.sender, 100 * 10**uint(decimals()));
+ constructor(string memory name, string memory symbol) ERC20(name, symbol) {
+ // Mint 100 tokens to msg.sender
+ // Similar to how
+ // 1 dollar = 100 cents
+ // 1 token = 1 * (10 ** decimals)
+ _mint(msg.sender, 100 * 10**uint(decimals()));
}
- // 默认是18,可以进行override
- function decimals() public view override returns (uint8) {
- return 6;
+ // 默认是18,可以进行override
+ function decimals() public view override returns (uint8) {
+ return 6;
}
}
@@ -3189,7 +3185,7 @@ 分析
@@ -3275,14 +3271,6 @@ 分析
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/02_ERC721.html" "b/cn/03_EIP\345\215\217\350\256\256/02_ERC721.html"
index 088ca0c5..8ea735b9 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/02_ERC721.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/02_ERC721.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3025,162 +3021,162 @@ 第2节:ERC721(非同质化
EIP-721: https://eips.ethereum.org/EIPS/eip-721
标准接口
-// SPDX-License-Identifier: MIT
-// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
+// SPDX-License-Identifier: MIT
+// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721.sol)
-pragma solidity ^0.8.0;
+pragma solidity ^0.8.0;
interface IERC165 {
- function supportsInterface(bytes4 interfaceID) external view returns (bool);
+ function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
interface IERC721 is IERC165 {
- // 3 EVENTS
- event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
+ // 3 EVENTS
+ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
- // 9 REQUIRED FUNCTIONS
- function balanceOf(address owner) external view returns (uint256 balance);
- function ownerOf(uint256 tokenId) external view returns (address owner);
- function safeTransferFrom(
- address from,
+ // 9 REQUIRED FUNCTIONS
+ function balanceOf(address owner) external view returns (uint256 balance);
+ function ownerOf(uint256 tokenId) external view returns (address owner);
+ function safeTransferFrom(
+ address from,
address to,
uint256 tokenId,
bytes calldata data
- ) external;
+ ) external;
- function safeTransferFrom(
- address from,
+ function safeTransferFrom(
+ address from,
address to,
uint256 tokenId
- ) external;
+ ) external;
- function transferFrom(
- address from,
+ function transferFrom(
+ address from,
address to,
uint256 tokenId
- ) external;
+ ) external;
- function approve(address to, uint256 tokenId) external;
- function setApprovalForAll(address operator, bool _approved) external;
- function getApproved(uint256 tokenId) external view returns (address operator);
- function isApprovedForAll(address owner, address operator) external view returns (bool);
+ function approve(address to, uint256 tokenId) external;
+ function setApprovalForAll(address operator, bool _approved) external;
+ function getApproved(uint256 tokenId) external view returns (address operator);
+ function isApprovedForAll(address owner, address operator) external view returns (bool);
- // 3 OPTIONAL FUNCTIONS
- function name() external view returns (string _name);
- function symbol() external view returns (string _symbol);
- function tokenURI(uint256 _tokenId) external view returns (string);
+ // 3 OPTIONAL FUNCTIONS
+ function name() external view returns (string _name);
+ function symbol() external view returns (string _symbol);
+ function tokenURI(uint256 _tokenId) external view returns (string);
}
部署
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
-import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
-/*
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+/*
1. 一个ERC721合约也是一个集合,可以有N多个token,但是每一个都是不一样的,有唯一的id
2. 每个tokenid可以关联一个URI,一般是.json文件,里面有三个字段,进而变成一个独一无二的展示,所以是NFT,三个字段为:
- description:nft描述
- url:nft图片存储在ipfs上的哈希值
- name:nft名字
-*/
+*/
contract SimpleCollectible is ERC721URIStorage {
uint256 public tokenCounter;
- constructor () public ERC721 ("Dogie", "DOG"){
- tokenCounter = 0;
+ constructor () public ERC721 ("Dogie", "DOG"){
+ tokenCounter = 0;
}
- function createCollectible(string memory tokenURI) public returns (uint256) {
+ function createCollectible(string memory tokenURI) public returns (uint256) {
uint256 newItemId = tokenCounter;
_safeMint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
- tokenCounter = tokenCounter + 1;
- return newItemId;
+ tokenCounter = tokenCounter + 1;
+ return newItemId;
}
- function _baseURI() internal view override returns (string memory) {
- return "https://gateway.pinata.cloud/ipfs/";
+ function _baseURI() internal view override returns (string memory) {
+ return "https://gateway.pinata.cloud/ipfs/";
}
- //1. createCollectible(QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta)
- //2. tokenURI(0)
- //3. 浏览器请求:https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta
+ //1. createCollectible(QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta)
+ //2. tokenURI(0)
+ //3. 浏览器请求:https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta
}
接受者为合约时
to如果是合约地址,则to合约必须实现onERC721Received接口,因为有回调校验,如果to是EOA则不需要实现该接口。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
interface IERC721Receiver {
- function onERC721Received(
+ function onERC721Received(
address operator,
- address from,
+ address from,
uint256 tokenId,
bytes calldata data
- ) external returns (bytes4);
+ ) external returns (bytes4);
}
contract ERC721Holder is IERC721Receiver {
- function onERC721Received(
+ function onERC721Received(
address,
address,
uint256,
bytes memory
- ) public virtual override returns (bytes4) {
- return this.onERC721Received.selector;
+ ) public virtual override returns (bytes4) {
+ return this.onERC721Received.selector;
}
}
完整代码
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.13;
-// 里面继承了IERC721Receiver.sol接口
-import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
-/*
+// 里面继承了IERC721Receiver.sol接口
+import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
+/*
1. 一个ERC721合约也是一个集合,可以有N多个token,但是每一个都是不一样的,有唯一的id
2. 每个tokenid可以关联一个图片(URI),进而变成一个独一无二的展示,所以是NFT
-*/
+*/
contract SimpleCollectible is ERC721URIStorage {
uint256 public tokenCounter;
- constructor () public ERC721 ("Dogie", "DOG"){
+ constructor () public ERC721 ("Dogie", "DOG"){
}
- function createCollectible(string memory tokenURI) public returns (uint256) {
+ function createCollectible(string memory tokenURI) public returns (uint256) {
uint256 newItemId = tokenCounter;
_safeMint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
- tokenCounter = tokenCounter + 1;
- return newItemId;
+ tokenCounter = tokenCounter + 1;
+ return newItemId;
}
- // 注释掉
- // function _baseURI() internal view override returns (string memory) {
- // return "https://gateway.pinata.cloud/ipfs/";
- // }
+ // 注释掉
+ // function _baseURI() internal view override returns (string memory) {
+ // return "https://gateway.pinata.cloud/ipfs/";
+ // }
}
contract ERC721Holder is IERC721Receiver {
- function onERC721Received(
+ function onERC721Received(
address,
address,
uint256,
bytes memory
- ) public virtual override returns (bytes4) {
- return this.onERC721Received.selector;
+ ) public virtual override returns (bytes4) {
+ return this.onERC721Received.selector;
}
}
测试
- // QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB是json文件的hash
- //1. createCollectible(QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB)
- //2. tokenURI(0)
- //3. 浏览器请求:https://gateway.pinata.cloud/ipfs/QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB
+ // QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB是json文件的hash
+ //1. createCollectible(QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB)
+ //2. tokenURI(0)
+ //3. 浏览器请求:https://gateway.pinata.cloud/ipfs/QmSiaLLeuBKPvqAQz2VQWJHUJMz6xjMgagePha4kPd38TB
其中的hash值为这个nft图片的描述文件的哈希:metadata.json,
@@ -3188,9 +3184,9 @@ 测试
- 图片的哈希为:QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta
{
- "description": "this is a nft1155 metadata json desc",
- "image": "https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
- "name": "duke nft"
+ "description": "this is a nft1155 metadata json desc",
+ "image": "https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
+ "name": "duke nft"
}
总结
@@ -3202,20 +3198,20 @@ 授权比较
- ERC721
-// 对单个id进行approve
-function approve(address to, uint256 tokenId) public virtual override;
+// 对单个id进行approve
+function approve(address to, uint256 tokenId) public virtual override;
-// 对某个地址授权所有的token
-function setApprovalForAll(address operator, bool approved) public virtual override;
+// 对某个地址授权所有的token
+function setApprovalForAll(address operator, bool approved) public virtual override;
- ERC1155
-// 每个对单个id的授权!!
-// what -》不支持!
+// 每个对单个id的授权!!
+// what -》不支持!
-// 对某个地址授权所有的token
-function setApprovalForAll(address operator, bool approved) external;
+// 对某个地址授权所有的token
+function setApprovalForAll(address operator, bool approved) external;
ERC721A
ERC721A是一套实现了IERC721接口的合约,它的优势是在批量mint时,可以节约gas,知名NFT项目Azuki就是由这个合约开发的
@@ -3247,7 +3243,7 @@ ERC721A
@@ -3333,14 +3329,6 @@ ERC721A
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/03_ERC1155.html" "b/cn/03_EIP\345\215\217\350\256\256/03_ERC1155.html"
index 233e55b1..313b3e87 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/03_ERC1155.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/03_ERC1155.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3027,10 +3023,10 @@ 第3节:ERC1155(半同质
setURI的时候,填写的是metadata.json,不是图片的url,使用pinata服务存储图片和metadata.json:
{
- "description": "this is a nft1155 metadata json desc",
- //"image": "ipfs://QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
- "image": "https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
- "name": "duke nft"
+ "description": "this is a nft1155 metadata json desc",
+ //"image": "ipfs://QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
+ "image": "https://gateway.pinata.cloud/ipfs/QmTfK2CeRBkRqSZHmnekdZYSBrsKLQ6U5Px8MWtGf1Eqta",
+ "name": "duke nft"
}
@@ -3042,65 +3038,65 @@ 第3节:ERC1155(半同质
更好的合约:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.0;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.0;
-import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol";
+import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155URIStorage.sol";
contract MockErc1155Token is ERC1155URIStorage {
string public name;
string public symbol;
uint256 public tokenCounter;
- constructor(string memory _name, string memory _symbol) ERC1155("") {
+ constructor(string memory _name, string memory _symbol) ERC1155("") {
name = _name;
symbol = _symbol;
}
- function createCollectible(string memory tokenURI, uint256 _amount)
- external
- returns (uint256)
- {
+ function createCollectible(string memory tokenURI, uint256 _amount)
+ external
+ returns (uint256)
+ {
uint256 newItemId = tokenCounter;
- _mint(_msgSender(), newItemId, _amount, "");
+ _mint(_msgSender(), newItemId, _amount, "");
_setURI(newItemId, tokenURI);
- tokenCounter = tokenCounter + 1;
- return newItemId;
+ tokenCounter = tokenCounter + 1;
+ return newItemId;
}
- // function mint(uint256 _id, uint256 _amount) external {
- // _mint(_msgSender(), _id, _amount, "");
- // }
+ // function mint(uint256 _id, uint256 _amount) external {
+ // _mint(_msgSender(), _id, _amount, "");
+ // }
- // function mintBatch(uint256[] memory _ids, uint256[] memory _amounts)
- // external
- // {
- // _mintBatch(_msgSender(), _ids, _amounts, "");
- // }
+ // function mintBatch(uint256[] memory _ids, uint256[] memory _amounts)
+ // external
+ // {
+ // _mintBatch(_msgSender(), _ids, _amounts, "");
+ // }
- function burn(uint256 _id, uint256 _amount) external {
+ function burn(uint256 _id, uint256 _amount) external {
_burn(msg.sender, _id, _amount);
}
- function burnBatch(uint256[] memory _ids, uint256[] memory _amounts)
- external
- {
+ function burnBatch(uint256[] memory _ids, uint256[] memory _amounts)
+ external
+ {
_burnBatch(msg.sender, _ids, _amounts);
}
- function burnForMint(
+ function burnForMint(
address _from,
uint256[] memory _burnIds,
uint256[] memory _burnAmounts,
uint256[] memory _mintIds,
uint256[] memory _mintAmounts
- ) external {
+ ) external {
_burnBatch(_from, _burnIds, _burnAmounts);
- _mintBatch(_from, _mintIds, _mintAmounts, "");
+ _mintBatch(_from, _mintIds, _mintAmounts, "");
}
- function setURI(uint256 _id, string memory _uri) external {
+ function setURI(uint256 _id, string memory _uri) external {
_setURI(_id, _uri);
}
}
@@ -3132,7 +3128,7 @@ 更好的合约:
@@ -3218,14 +3214,6 @@ 更好的合约:
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/04_ERC165.html" "b/cn/03_EIP\345\215\217\350\256\256/04_ERC165.html"
index 5a98f5da..8eb4f988 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/04_ERC165.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/04_ERC165.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,36 +3020,36 @@ 概述
EIP-165接口定义
EIP165提供了一种检测智能合约类型的方法,在实现上它只定义了一个接口,同时也明确了计算interfaceId的规则:对该协议(如NFT721)的所有接口selector的hash值做^运算(异或,XOR,^),再取前四字节作为interfaceId。
interface IERC165 {
- /**
- * @dev Returns true if this contract implements the interface defined by
+ /**
+ * @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
- */
- function supportsInterface(bytes4 interfaceId) external view returns (bool);
+ */
+ function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
EIP165协议的interfaceId为0x01ffc9a7,计算公式:
0x01ffc9a7=bytes4(keccak256(supportsInterface.selector))
NFT721的interfaceid
-// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
-interface ERC721 /* is ERC165 */ {
+// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
+interface ERC721 /* is ERC165 */ {
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
- function balanceOf(address _owner) external view returns (uint256);
- function ownerOf(uint256 _tokenId) external view returns (address);
- function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
- function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
- function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
- function approve(address _approved, uint256 _tokenId) external payable;
- function setApprovalForAll(address _operator, bool _approved) external;
- function getApproved(uint256 _tokenId) external view returns (address);
- function isApprovedForAll(address _owner, address _operator) external view returns (bool);
+ function balanceOf(address _owner) external view returns (uint256);
+ function ownerOf(uint256 _tokenId) external view returns (address);
+ function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
+ function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
+ function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
+ function approve(address _approved, uint256 _tokenId) external payable;
+ function setApprovalForAll(address _operator, bool _approved) external;
+ function getApproved(uint256 _tokenId) external view returns (address);
+ function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
NFT721的interfaceId为:0x80ac58cd,计算公式为:
@@ -3061,11 +3057,11 @@ NFT721的interfaceid
当我们想判断一个合约的类型是否为NFT721时,只需检查一下supportsInterface(0x80ac58cd)
是否返回true即可。
NFT721Meta的interfaceId
-// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
-interface ERC721Metadata /* is ERC721 */ {
- function name() external view returns (string _name);
- function symbol() external view returns (string _symbol);
- function tokenURI(uint256 _tokenId) external view returns (string);
+// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
+interface ERC721Metadata /* is ERC721 */ {
+ function name() external view returns (string _name);
+ function symbol() external view returns (string _symbol);
+ function tokenURI(uint256 _tokenId) external view returns (string);
}
NFT721Meta的interfaceId为:0x5b5e139f,计算公式为:
@@ -3073,14 +3069,14 @@ NFT721Meta的interfaceId
在NFT721中实现supportsInterface
下面这个接口是NFT721中的标准实现
- function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
- return
- // 0x80ac58cd
+ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
+ return
+ // 0x80ac58cd
interfaceId == type(IERC721).interfaceId ||
- // 0x5b5e139f
+ // 0x5b5e139f
interfaceId == type(IERC721Metadata).interfaceId ||
- // 0x01ffc9a7
- super.supportsInterface(interfaceId);
+ // 0x01ffc9a7
+ super.supportsInterface(interfaceId);
}
总结
@@ -3115,7 +3111,7 @@ 总结
@@ -3201,14 +3197,6 @@ 总结
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/05_EIP2612.html" "b/cn/03_EIP\345\215\217\350\256\256/05_EIP2612.html"
index 3f9f44f5..4b2c6be2 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/05_EIP2612.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/05_EIP2612.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3051,7 +3047,7 @@ 第5节:EIP2612(Permit相关)
@@ -3137,14 +3133,6 @@ 第5节:EIP2612(Permit相关)
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/06_EIP712.html" "b/cn/03_EIP\345\215\217\350\256\256/06_EIP712.html"
index cd5279b8..cd15e277 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/06_EIP712.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/06_EIP712.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3046,7 +3042,7 @@ 第6节:EIP712(结构化签
@@ -3132,14 +3128,6 @@ 第6节:EIP712(结构化签
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/07_ERC5114.html" "b/cn/03_EIP\345\215\217\350\256\256/07_ERC5114.html"
index 5e32c78e..2d3b02a1 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/07_ERC5114.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/07_ERC5114.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3054,7 +3050,7 @@ SBT_NFT721
@@ -3140,14 +3136,6 @@ SBT_NFT721
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/08_ERC1820.html" "b/cn/03_EIP\345\215\217\350\256\256/08_ERC1820.html"
index 8200867d..d6f345be 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/08_ERC1820.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/08_ERC1820.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3046,7 +3042,7 @@ 第8节:ERC1820
@@ -3132,14 +3128,6 @@ 第8节:ERC1820
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/09_ERC4626.html" "b/cn/03_EIP\345\215\217\350\256\256/09_ERC4626.html"
index 0f2d1d64..efa7279d 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/09_ERC4626.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/09_ERC4626.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3049,7 +3045,7 @@ 第9节:EIP4626
@@ -3135,14 +3131,6 @@ 第9节:EIP4626
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/10_EIP1559.html" "b/cn/03_EIP\345\215\217\350\256\256/10_EIP1559.html"
index fce79d8b..3b988897 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/10_EIP1559.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/10_EIP1559.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3081,7 +3077,7 @@ 3. 主网2021年已经支持了
@@ -3167,14 +3163,6 @@ 3. 主网2021年已经支持了
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/20_NFT\347\220\206\345\277\265\346\216\242\350\256\250.html" "b/cn/03_EIP\345\215\217\350\256\256/20_NFT\347\220\206\345\277\265\346\216\242\350\256\250.html"
index 575d14a6..8a8c427c 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/20_NFT\347\220\206\345\277\265\346\216\242\350\256\250.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/20_NFT\347\220\206\345\277\265\346\216\242\350\256\250.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3090,7 +3086,7 @@ so each t
Almost no top NFTs convey IP rights, Galaxy found
-In the case of Moonbirds, Galaxy found its switch from commercial use licensing to Creative Commons (CC0) — without community consent — highlights the fact that Moonbirds holders never owned any intellectual property (IP). The parent company behind Moonbirds and Oddities was calling the shots.
+In the case of Moonbirds, Galaxy found its switch from commercial use licensing to Creative Commons (CC0) — without community consent — highlights the fact that Moonbirds holders never owned any intellectual property (IP). The parent company behind Moonbirds and Oddities was calling the shots.
World of Women (WoW).
@@ -3102,8 +3098,8 @@ so each t
新:他说的不是NFT的IP权力如何转移,而是说NFT如何帮助转移IP权力
-# 1. 如果NFT是有权利的(CC0,或者创建的时候,明确有IP的),那么可以帮助转移
-# 2. 如果发型的时候没有赋予IP权力,那么则无法帮助完成IP权力转移。
+# 1. 如果NFT是有权利的(CC0,或者创建的时候,明确有IP的),那么可以帮助转移
+# 2. 如果发型的时候没有赋予IP权力,那么则无法帮助完成IP权力转移。
New challenges for IP rights owners? 知识产权所有者面临的新挑战?
@@ -3111,9 +3107,9 @@ so each t
这种IP权限的不确定,是有别于传统IP的,如何找到有效方式消除困惑,是需要解决的挑战之一。
-# 你的创作被其他人发行了NFT(电影)
+# 你的创作被其他人发行了NFT(电影)
-# 如果两个NFT高度相似
+# 如果两个NFT高度相似
These two separate ownership mechanisms (corpus mysticum with the copyright ownership of the art, design, etc. of the NFT and corpus mechanicum with the ownership of the NFT per se) can confuse and complicate the transfer of NFTs’ copyright ownership.
@@ -3121,7 +3117,7 @@ so each t
Could smart contracts be used for IP agreements? 智能合约能否用于知识产权协议?
-# 不仅局限于NFT了,这次仅仅说的是智能合约,所以可以把话题扩大来讲
+# 不仅局限于NFT了,这次仅仅说的是智能合约,所以可以把话题扩大来讲
答案是肯定的,目前的IP权限是没有写入智能合约的,而是有链下运营来决定的,我们完全可以把IP权限直接在合约部署的时候就写入代码中
@@ -3243,7 +3239,7 @@ 参考链接
@@ -3329,14 +3325,6 @@ 参考链接
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/21_metaverse.html" "b/cn/03_EIP\345\215\217\350\256\256/21_metaverse.html"
index 83309026..dc2d0ea5 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/21_metaverse.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/21_metaverse.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3052,7 +3048,7 @@ 第9章:Metaverse
@@ -3138,14 +3134,6 @@ 第9章:Metaverse
-
-
-
-
-
-
-
-
diff --git "a/cn/03_EIP\345\215\217\350\256\256/index.html" "b/cn/03_EIP\345\215\217\350\256\256/index.html"
index 23697083..d537f423 100644
--- "a/cn/03_EIP\345\215\217\350\256\256/index.html"
+++ "b/cn/03_EIP\345\215\217\350\256\256/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3069,7 +3065,7 @@ ERC和EIP的定义与区别?
@@ -3155,14 +3151,6 @@ ERC和EIP的定义与区别?
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/01_\351\207\215\345\205\245\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/01_\351\207\215\345\205\245\346\224\273\345\207\273.html"
index 6cc2d904..0062129c 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/01_\351\207\215\345\205\245\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/01_\351\207\215\345\205\245\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,10 +3020,10 @@ 第1节:重入攻击(Re-entry
假设:我们在A合约中调用B合约的方法
重入攻击是指:A合约调用B(恶意合约)结束之前,B(恶意合约)内的函数反向调用A(原合约)的函数实现的攻击。
以下案例是在使用call进行ether转账时,在fallback函数中再次调用了call转账,从而实现攻击效果。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-/*
+/*
EtherStore is a contract where you can deposit and withdraw ETH.
This contract is vulnerable to re-entrancy attack.
Let's see why.
@@ -3052,54 +3048,54 @@ 第1节:重入攻击(Re-entry
- Attack.fallback (receives 1 Ether)
- EtherStore.withdraw
- Attack fallback (receives 1 Ether)
-*/
+*/
contract EtherStore {
- mapping(address => uint) public balances;
+ mapping(address => uint) public balances;
- function deposit() public payable {
+ function deposit() public payable {
balances[msg.sender] += msg.value;
}
- function withdraw() public {
+ function withdraw() public {
uint bal = balances[msg.sender];
- require(bal > 0);
+ require(bal > 0);
- (bool sent, ) = msg.sender.call{value: bal}("");
- require(sent, "Failed to send Ether");
+ (bool sent, ) = msg.sender.call{value: bal}("");
+ require(sent, "Failed to send Ether");
- balances[msg.sender] = 0;
+ balances[msg.sender] = 0;
}
- // Helper function to check the balance of this contract
- function getBalance() public view returns (uint) {
- return address(this).balance;
+ // Helper function to check the balance of this contract
+ function getBalance() public view returns (uint) {
+ return address(this).balance;
}
}
contract Attack {
EtherStore public etherStore;
- constructor(address _etherStoreAddress) {
+ constructor(address _etherStoreAddress) {
etherStore = EtherStore(_etherStoreAddress);
}
- // Fallback is called when EtherStore sends Ether to this contract.
+ // Fallback is called when EtherStore sends Ether to this contract.
fallback() external payable {
- if (address(etherStore).balance >= 1 ether) {
+ if (address(etherStore).balance >= 1 ether) {
etherStore.withdraw();
}
}
- function attack() external payable {
- require(msg.value >= 1 ether);
- etherStore.deposit{value: 1 ether}();
+ function attack() external payable {
+ require(msg.value >= 1 ether);
+ etherStore.deposit{value: 1 ether}();
etherStore.withdraw();
}
- // Helper function to check the balance of this contract
- function getBalance() public view returns (uint) {
- return address(this).balance;
+ // Helper function to check the balance of this contract
+ function getBalance() public view returns (uint) {
+ return address(this).balance;
}
}
@@ -3107,49 +3103,56 @@ 第1节:重入攻击(Re-entry
- 在调用外部合约之前,优先更新状态变量;
- function withdraw() public {
+ function withdraw() public {
uint bal = balances[msg.sender];
- require(bal > 0);
+ require(bal > 0);
+
+ balances[msg.sender] = 0; //<<<=== 提前修改状态变量
- balances[msg.sender] = 0; //<<
+ (bool sent, ) = msg.sender.call{value: bal}("");
+ require(sent, "Failed to send Ether");
+ }
+
- 使用修饰器,阻止重入攻击
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract ReEntrancyGuard {
bool internal locked;
- modifier noReentrant() {
- require(!locked, "No re-entrancy");
- locked = true;
+ modifier noReentrant() {
+ require(!locked, "No re-entrancy");
+ locked = true;
_;
- locked = false;
+ locked = false;
}
}
contract EtherStore is ReEntrancyGuard {
- mapping(address => uint) public balances;
+ mapping(address => uint) public balances;
- function deposit() public payable {
+ function deposit() public payable {
balances[msg.sender] += msg.value;
}
- function withdraw() public noReentrant { //<<<=== 增加修饰器 uint bal="balances[msg.sender];" require(bal> 0);
+ function withdraw() public noReentrant { //<<<=== 增加修饰器
+ uint bal = balances[msg.sender];
+ require(bal > 0);
- (bool sent, ) = msg.sender.call{value: bal}("");
- require(sent, "Failed to send Ether");
+ (bool sent, ) = msg.sender.call{value: bal}("");
+ require(sent, "Failed to send Ether");
- balances[msg.sender] = 0;
+ balances[msg.sender] = 0;
}
- // Helper function to check the balance of this contract
- function getBalance() public view returns (uint) {
- return address(this).balance;
+ // Helper function to check the balance of this contract
+ function getBalance() public view returns (uint) {
+ return address(this).balance;
}
}
-===>
+
- 原理分析:
@@ -3181,7 +3184,7 @@ 第1节:重入攻击(Re-entry
@@ -3267,14 +3270,6 @@ 第1节:重入攻击(Re-entry
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/02_\350\277\224\345\233\236\345\200\274\346\240\241\351\252\214.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/02_\350\277\224\345\233\236\345\200\274\346\240\241\351\252\214.html"
index 02bcaf58..d881fdbf 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/02_\350\277\224\345\233\236\345\200\274\346\240\241\351\252\214.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/02_\350\277\224\345\233\236\345\200\274\346\240\241\351\252\214.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,18 +3020,18 @@ 第2节:返回值校验
调用外部合约函数时,有些函数调用失败不会抛出错误回滚交易而是返回 false,如果忘记检查函数返回值会导致误以为调用成功。
例如:approve等方法就是不安全的,因此引入了safeApprove
当使用call方法调用approve时,如果没有校验返回值,则即使失败,也不会抛出异常。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.8;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.8;
contract ReturnValue {
- function callchecked(address callee) public {
- (bool success, ) = callee.call("");
- require(success, "call failed!");
+ function callchecked(address callee) public {
+ (bool success, ) = callee.call("");
+ require(success, "call failed!");
}
- function callnotchecked(address callee) public {
- // 未校验
- (bool success, ) = callee.call("");
+ function callnotchecked(address callee) public {
+ // 未校验
+ (bool success, ) = callee.call("");
}
}
@@ -3066,7 +3062,7 @@ 第2节:返回值校验
@@ -3152,14 +3148,6 @@ 第2节:返回值校验
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/03_\345\220\210\347\272\246\350\207\252\346\235\200\345\257\274\350\207\264Dos.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/03_\345\220\210\347\272\246\350\207\252\346\235\200\345\257\274\350\207\264Dos.html"
index 687db375..ab6cb1a3 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/03_\345\220\210\347\272\246\350\207\252\346\235\200\345\257\274\350\207\264Dos.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/03_\345\220\210\347\272\246\350\207\252\346\235\200\345\257\274\350\207\264Dos.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,14 +3019,14 @@ 第3节:Self Destruct
合约自杀时,会将合约自身持有的ether全部转入到指定地址之中。
如果某个合约使用了balance方法来进行校验时,有可能会出现攻击漏洞。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-// The goal of this game is to be the 7th player to deposit 1 Ether.
-// Players can deposit only 1 Ether at a time.
-// Winner will be able to withdraw all Ether.
+// The goal of this game is to be the 7th player to deposit 1 Ether.
+// Players can deposit only 1 Ether at a time.
+// Winner will be able to withdraw all Ether.
-/*
+/*
1. Deploy EtherGame
2. Players (say Alice and Bob) decides to play, deposits 1 Ether each.
2. Deploy Attack with address of EtherGame
@@ -3040,40 +3036,78 @@ 第3节:Self Destruct
What happened?
Attack forced the balance of EtherGame to equal 7 ether.
Now no one can deposit and the winner cannot be set.
-*/
+*/
contract EtherGame {
- uint public targetAmount = 7 ether;
+ uint public targetAmount = 7 ether;
address public winner;
- function deposit() public payable {
- require(msg.value == 1 ether, "You can only send 1 Ether");
+ function deposit() public payable {
+ require(msg.value == 1 ether, "You can only send 1 Ether");
+
+ uint balance = address(this).balance;
+ require(balance <= targetAmount, "Game is over");
+
+ if (balance == targetAmount) {
+ winner = msg.sender;
+ }
+ }
+
+ function claimReward() public {
+ require(msg.sender == winner, "Not winner");
+
+ (bool sent, ) = msg.sender.call{value: address(this).balance}("");
+ require(sent, "Failed to send Ether");
+ }
+}
+
+contract Attack {
+ EtherGame etherGame;
+
+ constructor(EtherGame _etherGame) {
+ etherGame = EtherGame(_etherGame);
+ }
- uint balance = address(this).balance;
- require(balance <= targetamount, "game is over"); if (balance="=" targetamount) { winner="msg.sender;" } function claimreward() public require(msg.sender="=" winner, "not winner"); (bool sent, )="msg.sender.call{value:" address(this).balance}(""); require(sent, "failed to send ether"); contract attack ethergame ethergame; constructor(ethergame _ethergame) attack() payable you can simply break the game by sending ether so that balance>= 7 ether
+ function attack() public payable {
+ // You can simply break the game by sending ether so that
+ // the game balance >= 7 ether
- // cast address to payable
+ // cast address to payable
address payable addr = payable(address(etherGame));
selfdestruct(addr);
}
}
-=>
+
解决方案:不要依赖address(this).balance来进行校验
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract EtherGame {
- uint public targetAmount = 3 ether;
+ uint public targetAmount = 3 ether;
uint public balance;
address public winner;
- function deposit() public payable {
- require(msg.value == 1 ether, "You can only send 1 Ether");
+ function deposit() public payable {
+ require(msg.value == 1 ether, "You can only send 1 Ether");
- // !!通过状态变量来统计ether,此时即使合约自杀传入ether,也不会干扰条件判断逻辑!!
+ // !!通过状态变量来统计ether,此时即使合约自杀传入ether,也不会干扰条件判断逻辑!!
balance += msg.value;
- require(balance
+ require(balance <= targetAmount, "Game is over");
+
+ if (balance == targetAmount) {
+ winner = msg.sender;
+ }
+ }
+
+ function claimReward() public {
+ require(msg.sender == winner, "Not winner");
+
+ (bool sent, ) = msg.sender.call{value: balance}("");
+ require(sent, "Failed to send Ether");
+ }
+}
+
@@ -3101,7 +3135,7 @@ 第3节:Self Destruct
@@ -3187,14 +3221,6 @@ 第3节:Self Destruct
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/04_\350\257\273\345\217\226\347\247\201\346\234\211\345\217\230\351\207\217.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/04_\350\257\273\345\217\226\347\247\201\346\234\211\345\217\230\351\207\217.html"
index 0dba63bc..dba6919f 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/04_\350\257\273\345\217\226\347\247\201\346\234\211\345\217\230\351\207\217.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/04_\350\257\273\345\217\226\347\247\201\346\234\211\345\217\230\351\207\217.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,83 +3019,83 @@ 第4节:访问私有数据Pr
所有链上的数据都可以被访问;不要把任何敏感信息存储在链上
private修饰符表示链上合约无法访问该数据,但是可以通过链下(读取slot的方式读取私有数据)
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-/*
+/*
Note: cannot use web3 on JVM, so use the contract deployed on ropsten
Note: browser Web3 is old so use Web3 from truffle console
Contract deployed on Ropsten
0x3505a02BCDFbb225988161a95528bfDb279faD6b
-*/
+*/
-/*
+/*
# Storage
- 2 ** 256 slots
- 32 bytes for each slot
- data is stored sequentially in the order of declaration
- storage is optimized to save space. If neighboring variables fit in a single
32 bytes, then they are packed into the same slot, starting from the right
-*/
+*/
contract Vault {
- // slot 0
- uint public count = 123;
- // slot 1
+ // slot 0
+ uint public count = 123;
+ // slot 1
address public owner = msg.sender;
- bool public isTrue = true;
- uint16 public u16 = 31;
- // slot 2
+ bool public isTrue = true;
+ uint16 public u16 = 31;
+ // slot 2
bytes32 private password;
- // constants do not use storage
- uint public constant someConst = 123;
+ // constants do not use storage
+ uint public constant someConst = 123;
- // slot 3, 4, 5 (one for each array element)
- bytes32[3] public data;
+ // slot 3, 4, 5 (one for each array element)
+ bytes32[3] public data;
struct User {
uint id;
bytes32 password;
}
- // slot 6 - length of array
- // starting from slot hash(6) - array elements
- // slot where array element is stored = keccak256(slot)) + (index * elementSize)
- // where slot = 6 and elementSize = 2 (1 (uint) + 1 (bytes32))
+ // slot 6 - length of array
+ // starting from slot hash(6) - array elements
+ // slot where array element is stored = keccak256(slot)) + (index * elementSize)
+ // where slot = 6 and elementSize = 2 (1 (uint) + 1 (bytes32))
User[] private users;
- // slot 7 - empty
- // entries are stored at hash(key, slot)
- // where slot = 7, key = map key
- mapping(uint => User) private idToUser;
+ // slot 7 - empty
+ // entries are stored at hash(key, slot)
+ // where slot = 7, key = map key
+ mapping(uint => User) private idToUser;
- constructor(bytes32 _password) {
+ constructor(bytes32 _password) {
password = _password;
}
- function addUser(bytes32 _password) public {
- User memory user = User({id: users.length, password: _password});
+ function addUser(bytes32 _password) public {
+ User memory user = User({id: users.length, password: _password});
users.push(user);
idToUser[user.id] = user;
}
- function getArrayLocation(
+ function getArrayLocation(
uint slot,
uint index,
uint elementSize
- ) public pure returns (uint) {
- return uint(keccak256(abi.encodePacked(slot))) + (index * elementSize);
+ ) public pure returns (uint) {
+ return uint(keccak256(abi.encodePacked(slot))) + (index * elementSize);
}
- function getMapLocation(uint slot, uint key) public pure returns (uint) {
- return uint(keccak256(abi.encodePacked(key, slot)));
+ function getMapLocation(uint slot, uint key) public pure returns (uint) {
+ return uint(keccak256(abi.encodePacked(key, slot)));
}
}
-/*
+/*
slot 0 - count
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", 0, console.log)
slot 1 - u16, isTrue, owner
@@ -3128,7 +3124,7 @@ 第4节:访问私有数据Pr
user 1
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b828", console.log)
web3.eth.getStorageAt("0x3505a02BCDFbb225988161a95528bfDb279faD6b", "0xb39221ace053465ec3453ce2b36430bd138b997ecea25c1043da0c366812b829", console.log)
-*/
+*/
在线验证:https://web3playground.io/QmQgatm8kvy3QcWugTTiegiAz8mnqLkaXGRRu37yX1ucFq
@@ -3158,7 +3154,7 @@ 第4节:访问私有数据Pr
@@ -3244,14 +3240,6 @@ 第4节:访问私有数据Pr
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/11_\345\256\211\345\205\250\344\272\213\346\225\2051-poly\347\275\221\347\273\234\345\223\210\345\270\214\347\242\260\346\222\236\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/11_\345\256\211\345\205\250\344\272\213\346\225\2051-poly\347\275\221\347\273\234\345\223\210\345\270\214\347\242\260\346\222\236\346\224\273\345\207\273.html"
index cc5ab262..06f89eb3 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/11_\345\256\211\345\205\250\344\272\213\346\225\2051-poly\347\275\221\347\273\234\345\223\210\345\270\214\347\242\260\346\222\236\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/11_\345\256\211\345\205\250\344\272\213\346\225\2051-poly\347\275\221\347\273\234\345\223\210\345\270\214\347\242\260\346\222\236\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3055,7 +3051,7 @@ 第11节:安全
@@ -3141,14 +3137,6 @@ 第11节:安全
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/12_\345\256\211\345\205\250\344\272\213\346\225\2052-OP\344\273\243\345\270\201\351\207\215\346\224\276\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/12_\345\256\211\345\205\250\344\272\213\346\225\2052-OP\344\273\243\345\270\201\351\207\215\346\224\276\346\224\273\345\207\273.html"
index 7f779dc9..10a678d7 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/12_\345\256\211\345\205\250\344\272\213\346\225\2052-OP\344\273\243\345\270\201\351\207\215\346\224\276\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/12_\345\256\211\345\205\250\344\272\213\346\225\2052-OP\344\273\243\345\270\201\351\207\215\346\224\276\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3064,7 +3060,7 @@ 第12节:安全事故2-
@@ -3150,14 +3146,6 @@ 第12节:安全事故2-
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/13_\345\256\211\345\205\250\344\272\213\346\225\2053-FTX\345\233\240\345\205\215\346\211\213\347\273\255\350\264\271\346\217\220\345\270\201\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/13_\345\256\211\345\205\250\344\272\213\346\225\2053-FTX\345\233\240\345\205\215\346\211\213\347\273\255\350\264\271\346\217\220\345\270\201\346\224\273\345\207\273.html"
index 016edd61..4eb4f33c 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/13_\345\256\211\345\205\250\344\272\213\346\225\2053-FTX\345\233\240\345\205\215\346\211\213\347\273\255\350\264\271\346\217\220\345\270\201\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/13_\345\256\211\345\205\250\344\272\213\346\225\2053-FTX\345\233\240\345\205\215\346\211\213\347\273\255\350\264\271\346\217\220\345\270\201\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3098,7 +3094,7 @@ 本文涉及到的工具
@@ -3184,14 +3180,6 @@ 本文涉及到的工具
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/14_\345\256\211\345\205\250\344\272\213\346\225\2054-profanity\345\274\261\351\232\217\346\234\272\346\225\260\346\232\264\345\212\233\347\240\264\350\247\243\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/14_\345\256\211\345\205\250\344\272\213\346\225\2054-profanity\345\274\261\351\232\217\346\234\272\346\225\260\346\232\264\345\212\233\347\240\264\350\247\243\346\224\273\345\207\273.html"
index 1099c8d2..d6380887 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/14_\345\256\211\345\205\250\344\272\213\346\225\2054-profanity\345\274\261\351\232\217\346\234\272\346\225\260\346\232\264\345\212\233\347\240\264\350\247\243\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/14_\345\256\211\345\205\250\344\272\213\346\225\2054-profanity\345\274\261\351\232\217\346\234\272\346\225\260\346\232\264\345\212\233\347\240\264\350\247\243\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3049,7 +3045,7 @@ 第14
@@ -3135,14 +3131,6 @@ 第14
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/15_\345\256\211\345\205\250\344\272\213\346\225\2055-\345\207\272\345\235\227\347\233\270\345\205\263\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/15_\345\256\211\345\205\250\344\272\213\346\225\2055-\345\207\272\345\235\227\347\233\270\345\205\263\346\224\273\345\207\273.html"
index a29f29af..225c4507 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/15_\345\256\211\345\205\250\344\272\213\346\225\2055-\345\207\272\345\235\227\347\233\270\345\205\263\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/15_\345\256\211\345\205\250\344\272\213\346\225\2055-\345\207\272\345\235\227\347\233\270\345\205\263\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3064,7 +3060,7 @@ 第15节:安全事故5-
@@ -3150,14 +3146,6 @@ 第15节:安全事故5-
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/16_\345\256\211\345\205\250\344\272\213\346\225\2056-msgvalue\346\214\201\344\271\205\345\214\226\351\227\256\351\242\230.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/16_\345\256\211\345\205\250\344\272\213\346\225\2056-msgvalue\346\214\201\344\271\205\345\214\226\351\227\256\351\242\230.html"
index ac653165..16947c79 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/16_\345\256\211\345\205\250\344\272\213\346\225\2056-msgvalue\346\214\201\344\271\205\345\214\226\351\227\256\351\242\230.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/16_\345\256\211\345\205\250\344\272\213\346\225\2056-msgvalue\346\214\201\344\271\205\345\214\226\351\227\256\351\242\230.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3022,38 +3018,38 @@ 第16节:安全事
小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。
在如下代码中,存在msg.value被重复使用的问题:在delegatecall中,msg.value与当前batch函数中msg.value一致,这可能导致错误。
-function batch(bytes[] calldata calls, bool revertOnFail) external payable returns(bool[] memory success, bytes[] memory results) {
- successes = new bool[](calls.length);
- results = new bytes[](calls.length);
+function batch(bytes[] calldata calls, bool revertOnFail) external payable returns(bool[] memory success, bytes[] memory results) {
+ successes = new bool[](calls.length);
+ results = new bytes[](calls.length);
- for (uint256 i=0; i< calls.length; i++) {
- (bool success, bytes memory result) = address(this).delegatecall(calls[i]);
- require(success || !revertOnFail, _getReyerMsg(result));
+ for (uint256 i=0; i< calls.length; i++) {
+ (bool success, bytes memory result) = address(this).delegatecall(calls[i]);
+ require(success || !revertOnFail, _getReyerMsg(result));
successes[i] = success;
results[i] = result;
}
}
我们写一个测试案例:
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
contract batch {
uint public num;
address public sender;
uint public value;
- function mintBatch(uint _num) public payable {
- // Proxy's storage is set, Implementation is not modified.
- for (uint i = 0; i < _num; i++) {
- (bool success, bytes memory data) = address(this).delegatecall(
- abi.encodeWithSignature("mint(uint256)", _num)
+ function mintBatch(uint _num) public payable {
+ // Proxy's storage is set, Implementation is not modified.
+ for (uint i = 0; i < _num; i++) {
+ (bool success, bytes memory data) = address(this).delegatecall(
+ abi.encodeWithSignature("mint(uint256)", _num)
);
}
}
- function mint(uint _num) public payable {
- require(msg.value == 1 ether, "error");
+ function mint(uint _num) public payable {
+ require(msg.value == 1 ether, "error");
num = _num;
sender = msg.sender;
value += msg.value;
@@ -3097,7 +3093,7 @@ 第16节:安全事
@@ -3183,14 +3179,6 @@ 第16节:安全事
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/17_\345\256\211\345\205\250\344\272\213\346\225\2057-\347\224\250EOA\346\235\245call\346\226\271\346\263\225\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/17_\345\256\211\345\205\250\344\272\213\346\225\2057-\347\224\250EOA\346\235\245call\346\226\271\346\263\225\346\224\273\345\207\273.html"
index 87475c8a..e9866325 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/17_\345\256\211\345\205\250\344\272\213\346\225\2057-\347\224\250EOA\346\235\245call\346\226\271\346\263\225\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/17_\345\256\211\345\205\250\344\272\213\346\225\2057-\347\224\250EOA\346\235\245call\346\226\271\346\263\225\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,47 +3026,47 @@ 第17节:安全事
- 攻击案例:QBridege安全事件
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.13;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.13;
-import "hardhat/console.sol";
+import "hardhat/console.sol";
contract Test {
- function TestEOA(uint _num) public payable {
- address EOA = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
+ function TestEOA(uint _num) public payable {
+ address EOA = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
(bool success, bytes memory data) = address(EOA).call(
- abi.encodeWithSignature("mint(uint256)", _num)
+ abi.encodeWithSignature("mint(uint256)", _num)
);
- // 这个居然是true,这个存在攻击可能
- console.log("result:", success);
+ // 这个居然是true,这个存在攻击可能
+ console.log("result:", success);
}
- function TestNotExist(uint _num) public payable {
- (bool success, bytes memory data) = address(this).call(
- abi.encodeWithSignature("notExist(uint256)", _num)
+ function TestNotExist(uint _num) public payable {
+ (bool success, bytes memory data) = address(this).call(
+ abi.encodeWithSignature("notExist(uint256)", _num)
);
- // true,因为执行了fallback
- // 如果fallback不存在,则返回false,但是不会抛异常
- console.log("result:", success);
+ // true,因为执行了fallback
+ // 如果fallback不存在,则返回false,但是不会抛异常
+ console.log("result:", success);
}
- function TestExist(uint _num) public payable {
- (bool success, bytes memory data) = address(this).call(
- abi.encodeWithSignature("mint(uint256)", _num)
+ function TestExist(uint _num) public payable {
+ (bool success, bytes memory data) = address(this).call(
+ abi.encodeWithSignature("mint(uint256)", _num)
);
- // true
- console.log("result:", success);
+ // true
+ console.log("result:", success);
}
- function mint(uint _num) public payable {
- console.log("mint called:", _num);
+ function mint(uint _num) public payable {
+ console.log("mint called:", _num);
}
fallback() external payable {
- console.log("fallback called!");
+ console.log("fallback called!");
}
}
@@ -3106,7 +3102,7 @@ 第17节:安全事
@@ -3192,14 +3188,6 @@ 第17节:安全事
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/18_\345\256\211\345\205\250\344\272\213\346\225\2058-\345\217\257\347\207\203\347\203\247\344\273\243\345\270\201\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/18_\345\256\211\345\205\250\344\272\213\346\225\2058-\345\217\257\347\207\203\347\203\247\344\273\243\345\270\201\346\224\273\345\207\273.html"
index e4e4359d..117e572f 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/18_\345\256\211\345\205\250\344\272\213\346\225\2058-\345\217\257\347\207\203\347\203\247\344\273\243\345\270\201\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/18_\345\256\211\345\205\250\344\272\213\346\225\2058-\345\217\257\347\207\203\347\203\247\344\273\243\345\270\201\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3020,24 +3016,24 @@
第18节:安全事故8-可燃烧代币攻击
有些代币在转账时会销毁一部分转账费用,导致实际收到的代币余额偏少,如果开发者没考虑到这一点,以转账值计算,会导致出现偏差。
我们可以通过校验转账前后的token余额来进行校验,确保转账之后确实收到了预期数量的token,从而防止作恶行为。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.8;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.8;
contract StakePool {
- function stake(address _token_addr, uint256 _amount) public {
- // 1. 检查转账之前的余额
- uint256 balance_before_transfer = IERC20(_token_addr).balanceOf(address(this));
+ function stake(address _token_addr, uint256 _amount) public {
+ // 1. 检查转账之前的余额
+ uint256 balance_before_transfer = IERC20(_token_addr).balanceOf(address(this));
- // 2. 转账
- IERC20(_token_addr).safeTransferFrom(msg.sender, address(this), _amount);
+ // 2. 转账
+ IERC20(_token_addr).safeTransferFrom(msg.sender, address(this), _amount);
- // 3. 检查转账之后的余额
- uint256 balance_after_transfer = IERC20(_token_addr).balanceOf(address(this));
+ // 3. 检查转账之后的余额
+ uint256 balance_after_transfer = IERC20(_token_addr).balanceOf(address(this));
- // 4. 计算差值,得到实际收取到的金额,进行校验!
+ // 4. 计算差值,得到实际收取到的金额,进行校验!
uint256 received_amount = balance_after_transfer - balance_before_transfer;
- require(received_amount >= _amount, "received should gt _amount");
+ require(received_amount >= _amount, "received should gt _amount");
}
}
@@ -3068,7 +3064,7 @@ 第18节:安全事故8
@@ -3154,14 +3150,6 @@ 第18节:安全事故8
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/19_\345\256\211\345\205\250\344\272\213\346\225\2059-\347\255\276\345\220\215\351\252\214\350\257\201\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/19_\345\256\211\345\205\250\344\272\213\346\225\2059-\347\255\276\345\220\215\351\252\214\350\257\201\346\224\273\345\207\273.html"
index 235e46c6..3618dd6b 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/19_\345\256\211\345\205\250\344\272\213\346\225\2059-\347\255\276\345\220\215\351\252\214\350\257\201\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/19_\345\256\211\345\205\250\344\272\213\346\225\2059-\347\255\276\345\220\215\351\252\214\350\257\201\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3049,7 +3045,7 @@ 第19节:安全事故9-
@@ -3135,14 +3131,6 @@ 第19节:安全事故9-
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/20_\345\256\211\345\205\250\344\272\213\346\225\20510-delegatecall\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/20_\345\256\211\345\205\250\344\272\213\346\225\20510-delegatecall\346\224\273\345\207\273.html"
index 23182cf6..0e79af96 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/20_\345\256\211\345\205\250\344\272\213\346\225\20510-delegatecall\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/20_\345\256\211\345\205\250\344\272\213\346\225\20510-delegatecall\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3020,31 +3016,31 @@
第20节:安全事故10-delegatecall攻击
当合约中存在delegatecall操作时,要格外小心,我们可以把使用delegatecall的合约当成代理合约,可以通过业务合约(Attack合约)来对代理合约进行升级,从而修改了代理合约中的数据。
以下案例中,我们通过Attack合约完成了对Proxy合约的owner修改。
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.8;
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.8.8;
-// 操作步骤:
-// 1. 使用account0部署Proxy,此时owner是account0
-// 2. 使用account1部署Attack,得到地址attack_addr
-// 3. 调用forward(attack_addr, 0x40caae06)
-// 4. 检查owner地址,此时由account0变成了account1
+// 操作步骤:
+// 1. 使用account0部署Proxy,此时owner是account0
+// 2. 使用account1部署Attack,得到地址attack_addr
+// 3. 调用forward(attack_addr, 0x40caae06)
+// 4. 检查owner地址,此时由account0变成了account1
-contract Proxy {
+contract Proxy {
address public owner;
- constructor() {
+ constructor() {
owner = msg.sender;
}
- // _data: 0x40caae06
- function forward(address callee, bytes memory _data) public {
+ // _data: 0x40caae06
+ function forward(address callee, bytes memory _data) public {
(bool success, ) = callee.delegatecall(_data);
- require(success, "tx failed!");
+ require(success, "tx failed!");
}
}
contract Attack {
address public owner;
- function setOwner() public {
+ function setOwner() public {
owner = tx.origin;
}
}
@@ -3081,7 +3077,7 @@ 第20节:安全事故10-
@@ -3167,14 +3163,6 @@ 第20节:安全事故10-
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/21_\345\256\211\345\205\250\344\272\213\346\225\20511-tx.origin\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/21_\345\256\211\345\205\250\344\272\213\346\225\20511-tx.origin\346\224\273\345\207\273.html"
index 3a72d1a9..4b6efa04 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/21_\345\256\211\345\205\250\344\272\213\346\225\20511-tx.origin\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/21_\345\256\211\345\205\250\344\272\213\346\225\20511-tx.origin\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,34 +3020,34 @@ 第21节:安全事故11-tx.o
没有增加重入检查
校验条件错误:tx.origin == owner,这个在重入时是无效的
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.11;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.11;
-// THIS CONTRACT CONTAINS A BUG - DO NOT USE
+// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract MyWallet {
address owner;
- constructor() {
+ constructor() {
owner = msg.sender;
}
receive () external payable {
}
- function transferTo(address payable dest, uint amount) public {
- // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
- require(tx.origin == owner);
+ function transferTo(address payable dest, uint amount) public {
+ // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
+ require(tx.origin == owner);
- // At this point, the caller's code is executed, and can call withdrawBalance again
- (bool success, ) = dest.call{value: amount}("");
- require(success);
+ // At this point, the caller's code is executed, and can call withdrawBalance again
+ (bool success, ) = dest.call{value: amount}("");
+ require(success);
}
}
interface UserWallet {
- function transferTo(address payable dest, uint amount) external;
+ function transferTo(address payable dest, uint amount) external;
}
contract Attack {
address payable owner;
- constructor() {
+ constructor() {
owner = payable(msg.sender);
}
receive() external payable {
@@ -3093,7 +3089,7 @@ 第21节:安全事故11-tx.o
@@ -3179,14 +3175,6 @@ 第21节:安全事故11-tx.o
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/22_\345\256\211\345\205\250\344\272\213\346\225\20512-DOS\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/22_\345\256\211\345\205\250\344\272\213\346\225\20512-DOS\346\224\273\345\207\273.html"
index a22708af..e79ca1f3 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/22_\345\256\211\345\205\250\344\272\213\346\225\20512-DOS\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/22_\345\256\211\345\205\250\344\272\213\346\225\20512-DOS\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3019,24 +3015,24 @@
第22节:安全事故12-DOS攻击
调用外部合约可能永久失败导致本合约不能接受新的指令,例如当合约主动对另外一个合约转账,而被转账合约没有接受转账的函数时,转账失败,此时合约可能进入拒绝服务状态。
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.11;
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.11;
contract Refunder {
address[] private refundAddresses;
-mapping (address => uint) public refunds;
- constructor() {
- refundAddresses.push(0x79B483371E87d664cd39491b5F06250165e4b184);
- refundAddresses.push(0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB);
+mapping (address => uint) public refunds;
+ constructor() {
+ refundAddresses.push(0x79B483371E87d664cd39491b5F06250165e4b184);
+ refundAddresses.push(0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB);
}
- // bad
- function refundAll() public {
- // arbitrary length iteration based on how many addresses participated
- for(uint x; x < refundAddresses.length; x++) {
- // doubly bad, now a single failure on send will hold up all funds
- require(payable(refundAddresses[x]).send(refunds[refundAddresses[x]]));
+ // bad
+ function refundAll() public {
+ // arbitrary length iteration based on how many addresses participated
+ for(uint x; x < refundAddresses.length; x++) {
+ // doubly bad, now a single failure on send will hold up all funds
+ require(payable(refundAddresses[x]).send(refunds[refundAddresses[x]]));
}
}
}
@@ -3070,7 +3066,7 @@ 第22节:安全事故12-DOS攻击
@@ -3156,14 +3152,6 @@ 第22节:安全事故12-DOS攻击
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/23_\345\256\211\345\205\250\344\272\213\346\225\20513-encodePacked\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/23_\345\256\211\345\205\250\344\272\213\346\225\20513-encodePacked\346\224\273\345\207\273.html"
index d532bfd6..021d5b29 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/23_\345\256\211\345\205\250\344\272\213\346\225\20513-encodePacked\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/23_\345\256\211\345\205\250\344\272\213\346\225\20513-encodePacked\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3020,35 +3016,35 @@
第23节:安全事故13-encodePacked攻击
abi.encodePacked() 采用非填充序列化,当序列化参数包含多个变长数组时,攻击者可以在保持所有元素顺序不变的前提下,改变两个变长数组的元素,如此序列化的结果相同。
在下面的代码中:通过构造 addUser 的输入,攻击者可以将 regularUsers 的成员加入 admins 成员,但是构造的输入和原输入的签名相同。
-// SPDX-License-Identifier: MIT
-pragma solidity 0.8.11;
-import "./ECDSA.sol";
+// SPDX-License-Identifier: MIT
+pragma solidity 0.8.11;
+import "./ECDSA.sol";
contract AccessControl {
- using ECDSA for bytes32;
- mapping(address => bool) isAdmin;
- mapping(address => bool) isRegularUser;
+ using ECDSA for bytes32;
+ mapping(address => bool) isAdmin;
+ mapping(address => bool) isRegularUser;
- // Add admins and regular users.
- function addUsers(
+ // Add admins and regular users.
+ function addUsers(
address[] calldata admins,
address[] calldata regularUsers,
bytes calldata signature
- )
- external
- {
- if (!isAdmin[msg.sender]) {
- // Allow calls to be relayed with an admin's signature.
+ )
+ external
+ {
+ if (!isAdmin[msg.sender]) {
+ // Allow calls to be relayed with an admin's signature.
bytes32 hash = keccak256(abi.encodePacked(admins, regularUsers));
address signer = hash.toEthSignedMessageHash().recover(signature);
- require(isAdmin[signer], "Only admins can add users.");
+ require(isAdmin[signer], "Only admins can add users.");
}
- for (uint256 i = 0; i < admins.length; i++) {
- isAdmin[admins[i]] = true;
+ for (uint256 i = 0; i < admins.length; i++) {
+ isAdmin[admins[i]] = true;
}
- for (uint256 i = 0; i < regularUsers.length; i++) {
- isRegularUser[regularUsers[i]] = true;
+ for (uint256 i = 0; i < regularUsers.length; i++) {
+ isRegularUser[regularUsers[i]] = true;
}
}
}
@@ -3081,7 +3077,7 @@ 第23节:安全事故13-
@@ -3167,14 +3163,6 @@ 第23节:安全事故13-
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/24_\345\256\211\345\205\250\344\272\213\346\225\20514-\347\237\255\345\234\260\345\235\200\346\224\273\345\207\273.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/24_\345\256\211\345\205\250\344\272\213\346\225\20514-\347\237\255\345\234\260\345\235\200\346\224\273\345\207\273.html"
index 4a068fb9..dbfc2a79 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/24_\345\256\211\345\205\250\344\272\213\346\225\20514-\347\237\255\345\234\260\345\235\200\346\224\273\345\207\273.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/24_\345\256\211\345\205\250\344\272\213\346\225\20514-\347\237\255\345\234\260\345\235\200\346\224\273\345\207\273.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3019,64 +3015,64 @@
第24节:安全事故14-短地址攻击
如果传入的 to 是末端缺省的短地址,EVM 会将后面字节补足地址,而最后的 value 值不足则用 0 填充,导致实际转出的代币数值倍增,在执行transfer函数时,原型如下:
-function transfer(address _to, uint256 _value) returns (bool success)`
+function transfer(address _to, uint256 _value) returns (bool success)`
在正常情况下,会给EVM传入136位的字节码(不包含0x),示例如下:
a9059cbb000000000000000000000000490e588126207a8132c8e002c8554e19cb380b7a000000000000000000000000000000000000000000000000000000003430bca1
但是,如果我们选择的_to地址后面正好有几个0(假设:3个0),而我们刻意将这3个0去掉,那么EVM会自动使用后面的 _value
值的高位0对这缺失的3个0进行补位,同时EVM也会在_value
后面进行再次3个补零,从而保证136位字节码完整,而这种机制导致的结果就是:原本value是1的时候,从后面补三个零,将value变成了1000,这相当于转账的值放大了1000倍(10^3)。
代码验证
-import { ethers } from "hardhat";
-
-async function main() {
- // 0xEAd54777D54C2C1bE1458E749fDfB5d654458309
- const senderPrivateKey = "0xa7b0f6c4cddc5c818655f5c0eb9e42f803dcc400ec284521e9092e8b243473c3"
- const provider_default = new ethers.providers.JsonRpcProvider("https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161");
-
- let sender = new ethers.Wallet(senderPrivateKey, provider_default)
- // 获取地址,与hardhat原生的不一样
- let senderAddr = await sender.getAddress()
-
- // 提前转入足量的eth和token
- let balance = await sender.getBalance()
- console.log('balance Of Ether: ' + balance, ', sender:', senderAddr)
-
- const contractAddr = "0x11Fe617f76aC652F995E7BA0fE4001F741e6d003"
-
- // 这里要加上signer
- let contractInstance = await ethers.getContractAt("Test", contractAddr, sender)
- console.log('contractInstance:', contractInstance.address);
- console.log("balance of sender:", await contractInstance.BalanceOf(senderAddr))
-
- //1. 构造攻击bytecode
- let transferToCode = contractInstance.interface.encodeFunctionData("transferTo", ["0xdfca6234eb09125632f8f3c71bf8733073b7cd00", 123])
- // 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b
- // 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b, 剪掉两个0
- console.log('transferToCode:', transferToCode);
-
- // 2. 发起攻击
- const tx = {
- // data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b', //invalid data
- data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b', // valid data
- nonce: 1, //你发送交易的帐号的最后一笔交易的 nonce + 1
- gasLimit: '0x2dc6c0',
- gasPrice: '0x2540be400',
- value: '0x0',
- to: contractAddr
+import { ethers } from "hardhat";
+
+async function main() {
+ // 0xEAd54777D54C2C1bE1458E749fDfB5d654458309
+ const senderPrivateKey = "0xa7b0f6c4cddc5c818655f5c0eb9e42f803dcc400ec284521e9092e8b243473c3"
+ const provider_default = new ethers.providers.JsonRpcProvider("https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161");
+
+ let sender = new ethers.Wallet(senderPrivateKey, provider_default)
+ // 获取地址,与hardhat原生的不一样
+ let senderAddr = await sender.getAddress()
+
+ // 提前转入足量的eth和token
+ let balance = await sender.getBalance()
+ console.log('balance Of Ether: ' + balance, ', sender:', senderAddr)
+
+ const contractAddr = "0x11Fe617f76aC652F995E7BA0fE4001F741e6d003"
+
+ // 这里要加上signer
+ let contractInstance = await ethers.getContractAt("Test", contractAddr, sender)
+ console.log('contractInstance:', contractInstance.address);
+ console.log("balance of sender:", await contractInstance.BalanceOf(senderAddr))
+
+ //1. 构造攻击bytecode
+ let transferToCode = contractInstance.interface.encodeFunctionData("transferTo", ["0xdfca6234eb09125632f8f3c71bf8733073b7cd00", 123])
+ // 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b
+ // 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b, 剪掉两个0
+ console.log('transferToCode:', transferToCode);
+
+ // 2. 发起攻击
+ const tx = {
+ // data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b', //invalid data
+ data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b', // valid data
+ nonce: 1, //你发送交易的帐号的最后一笔交易的 nonce + 1
+ gasLimit: '0x2dc6c0',
+ gasPrice: '0x2540be400',
+ value: '0x0',
+ to: contractAddr
}
- let signedTx = await sender.signTransaction(tx)
- console.log('signedTx:', signedTx);
+ let signedTx = await sender.signTransaction(tx)
+ console.log('signedTx:', signedTx);
- let res = await sender.sendTransaction(tx)
- console.log("tx hash:", res.hash);
+ let res = await sender.sendTransaction(tx)
+ console.log("tx hash:", res.hash);
}
-// We recommend this pattern to be able to use async/await everywhere
-// and properly handle errors.
-main().catch((error) => {
- console.error(error);
- process.exitCode = 1;
+// We recommend this pattern to be able to use async/await everywhere
+// and properly handle errors.
+main().catch((error) => {
+ console.error(error);
+ process.exitCode = 1;
});
总结:EVM已经修复了
@@ -3116,7 +3112,7 @@ 参考链接
@@ -3202,14 +3198,6 @@ 参考链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/30_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\344\270\200\350\210\254\345\216\237\345\210\231.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/30_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\344\270\200\350\210\254\345\216\237\345\210\231.html"
index 94abcc8f..d08d95eb 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/30_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\344\270\200\350\210\254\345\216\237\345\210\231.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/30_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\344\270\200\350\210\254\345\216\237\345\210\231.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3092,7 +3088,7 @@ 代码Code
C19
- 不要改变函数参数。
C20
- 即时计算价值是否比存储价值更便宜?
C21
- 是否所有状态变量都从正确的合约中读取(master还是clone)?
-C22
- 是否正确使用了比较运算符(>
、<
、>=
、
),尤其是为了防止差一(多一次或 者少一次)错误?
+C22
- 是否正确使用了比较运算符(>
、<
、>=
、<=
),尤其是为了防止差一(多一次或 者少一次)错误?
C23
- 是否正确使用了逻辑运算符(==
、!=
、&&
、||
、!
),尤其是为了防止差一错误?
C24
- 在除法之前总是乘法,除非乘法可能溢出。
C25
- Magic numbers(魔数)是否被具有直观名称的常数替换?
@@ -3223,7 +3219,7 @@ 小结
@@ -3309,14 +3305,6 @@ 小结
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/31_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\347\263\273\347\273\237\350\247\204\345\210\231.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/31_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\347\263\273\347\273\237\350\247\204\345\210\231.html"
index 15a5d164..ef79f548 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/31_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\347\263\273\347\273\237\350\247\204\345\210\231.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/31_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\347\263\273\347\273\237\350\247\204\345\210\231.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3047,7 +3043,7 @@ 31_智能合约编写系统规则
@@ -3133,14 +3129,6 @@ 31_智能合约编写系统规则
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/32_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\346\234\200\344\275\263\345\256\236\350\267\265.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/32_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\346\234\200\344\275\263\345\256\236\350\267\265.html"
index 630cdf17..11754aaa 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/32_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\346\234\200\344\275\263\345\256\236\350\267\265.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/32_\346\231\272\350\203\275\345\220\210\347\272\246\347\274\226\345\206\231\346\234\200\344\275\263\345\256\236\350\267\265.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3059,7 +3055,7 @@ 32_智能合约编写最佳实践
@@ -3145,14 +3141,6 @@ 32_智能合约编写最佳实践
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/33_Solgraph.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/33_Solgraph.html"
index 8b1518dc..4f3e88db 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/33_Solgraph.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/33_Solgraph.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3036,20 +3032,20 @@ 合约
contract MyContract {
uint balance;
- function MyContract() {
- Mint(1000000);
+ function MyContract() {
+ Mint(1000000);
}
- function Mint(uint amount) internal {
+ function Mint(uint amount) internal {
balance = amount;
}
- function Withdraw() {
+ function Withdraw() {
msg.sender.send(balance);
}
- function GetBalance() constant returns(uint) {
- return balance;
+ function GetBalance() constant returns(uint) {
+ return balance;
}
}
@@ -3071,13 +3067,13 @@ 执行
}
渲染图片
-# 安装渲染工具
+# 安装渲染工具
brew install graphviz
-# 执行渲染
+# 执行渲染
dot -Tpng MyContract.dot -o MyContract.png
-# Mac用户可以使用内置工具Preview.app查看方式
+# Mac用户可以使用内置工具Preview.app查看方式
pbpaste | solgraph | dot -Tpng | open -f -a /Applications/Preview.app
执行效果
@@ -3114,7 +3110,7 @@ 链接
@@ -3200,14 +3196,6 @@ 链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/34_Mythril.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/34_Mythril.html"
index dfb3b417..230438a5 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/34_Mythril.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/34_Mythril.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,100 +3019,100 @@ 概述
Install(local)
pip3 install mythril
-# 如果报错,请依次执行
+# 如果报错,请依次执行
python3.10 -m pip install --upgrade pip
-# 大概率是这个包缺失
+# 大概率是这个包缺失
pip install maturin
Install(docker)
docker pull mythril/myth
help
-docker run mythril/myth --help
+docker run mythril/myth --help
-# 以下是输出内容
+# 以下是输出内容
positional arguments:
- {safe-functions,analyze,a,disassemble,d,list-detectors,read-storage,function-to-hash,hash-to-address,version,help}
+ {safe-functions,analyze,a,disassemble,d,list-detectors,read-storage,function-to-hash,hash-to-address,version,help}
Commands
- safe-functions Check functions which are completely safe using
+ safe-functions Check functions which are completely safe using
symbolic execution
analyze (a) Triggers the analysis of the smart contract
disassemble (d) Disassembles the smart contract
list-detectors Lists available detection modules
read-storage Retrieves storage slots from a given address through
rpc
- function-to-hash Returns the hash signature of the function
- hash-to-address converts the hashes in the blockchain to ethereum
+ function-to-hash Returns the hash signature of the function
+ hash-to-address converts the hashes in the blockchain to ethereum
address
version Outputs the version
optional arguments:
- -h, --help show this help message and exit
- -v LOG_LEVEL log level (0-5)
+ -h, --help show this help message and exit
+ -v LOG_LEVEL log level (0-5)
disassemble
-docker run mythril/myth disassemble -c "0x6060"
+docker run mythril/myth disassemble -c "0x6060"
analyze
-# 指定文件
-myth analyze
-docker run -v $(pwd):/tmp mythril/myth analyze /tmp/
-
-# 指定地址
-myth analyze -a
-docker run mythril/myth analyze -a
-
+# 指定文件
+myth analyze <solidity-file>
+docker run -v $(pwd):/tmp mythril/myth analyze /tmp/<solidity-file>
+
+# 指定地址
+myth analyze -a <contract-address>
+docker run mythril/myth analyze -a <contract-address>
+
演示案例
创建文件test.sol,内容如下:
contract Exceptions {
- uint256[8] myarray;
- uint counter = 0;
- function assert1() public pure {
- uint256 i = 1;
+ uint256[8] myarray;
+ uint counter = 0;
+ function assert1() public pure {
+ uint256 i = 1;
- // 会报错,因为assert一般不用来校验参数,而是校验状态变量。
- assert(i == 0);
+ // 会报错,因为assert一般不用来校验参数,而是校验状态变量。
+ assert(i == 0);
}
- function counter_increase() public {
- counter+=1;
+ function counter_increase() public {
+ counter+=1;
}
- function assert5(uint input_x) public view{
- require(counter>2);
+ function assert5(uint input_x) public view{
+ require(counter>2);
- // 会报错,因为assert一般不用来校验参数,而是校验状态变量。
- assert(input_x > 10);
+ // 会报错,因为assert一般不用来校验参数,而是校验状态变量。
+ assert(input_x > 10);
}
- function assert2() public pure {
- uint256 i = 1;
- assert(i > 0);
+ function assert2() public pure {
+ uint256 i = 1;
+ assert(i > 0);
}
- function assert3(uint256 input) public pure {
- assert(input != 23);
+ function assert3(uint256 input) public pure {
+ assert(input != 23);
}
- function require_is_fine(uint256 input) public pure {
- require(input != 23);
+ function require_is_fine(uint256 input) public pure {
+ require(input != 23);
}
- function this_is_fine(uint256 input) public pure {
- if (input > 0) {
- uint256 i = 1/input;
+ function this_is_fine(uint256 input) public pure {
+ if (input > 0) {
+ uint256 i = 1/input;
}
}
- function this_is_find_2(uint256 index) public view {
- if (index < 8) {
+ function this_is_find_2(uint256 index) public view {
+ if (index < 8) {
uint256 i = myarray[index];
}
}
}
执行命令:
-docker run -v $(pwd):/tmp mythril/myth analyze /tmp/test.sol
+docker run -v $(pwd):/tmp mythril/myth analyze /tmp/test.sol
Output如下,点击查看完整输出
@@ -3159,7 +3155,7 @@ 链接
@@ -3245,14 +3241,6 @@ 链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/35_Slither.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/35_Slither.html"
index f1deaf62..9f7473ae 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/35_Slither.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/35_Slither.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3021,14 +3017,14 @@ 第34节:slither教程
概述
合约静态扫描工具🔧,remix有集成,误报率比较高
安装
-# pip
+# pip
pip3 install slither-analyzer
-# git 安装
-git clone https://github.com/crytic/slither.git && cd slither
+# git 安装
+git clone https://github.com/crytic/slither.git && cd slither
python3 setup.py install
-# docker 安装
+# docker 安装
docker pull trailofbits/eth-security-toolbox
docker run -it -v /home/share:/share trailofbits/eth-security-toolbox
@@ -3061,7 +3057,7 @@ 链接
@@ -3147,14 +3143,6 @@ 链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/36_REMIX\351\235\231\346\200\201\346\211\253\346\217\217\345\267\245\345\205\267.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/36_REMIX\351\235\231\346\200\201\346\211\253\346\217\217\345\267\245\345\205\267.html"
index aaa7d196..ceee2e2b 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/36_REMIX\351\235\231\346\200\201\346\211\253\346\217\217\345\267\245\345\205\267.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/36_REMIX\351\235\231\346\200\201\346\211\253\346\217\217\345\267\245\345\205\267.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3054,7 +3050,7 @@ 链接
@@ -3140,14 +3136,6 @@ 链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/37_SECURIF.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/37_SECURIF.html"
index 0459a27b..25bbbeb2 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/37_SECURIF.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/37_SECURIF.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3021,7 +3017,7 @@ 第37节:SECURIF
概述
Securify 2.0 is a security scanner for Ethereum smart contracts supported by the Ethereum Foundation and ChainSecurity. The core research behind Securify was conducted at the Secure, Reliable, and Intelligent Systems Lab at ETH Zurich.
安装
-git clone git@github.com:eth-sri/securify2.git
+git clone git@github.com:eth-sri/securify2.git
sudo docker build -t securify .
@@ -3056,7 +3052,7 @@ 链接
@@ -3142,14 +3138,6 @@ 链接
-
-
-
-
-
-
-
-
diff --git "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/index.html" "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/index.html"
index 09267403..899828db 100644
--- "a/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/index.html"
+++ "b/cn/04_\345\220\210\347\272\246\346\224\273\345\207\273/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3098,7 +3094,7 @@ 审计范畴
@@ -3184,14 +3180,6 @@ 审计范畴
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/01_QuickStart.html" "b/cn/05_hardhat\346\241\206\346\236\266/01_QuickStart.html"
index 7c29bdfc..3b04d609 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/01_QuickStart.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/01_QuickStart.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3023,39 +3019,39 @@ 第一节:快速体验QuickStart
职场进阶: https://dukeweb3.com
实用命令
-## hh
+## hh
npm install --global hardhat-shorthand
-# 查看支持的网络
+# 查看支持的网络
npx hardhat verify --list-networks
环境构造
-#创建npm空项目
+#创建npm空项目
npm init
-#安装命令,对照两个版本的差异性
-npm install --save-dev hardhat@2.11.1 # 新案例,新工具包
+#安装命令,对照两个版本的差异性
+npm install --save-dev hardhat@2.11.1 # 新案例,新工具包
npm install --save-dev hardhat@2.9.7
-#创建工程
+#创建工程
npx hardhat-》选择高级ts项目
-#运行测试(默认已经不支持)
+#运行测试(默认已经不支持)
npx hardhat accounts
-#编译合约
+#编译合约
npx hardhat compile
-#单元测试
-npx hardhat test
+#单元测试
+npx hardhat test
-#运行脚本,部署合约
+#运行脚本,部署合约
npx hardhat run scripts/deploy.ts
-#启动节点node
+#启动节点node
npx hardhat node
-#部署合约到本地node节点
+#部署合约到本地node节点
npx hardhat run scripts/deploy.ts --network localhost
目录结构
@@ -3068,14 +3064,14 @@ 目录结构
hardhat-toolbox
当前版本:hardhat:^2.11.1,在配置文件中,引用了hardhat-toolbox包,这是一个集合,安装了常用的npm包。
-import { HardhatUserConfig } from "hardhat/config";
-import "@nomicfoundation/hardhat-toolbox";
+import { HardhatUserConfig } from "hardhat/config";
+import "@nomicfoundation/hardhat-toolbox";
-const config: HardhatUserConfig = {
- solidity: "0.8.9",
+const config: HardhatUserConfig = {
+ solidity: "0.8.9",
};
-export default config;
+export default config;
修改配置文件
@@ -3095,64 +3091,64 @@ 修改配置文件
安装dotenv
npm install dotenv
-#在配置文件中引用
-require('dotenv').config()
+#在配置文件中引用
+require('dotenv').config()
配置文件完整内容:
-import { HardhatUserConfig } from "hardhat/config";
-import "@nomicfoundation/hardhat-toolbox";
-// import * as dotenv from "dotenv";
-require('dotenv').config()
-
-const PRIVATE_KEY = process.env.PRIVATE_KEY || ''
-const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''
-const INFURA_KEY = process.env.INFURA_KEY || ''
-const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || ''
-
-console.log("PRIVATE_KEY: ", PRIVATE_KEY);
-console.log("ALCHEMY_KEY: ", ALCHEMY_KEY);
-
-const config: HardhatUserConfig = {
- networks: {
- hardhat: {
+import { HardhatUserConfig } from "hardhat/config";
+import "@nomicfoundation/hardhat-toolbox";
+// import * as dotenv from "dotenv";
+require('dotenv').config()
+
+const PRIVATE_KEY = process.env.PRIVATE_KEY || ''
+const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''
+const INFURA_KEY = process.env.INFURA_KEY || ''
+const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || ''
+
+console.log("PRIVATE_KEY: ", PRIVATE_KEY);
+console.log("ALCHEMY_KEY: ", ALCHEMY_KEY);
+
+const config: HardhatUserConfig = {
+ networks: {
+ hardhat: {
},
- goerli: {
- url: `https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_KEY}`,
- accounts: [PRIVATE_KEY]
+ goerli: {
+ url: `https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_KEY}`,
+ accounts: [PRIVATE_KEY]
},
- mainnet: {
- url: `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`,
- accounts: [PRIVATE_KEY]
+ mainnet: {
+ url: `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`,
+ accounts: [PRIVATE_KEY]
},
- kovan: {
- // url: `https://eth-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`,
- url: `https://kovan.infura.io/v3/${INFURA_KEY}`,
- accounts: [PRIVATE_KEY]
+ kovan: {
+ // url: `https://eth-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`,
+ url: `https://kovan.infura.io/v3/${INFURA_KEY}`,
+ accounts: [PRIVATE_KEY]
},
- ropsten: {
- // url: `https://eth-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`,
- url: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
- accounts: [PRIVATE_KEY]
+ ropsten: {
+ // url: `https://eth-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`,
+ url: `https://ropsten.infura.io/v3/${INFURA_KEY}`,
+ accounts: [PRIVATE_KEY]
}
},
- solidity: {
- version: "0.8.9",
- settings: {
- optimizer: {
- enabled: true,
- runs: 200
+ solidity: {
+ version: "0.8.9",
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 200
}
}
},
- etherscan: {
- apiKey: {
- ropsten: ETHERSCAN_API_KEY
+ etherscan: {
+ apiKey: {
+ ropsten: ETHERSCAN_API_KEY
}
}
};
-export default config;
+export default config;
@@ -3162,7 +3158,7 @@ 修改配置文件
Verify
-#https://ropsten.etherscan.io/address/0x61c8E000634154dF38B2ec23483fa2E08984d938#code
+#https://ropsten.etherscan.io/address/0x61c8E000634154dF38B2ec23483fa2E08984d938#code
npx hardhat verify 0x61c8E000634154dF38B2ec23483fa2E08984d938 1694667145 --network ropsten
@@ -3201,7 +3197,7 @@ 参考链接
@@ -3287,14 +3283,6 @@ 参考链接
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/02_\345\215\225\345\205\203\346\265\213\350\257\225.html" "b/cn/05_hardhat\346\241\206\346\236\266/02_\345\215\225\345\205\203\346\265\213\350\257\225.html"
index 9424a732..99e1c13b 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/02_\345\215\225\345\205\203\346\265\213\350\257\225.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/02_\345\215\225\345\205\203\346\265\213\350\257\225.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,26 +3025,26 @@ 第2节:单元测试
- describe('Create Offer', () => {
- it.only('should faild to call createOffer directly', async () => {
- let last = await getOffer(lastOffer)
- await expect(last.createOffer(tokens, info)).to.revertedWith("Bazaar: permission denied")
+ describe('Create Offer', () => {
+ it.only('should faild to call createOffer directly', async () => {
+ let last = await getOffer(lastOffer)
+ await expect(last.createOffer(tokens, info)).to.revertedWith("Bazaar: permission denied")
})
})
- 测试单个文件
-npx hardhat test test/xxx.spec.ts
+npx hardhat test test/xxx.spec.ts
- 如果测试过程中需要改变块高
-async function mineBlocks(blockNumber) {
- while (blockNumber > 0) {
+async function mineBlocks(blockNumber) {
+ while (blockNumber > 0) {
blockNumber--;
await hre.network.provider.request({
- method: "evm_mine",
+ method: "evm_mine",
});
}
}
@@ -3059,22 +3055,24 @@ 第2节:单元测试
Sometimes when we run test
, it takes very long to execute why? (network issue, disconnect the network may work)
Be sure never call any function(make any assignment) out side of beforeEach or a describe or it will fail
-describe("Rivers", async function () {
- let accounts: SignerWithAddress[]
- let compIns: Comp
- let unitrollerIns: Unitroller
- let comptrollerIns: Comptroller
- let sPriceOracleIns: SimplePriceOracle
- let container: Box[] = [];
- // accounts = await hre.ethers.getSigners(); <-- error beforeeach(async ()> {
- console.log("begin beforeEach");
- accounts = await hre.ethers.getSigners();
- console.log("accounts len: ", accounts.length);
-
- await loadFixture(deployOpenEdenFixture);
+describe("Rivers", async function () {
+ let accounts: SignerWithAddress[]
+ let compIns: Comp
+ let unitrollerIns: Unitroller
+ let comptrollerIns: Comptroller
+ let sPriceOracleIns: SimplePriceOracle
+ let container: Box[] = [];
+ // accounts = await hre.ethers.getSigners(); <-- error
+
+ beforeEach(async () => {
+ console.log("begin beforeEach");
+ accounts = await hre.ethers.getSigners();
+ console.log("accounts len: ", accounts.length);
+
+ await loadFixture(deployOpenEdenFixture);
})
})
--->
+
@@ -3104,7 +3102,7 @@ 第2节:单元测试
@@ -3190,14 +3188,6 @@ 第2节:单元测试
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/03_fork\344\270\273\347\275\221.html" "b/cn/05_hardhat\346\241\206\346\236\266/03_fork\344\270\273\347\275\221.html"
index 696e58b2..a52fc71e 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/03_fork\344\270\273\347\275\221.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/03_fork\344\270\273\347\275\221.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3030,83 +3026,83 @@ 第3节:fork主网
impersonate_account
默认情况下,我们的hardaht账户在主网上是没有资产的,因此我们在使用fork功能时,需要impersonate(扮演)成其他地址(这个人在主网上,在我们指定的块高上,是有真实资产的),具体代码如下:
创建test.fork/sendTransactionFork.ts:(单独创建一个fork的文件夹,与原来的test分开)
-/* eslint-disable no-console */
-import { network, ethers } from "hardhat";
-// import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
-import { JsonRpcServer } from "hardhat/types";
-import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
-import { JsonRpcSigner } from "@ethersproject/providers";
-
-describe("sendTransaction", () => {
- let signer: JsonRpcSigner;
- let acc0: SignerWithAddress;
-
- beforeEach(async () => {
- // https://etherscan.io/accounts
- const ETHWHALE = "0xF977814e90dA44bFA03b6295A0616a897441aceC";
- await network.provider.request({
- method: "hardhat_impersonateAccount",
- params: [ETHWHALE],
+/* eslint-disable no-console */
+import { network, ethers } from "hardhat";
+// import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
+import { JsonRpcServer } from "hardhat/types";
+import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
+import { JsonRpcSigner } from "@ethersproject/providers";
+
+describe("sendTransaction", () => {
+ let signer: JsonRpcSigner;
+ let acc0: SignerWithAddress;
+
+ beforeEach(async () => {
+ // https://etherscan.io/accounts
+ const ETHWHALE = "0xF977814e90dA44bFA03b6295A0616a897441aceC";
+ await network.provider.request({
+ method: "hardhat_impersonateAccount",
+ params: [ETHWHALE],
});
- //1. 获取hardaht内置账户
- const accounts = await ethers.getSigners();
- acc0 = accounts[0];
- console.log("acc0:", acc0.address);
+ //1. 获取hardaht内置账户
+ const accounts = await ethers.getSigners();
+ acc0 = accounts[0];
+ console.log("acc0:", acc0.address);
- //2. 查看金额,应该为:10000000000000000000000(这是初始化的值)
- let bal = await ethers.provider.getBalance(acc0.address);
- console.log("acc0 bal:", bal.toString());
+ //2. 查看金额,应该为:10000000000000000000000(这是初始化的值)
+ let bal = await ethers.provider.getBalance(acc0.address);
+ console.log("acc0 bal:", bal.toString());
- //3. 重要‼️ acc0扮演成: 三方地址(有真实资产的地址),后续signer代表的就是这个三方地址了,而不是原来的acc0
+ //3. 重要‼️ acc0扮演成: 三方地址(有真实资产的地址),后续signer代表的就是这个三方地址了,而不是原来的acc0
signer = ethers.provider.getSigner(ETHWHALE);
- bal = await ethers.provider.getBalance(signer.getAddress());
+ bal = await ethers.provider.getBalance(signer.getAddress());
- //4. 查看这个资产
- console.log("ETHWHALE bal:", bal.toString());
+ //4. 查看这个资产
+ console.log("ETHWHALE bal:", bal.toString());
});
- describe("sendTransaction Test", () => {
- it("should send transaction", async () => {
- // 4. 测试一下,用三方地址给我们的acc0转账
- await signer.sendTransaction({
- to: acc0.address,
- value: ethers.utils.parseEther("20"),
+ describe("sendTransaction Test", () => {
+ it("should send transaction", async () => {
+ // 4. 测试一下,用三方地址给我们的acc0转账
+ await signer.sendTransaction({
+ to: acc0.address,
+ value: ethers.utils.parseEther("20"),
});
- const bal = await ethers.provider.getBalance(acc0.address);
- // 5. 查看acc0的金额,应该是:10020000000000000000000
- console.log("new acc0 bal:", bal.toString());
+ const bal = await ethers.provider.getBalance(acc0.address);
+ // 5. 查看acc0的金额,应该是:10020000000000000000000
+ console.log("new acc0 bal:", bal.toString());
});
});
});
修改配置文件
hardhat.config.ts
-const mainnetFork = MAINNET_FORK //我们使用环境变量来控制fork与否
+const mainnetFork = MAINNET_FORK //我们使用环境变量来控制fork与否
? {
- url: 'urlxxxxx'
- blockNumber: 21577481,
+ url: 'urlxxxxx'
+ blockNumber: 21577481,
}
- : undefined;
-
-const config: HardhatUserConfig = {
- networks: {
- bsc_main: 'urlxxxxx'
- hardhat: {
- blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
- gas: DEFAULT_BLOCK_GAS_LIMIT,
- gasPrice: 8000000000,
- chainId: 56,
- throwOnTransactionFailures: true,
- throwOnCallFailures: true,
- accounts:
- forking: mainnetFork // 开关在这里生效
+ : undefined;
+
+const config: HardhatUserConfig = {
+ networks: {
+ bsc_main: 'urlxxxxx'
+ hardhat: {
+ blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT,
+ gas: DEFAULT_BLOCK_GAS_LIMIT,
+ gasPrice: 8000000000,
+ chainId: 56,
+ throwOnTransactionFailures: true,
+ throwOnCallFailures: true,
+ accounts:
+ forking: mainnetFork // 开关在这里生效
},
},
执行代码
- MAINNET_FORK=true npx hardhat test test.fork/sendTransactionFork.ts
+ MAINNET_FORK=true npx hardhat test test.fork/sendTransactionFork.ts
@@ -3136,7 +3132,7 @@ 执行代码
@@ -3222,14 +3218,6 @@ 执行代码
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/04_\345\244\232\347\211\210\346\234\254\347\274\226\350\257\221.html" "b/cn/05_hardhat\346\241\206\346\236\266/04_\345\244\232\347\211\210\346\234\254\347\274\226\350\257\221.html"
index f3288544..2709bdd5 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/04_\345\244\232\347\211\210\346\234\254\347\274\226\350\257\221.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/04_\345\244\232\347\211\210\346\234\254\347\274\226\350\257\221.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3025,26 +3021,26 @@ 第4节:多版本编译
解决多版本编译:https://hardhat.org/hardhat-runner/docs/advanced/multiple-solidity-versions
如果指定多个版本,当合约中未指定明确版本时,hardhat会默认使用最高的版本编译,此时会出现问题;解决办法是对特定的文件明确编译器版本号,即下文中的overrides部分:
solidity: {
- overrides: {
- '@uniswap/v3-core/contracts/libraries/FullMath.sol': {
- version: '0.7.6'
+ overrides: {
+ '@uniswap/v3-core/contracts/libraries/FullMath.sol': {
+ version: '0.7.6'
},
- '@uniswap/v3-core/contracts/libraries/TickMath.sol': {
- version: '0.7.6'
+ '@uniswap/v3-core/contracts/libraries/TickMath.sol': {
+ version: '0.7.6'
}
},
- compilers: [
+ compilers: [
{
- version: '0.8.10',
- settings: {
- optimizer: {
- enabled: true,
- runs: 200
+ version: '0.8.10',
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 200
}
}
},
{
- version: '0.6.12'
+ version: '0.6.12'
}
]
},
@@ -3076,7 +3072,7 @@ 第4节:多版本编译
@@ -3162,14 +3158,6 @@ 第4节:多版本编译
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/05_\345\244\232\347\275\221\347\273\234\351\203\250\347\275\262\347\233\270\345\220\214\345\234\260\345\235\200.html" "b/cn/05_hardhat\346\241\206\346\236\266/05_\345\244\232\347\275\221\347\273\234\351\203\250\347\275\262\347\233\270\345\220\214\345\234\260\345\235\200.html"
index 48f56173..45cc5978 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/05_\345\244\232\347\275\221\347\273\234\351\203\250\347\275\262\347\233\270\345\220\214\345\234\260\345\235\200.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/05_\345\244\232\347\275\221\347\273\234\351\203\250\347\275\262\347\233\270\345\220\214\345\234\260\345\235\200.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3029,47 +3025,47 @@ 第5节:多链部署相同地址
hardhat.config.ts中增加:
-import "xdeployer";
+import "xdeployer";
增加配置
xdeploy: {
- contract: "Mock1Inch",
- //constructorArgsPath: "",
- constructorArgsPath: "./deploy-args.ts",
- salt: "Bydefi",
- signer: process.env.PRIVATE_KEY,
- networks: ["hardhat", "ropsten"],
- rpcUrls: ["hardhat", NETWORKS_RPC_URL["ropsten"]],
- gasLimit: DEFAULT_BLOCK_GAS_LIMIT
+ contract: "Mock1Inch",
+ //constructorArgsPath: "",
+ constructorArgsPath: "./deploy-args.ts",
+ salt: "Bydefi",
+ signer: process.env.PRIVATE_KEY,
+ networks: ["hardhat", "ropsten"],
+ rpcUrls: ["hardhat", NETWORKS_RPC_URL["ropsten"]],
+ gasLimit: DEFAULT_BLOCK_GAS_LIMIT
},
- solidity: {
- version: "0.8.9",
- settings: {
- optimizer: {
- enabled: true,
- runs: 200
+ solidity: {
+ version: "0.8.9",
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 200
}
}
},
上述配置中,需要指定构造函数的文件,我们创建:deploy-args.ts,填写相应内容:
-const data = [
- "arg1",
- "arg2",
+const data = [
+ "arg1",
+ "arg2",
...
];
-export { data };
+export { data };
增加contracts/Create2DeployerLocal.sol(必要的)
-// SPDX-License-Identifier: MIT
+// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.9;
+pragma solidity ^0.8.9;
-import "xdeployer/src/contracts/Create2Deployer.sol";
+import "xdeployer/src/contracts/Create2Deployer.sol";
contract Create2DeployerLocal is Create2Deployer {}
@@ -3120,7 +3116,7 @@ 手动部署
@@ -3206,14 +3202,6 @@ 手动部署
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/06_\345\256\236\347\224\250\346\217\222\344\273\266.html" "b/cn/05_hardhat\346\241\206\346\236\266/06_\345\256\236\347\224\250\346\217\222\344\273\266.html"
index 29e8d0c8..b411719a 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/06_\345\256\236\347\224\250\346\217\222\344\273\266.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/06_\345\256\236\347\224\250\346\217\222\344\273\266.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3024,24 +3020,24 @@ 第6节:实用插件
检查slot布局,优化gas
-# 安装
+# 安装
npm i hardhat-storage-layout
-# hardhat.config.js
-require('hardhat-storage-layout');
+# hardhat.config.js
+require('hardhat-storage-layout');
-# 运行
+# 运行
npx hardhat check
检查合约size
-# 安装
+# 安装
npm install --save-dev hardhat-contract-sizer
-# hardhat.config.js
-require('hardhat-contract-sizer');
+# hardhat.config.js
+require('hardhat-contract-sizer');
-# 运行
+# 运行
npx hardhat size-contracts
@@ -3073,7 +3069,7 @@ 第6节:实用插件
@@ -3159,14 +3155,6 @@ 第6节:实用插件
-
-
-
-
-
-
-
-
diff --git "a/cn/05_hardhat\346\241\206\346\236\266/index.html" "b/cn/05_hardhat\346\241\206\346\236\266/index.html"
index ba3ae9a5..e776fd63 100644
--- "a/cn/05_hardhat\346\241\206\346\236\266/index.html"
+++ "b/cn/05_hardhat\346\241\206\346\236\266/index.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3066,7 +3062,7 @@
@@ -3152,14 +3148,6 @@
-
-
-
-
-
-
-
-
diff --git "a/cn/06_ethers/01_Node.js\344\273\213\347\273\215.html" "b/cn/06_ethers/01_Node.js\344\273\213\347\273\215.html"
index 2ec96408..ee89d20f 100644
--- "a/cn/06_ethers/01_Node.js\344\273\213\347\273\215.html"
+++ "b/cn/06_ethers/01_Node.js\344\273\213\347\273\215.html"
@@ -36,10 +36,6 @@
-
-
-
-
@@ -3071,36 +3067,36 @@ 2. 定义变量
- 使用
let
来定义一个变量,而不要再使用var
了,因为var
有很多坑;可以认为let
就是修复了bug的var
。比如,var允许重复声明变量而且不报错;var的作用域让人感觉疑惑。
- 最佳实践:优先用
const
,如果变量需要被修改才用let
;要理解目前很多早期写的项目中仍然是用var
。
-var i = 10;
-console.log("var :" , i);
+var i = 10;
+console.log("var :" , i);
-var i = 100;
-console.log("var :" , i);
+var i = 100;
+console.log("var :" , i);
-function test () {
- var m = 10;
- console.log("test m :", m);
+function test () {
+ var m = 10;
+ console.log("test m :", m);
}
test();
-//console.log("test outside :", m);
+//console.log("test outside :", m);
-let j = "hello"
-console.log("j :" , j);
+let j = "hello"
+console.log("j :" , j);
-j = "HELLO"
-console.log("j :" , j);
+j = "HELLO"
+console.log("j :" , j);
-const k = [1,2,3,4];
-console.log("k0 :" , k);
+const k = [1,2,3,4];
+console.log("k0 :" , k);
-k[0] = 100;
-console.log("k1 :" , k);
+k[0] = 100;
+console.log("k1 :" , k);
-//k = [4,5,6,7];
-//console.log("k2 :" , k)
+//k = [4,5,6,7];
+//console.log("k2 :" , k)
3. 解构赋值
@@ -3108,24 +3104,24 @@ 3. 解构赋值
数组的解构赋值
-const arr = [1, 2, 3] //我们得到了一个数组
-let [a, b, c] = arr //可以这样同时定义变量和赋值
-console.log(a, b, c); // 1 2 3
+const arr = [1, 2, 3] //我们得到了一个数组
+let [a, b, c] = arr //可以这样同时定义变量和赋值
+console.log(a, b, c); // 1 2 3
对象的解构赋值(常用)
-const obj = { name: '俊哥',address:'深圳', age: '100'} //我们得到了一个对象
-let {name, age} = obj //可以这样定义变量并赋值
-console.log(name, age); //俊哥 100
+const obj = { name: '俊哥',address:'深圳', age: '100'} //我们得到了一个对象
+let {name, age} = obj //可以这样定义变量并赋值
+console.log(name, age); //俊哥 100
函数参数的解构赋值(常用)
-const person = { name: '小明', age: 11}
-function printPerson({name, age}) { // 函数参数可以解构一个对象
- console.log(`姓名:${name} 年龄:${age}`);
- //console.log("姓名:", name, "年龄:", age);
+const person = { name: '小明', age: 11}
+function printPerson({name, age}) { // 函数参数可以解构一个对象
+ console.log(`姓名:${name} 年龄:${age}`);
+ //console.log("姓名:", name, "年龄:", age);
}
-printPerson(person) // 姓名:小明 年龄:11
+printPerson(person) // 姓名:小明 年龄:11
@@ -3135,29 +3131,29 @@ 4. 函数扩展
参数默认值,从ES6开始,我们可以为一个函数的参数设置默认值, go语言有默认值吗?
-function foo(name, address = '深圳') {
- console.log(name, address);
+function foo(name, address = '深圳') {
+ console.log(name, address);
}
-foo("小明") // address将使用默认值
-foo("小王", '上海') // address被赋值为'上海'
+foo("小明") // address将使用默认值
+foo("小王", '上海') // address被赋值为'上海'
箭头函数,将function
换成=>
定义的函数,就是箭头函数
只适合用于普通函数,不要用在构造函数,不要用在成员函数,不要用着原型函数
-function add(x, y) {
- return x + y
+function add(x, y) {
+ return x + y
}
-//演示自执行函数
-//函数也是变量,可以赋值
-// 这个箭头函数等同于上面的add函数
+//演示自执行函数
+//函数也是变量,可以赋值
+// 这个箭头函数等同于上面的add函数
(x, y) => x +y;
-// 如果函数体有多行,则需要用大括号包裹
-(x, y) => {
- if(x >0){
- return x + y
- }else {
- return x - y
+// 如果函数体有多行,则需要用大括号包裹
+(x, y) => {
+ if(x >0){
+ return x + y
+ }else {
+ return x - y
}
}
@@ -3167,30 +3163,30 @@ 5. Class继承
由于==js一开始被设计为函数式语言==,万物皆函数。所有对象都是从函数原型继承而来,通过继承某个函数的原型来实现对象的继承。但是这种写法会让新学者产生疑惑,并且和传统的OOP语言差别很大。ES6 封装了class语法来大大简化了对象的继承。
-class Person {
- constructor(name, age){
- this.name = name
- this.age = age
+class Person {
+ constructor(name, age){
+ this.name = name
+ this.age = age
}
- // 注意:没有function关键字
- sayHello(){
- console.log(`大家好,我叫${this.name}`);
+ // 注意:没有function关键字
+ sayHello(){
+ console.log(`大家好,我叫${this.name}`);
}
}
-class Man extends Person{
- constructor(name, age){
- super(name, age)
+class Man extends Person{
+ constructor(name, age){
+ super(name, age)
}
- //重写父类的方法
- sayHello(){
- console.log('我重写了父类的方法!');
+ //重写父类的方法
+ sayHello(){
+ console.log('我重写了父类的方法!');
}
}
-let p = new Person("小明", 33) //创建对象
-p.sayHello() // 调用对象p的方法,打印 大家好,我叫小明
-let m = new Man("小五", 33)
-m.sayHello() // 我重写了父类的方法!
+let p = new Person("小明", 33) //创建对象
+p.sayHello() // 调用对象p的方法,打印 大家好,我叫小明
+let m = new Man("小五", 33)
+m.sayHello() // 我重写了父类的方法!
6. 总结
ES6 的新语法有很多,有人将它总结为了一本书。当然,ES6提出的只是标准,各大浏览器和node基本实现了90%以上的新特性,极其个别还没有实现。我们目前讲的是最基本的一些语法,由于你们还未了解同步和异步的概念;Promise和async/await的内容将会在后面的课程中讲解。
@@ -3218,58 +3214,58 @@ 2. 回调函数callback
function foo2(value, callback1, callback2) { }
3. 同步调用(阻塞)
请事先在当前目录下准备文件"input.txt",写入任意数据。
-var fs = require("fs");
+var fs = require("fs");
-var data = fs.readFileSync('input.txt');
+var data = fs.readFileSync('input.txt');
-console.log(data.toString());
-console.log("程序执行结束!");
+console.log(data.toString());
+console.log("程序执行结束!");
4. 异步调用(非阻塞)
-var fs = require("fs");
+var fs = require("fs");
-//error first callback
-fs.readFile('input.txt', function (err, data) {
- if (err) return console.error(err);
- console.log(data.toString());
+//error first callback
+fs.readFile('input.txt', function (err, data) {
+ if (err) return console.error(err);
+ console.log(data.toString());
});
-console.log("程序执行结束!");
+console.log("程序执行结束!");
demo
-//node内置的读取文件的模块
-let fs = require('fs')
+//node内置的读取文件的模块
+let fs = require('fs')
-let filename = '1.txt'
+let filename = '1.txt'
-//先测试同步读取文件
-let data = fs.readFileSync(filename, 'utf-8')
-console.log('同步读取文件内容 data :', data)
+//先测试同步读取文件
+let data = fs.readFileSync(filename, 'utf-8')
+console.log('同步读取文件内容 data :', data)
-//同步执行:
-//1. 同步读取文件时,无需回调函数,返回值就是读取的数据内容
-//2. 主线程阻塞在读取函数这里,一直到读取完成后,才继续向下执行
-//3. 慢,效率低
+//同步执行:
+//1. 同步读取文件时,无需回调函数,返回值就是读取的数据内容
+//2. 主线程阻塞在读取函数这里,一直到读取完成后,才继续向下执行
+//3. 慢,效率低
-//异步执行:
-//1. 需要注册一个回调函数
-//2. 主线发现是异步调用时,直接把任务丢给nodejs的后台线程执行, 主线程继续向下执行其他代码
-//3. 当后台执行完成时,会通知通过回调函数通知主线程,主线程执行。
+//异步执行:
+//1. 需要注册一个回调函数
+//2. 主线发现是异步调用时,直接把任务丢给nodejs的后台线程执行, 主线程继续向下执行其他代码
+//3. 当后台执行完成时,会通知通过回调函数通知主线程,主线程执行。
-//测试异步读取文件
-fs.readFile(filename, 'utf-8', /*回调函数*/ function (err, data) {
- if (err) {
- console.log('读取文件出错:', err)
- return
+//测试异步读取文件
+fs.readFile(filename, 'utf-8', /*回调函数*/ function (err, data) {
+ if (err) {
+ console.log('读取文件出错:', err)
+ return
}
- console.log('异步读取文件数据:', data)
+ console.log('异步读取文件数据:', data)
})
-console.log('异步读取数据2222!')
+console.log('异步读取数据2222!')
5. 异步调用原理(了解即可)
- 事件处理机制
@@ -3301,70 +3297,70 @@ 6. NodeJs能做什么?
四、常用数据类型
1. Buffer
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型,因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
-console.log("=============== buffer 与 编码");
-const buf = Buffer.from('runoob', 'ascii');
-console.log(buf);
-console.log(buf.toString());
-console.log(buf.toString('utf8'));
-console.log(buf.toString('hex'));
+console.log("=============== buffer 与 编码");
+const buf = Buffer.from('runoob', 'ascii');
+console.log(buf);
+console.log(buf.toString());
+console.log(buf.toString('utf8'));
+console.log(buf.toString('hex'));
-console.log("=============== 创建buffer")
-//指定长度,默认值为buffer,string,number
-const buf1 = Buffer.alloc(10, 'test')
-console.log("buf1 " + buf1)
+console.log("=============== 创建buffer")
+//指定长度,默认值为buffer,string,number
+const buf1 = Buffer.alloc(10, 'test')
+console.log("buf1 " + buf1)
-//数组,buffer,string
-let buffer = Buffer.from([1, 2,3,4])
+//数组,buffer,string
+let buffer = Buffer.from([1, 2,3,4])
-console.log(buffer)
-console.log(buffer.toString())
-console.log(buffer.toJSON())
+console.log(buffer)
+console.log(buffer.toString())
+console.log(buffer.toJSON())
-console.log("=============写入buffer")
+console.log("=============写入buffer")
-let buf2 = Buffer.alloc(152);
-let len = buf2.write('hello world!')
-console.log("len : " + len);
+let buf2 = Buffer.alloc(152);
+let len = buf2.write('hello world!')
+console.log("len : " + len);
2. 事件
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件。
-let event = require('events');
+