Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: chapter 4.1 #37

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/en/summary/contract-writing-principles.md
Original file line number Diff line number Diff line change
@@ -1 +1,96 @@
# Contract Writing Principles

## About This Section

Following a clear set of principles when writing Ethereum smart contracts helps build safer, more scalable, and efficient contracts.

In this section, we will explore key principles in contract design. Whether constructing a simple [TODO](../../../example/solidity_todo_example/contracts/Todo.sol) contract or complex financial contracts, adhering to these principles ensures more robust code.

## Principles

In the process of developing smart contracts, prioritize the **security** of the contract first, followed by **performance**, and then others.

**Security > Performance > Others**: In the world of smart contracts, security is the absolute priority. Performance can be optimized on the premise of security, and other features can be added as needed, but never at the expense of security.

### Security

Security is the core of smart contract development. Our goal is to ensure that contracts are not vulnerable to attacks and that funds are secure.

1. **Test Your Contracts:** Always write test code for each function point when developing smart contracts. Testing helps uncover hidden vulnerabilities and ensures high-quality code, especially since contracts cannot be modified once deployed.

2. **Avoid Using `tx.origin` for Authorization Control:** `tx.origin` is susceptible to phishing attacks and is not suitable for authorization verification. Attackers can exploit malicious contracts to deceive users, leading to the misuse of `tx.origin`. Use `msg.sender` for verification to avoid such attacks.

```solidity
// Unsafe example
function onlyOwner() public view {
require(tx.origin == owner, "Not authorized"); // Not recommended to use tx.origin
// Business logic
}

// Safe example
function onlyOwner() public view {
require(msg.sender == owner, "Not authorized"); // Use msg.sender
// Business logic
}
```

3. **Prevent Reentrancy Attacks:** When interacting with external contracts or complex logic, use [ReentrancyGuard](https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard) to prevent reentrancy attacks.

### Performance

Code executed on Ethereum consumes `Gas`, and optimizing contract performance can reduce user costs and improve contract response speed.

1. **Reduce Storage Operations:** Each write operation to on-chain storage consumes a large amount of `Gas`. Therefore, minimize the number of storage operations and avoid unnecessary data updates and large data writes.

```solidity
// Not recommended: multiple storage updates
function inefficientUpdate(uint256 newValue) public {
myStoredValue = newValue;
anotherStoredValue = newValue;
}

// Recommended: optimize storage updates
function optimizedUpdate(uint256 newValue) public {
if (myStoredValue != newValue) {
myStoredValue = newValue;
}
if (anotherStoredValue != newValue) {
anotherStoredValue = newValue;
}
}
```

In actual development, **if you plan to deploy an NFT contract, avoid using ERC721Enumerable unless necessary**. `ERC721Enumerable` tracks and enumerates all `Token IDs` through additional storage, significantly increasing each operation's cost. After the first minting, the cost is almost three times higher than `ERC721`!

2. **Move Calculations Off-Chain:** When security can be ensured, move complex calculation logic off-chain to reduce `Gas` costs. For example, you can calculate off-chain and then submit the result on-chain for contract verification. If you're working on a `DeFi` project, the matching logic can be computed offline.

3. **Optimize Loops and Batch Operations:** Avoid using large loops or batch operations in contracts, especially those involving on-chain storage updates. For handling large data, consider executing in batches or using external batch submission tools to reduce the `Gas` consumption per transaction.

```solidity
// Avoid large loop operations in contracts
function processInBatches(address[] memory accounts, uint256[] memory values) public {
uint batchSize = 10; // Execute 10 operations at a time
for (uint i = 0; i < batchSize; i++) {
// Processing logic...
}
}
```

### Other Principles

In contract design, there are also general design principles that do not directly involve security or performance but can improve code clarity and maintainability.

1. **Keep It Modular and Simple:** Split contract code into modular functions, ensuring each function focuses on a single task. Modular code is not only easier to maintain and test but also easier to reuse.

2. **Use Clear Variable and Function Names:** Adopt descriptive variable and function names so that even other developers can understand the contract logic without documentation. This aids code readability and maintainability.

## Summary

Following these principles in future contract writing will make your code more robust and reliable. As blockchain technology evolves, the performance and security standards of smart contracts will also improve, and mastering these fundamental principles will allow you to navigate future contract development with ease.

## Additional Resources

- [Solidity Security Best Practices](https://consensys.github.io/smart-contract-best-practices/)
- [Ethereum Smart Contract Best Practices](https://ethereum.org/en/developers/docs/smart-contracts/security/)
- [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/)
- [The Ultimate Guide to NFT Gas Optimization](https://learnblockchain.cn/article/3920)
95 changes: 95 additions & 0 deletions src/zh/summary/contract-writing-principles.md
Original file line number Diff line number Diff line change
@@ -1 +1,96 @@
# 合约编写原则

## 关于本节

在编写以太坊智能合约时,遵循一套清晰的原则,有助于构建更安全、可扩展和高效的合约。

在本节中,我们将探讨合约设计中的关键原则。无论是构建一个简单的 [TODO](../../../example/solidity_todo_example/contracts/Todo.sol) 合约,还是复杂的金融合约,遵循这些原则能确保代码更具健壮性。

## 原则

智能合约开发的过程中, 优先考虑合约的**安全性**,其次是**性能**,最后是其他。

**安全 > 性能 > 其他**:在智能合约的世界中,安全性是绝对优先的。性能可以在安全性保障的前提下进行优化,而其他特性则可以视实际需求加入,但绝不能以牺牲安全为代价。

### 安全

安全性是智能合约开发的核心,我们的目标是确保合约不会受到攻击并保障资金的安全。

1. **测试你的合约:** 在编写智能合约时,务必为每个功能点编写测试代码。测试能够帮助发现隐藏的漏洞,并提供高质量的代码保障,尤其在合约一旦部署即不可修改的情况下更为重要。

2. **避免使用 `tx.origin` 进行权限控制**:`tx.origin` 很容易被利用进行钓鱼攻击,不适合用于权限验证。攻击者可通过恶意合约诱骗用户,使 `tx.origin` 被错误使用。应使用 `msg.sender` 验证权限以避免此类攻击。

```solidity
// 不安全示例
function onlyOwner() public view {
require(tx.origin == owner, "Not authorized"); // 不建议使用 tx.origin
// 业务逻辑
}

// 安全示例
function onlyOwner() public view {
require(msg.sender == owner, "Not authorized"); // 使用 msg.sender
// 业务逻辑
}
```

3. **防止重入攻击**:在涉及与外部合约交互/复杂逻辑中,使用 [ReentrancyGuard](https://docs.openzeppelin.com/contracts/4.x/api/security#ReentrancyGuard),以防止重入攻击。

### 性能

在以太坊上执行的代码需要消耗 `Gas`,优化合约的性能可以减少用户的使用成本,提高合约的响应速度。

1. **减少存储操作**:每次写入链上存储的操作都消耗大量 `Gas`。因此,尽量减少存储的次数,避免不必要的数据更新和大数据量的写入操作。

```solidity
// 不建议:多次存储更新
function inefficientUpdate(uint256 newValue) public {
myStoredValue = newValue;
anotherStoredValue = newValue;
}

// 建议:优化存储更新
function optimizedUpdate(uint256 newValue) public {
if (myStoredValue != newValue) {
myStoredValue = newValue;
}
if (anotherStoredValue != newValue) {
anotherStoredValue = newValue;
}
}
```

在实际的开发中, **如果你准备部署一个 NFT 合约,避免使用 ERC721Enumerable,除非确有需要**。因为 `ERC721Enumerable` 通过额外存储来跟踪和枚举所有 `Token ID`,导致每次操作的成本大幅增加。`ERC721Enumerable` 在第一次铸造后的成本几乎比 `ERC721` 高出 3 倍!

2. **将计算移出链上**:在能保证安全的前提下,尽量将复杂的计算逻辑移出链上,以减少 `Gas` 费用。比如可以在链下计算完毕后提交计算结果上链,再由链上合约进行结果验证。如果你在做一个 `DeFi`, 其中撮合的逻辑可以在线下计算.

3. **优化循环和批量操作**:尽量避免在合约中使用大型循环或批量操作,尤其是涉及链上存储更新的循环。对于大量数据处理,可以考虑分批执行或借助外部批量提交工具,降低单笔交易的 `Gas` 消耗。

```solidity
// 避免在合约中进行大量循环操作
function processInBatches(address[] memory accounts, uint256[] memory values) public {
uint batchSize = 10; // 每次执行 10 个操作
for (uint i = 0; i < batchSize; i++) {
// 处理逻辑...
}
}
```

### 其他原则

在合约设计中,也有一些不直接涉及安全或性能的通用设计原则,可以提高代码的清晰性和可维护性。

1. **保持模块化和简洁**:将合约代码拆分为模块化的函数,确保每个函数专注于单一功能。模块化代码不仅易于维护和测试,还便于重用。

2. **使用明确的变量和函数命名**:采用具有描述性的变量名和函数名,这样即使是其他开发者在没有文档的情况下,也能理解合约的逻辑。这有助于代码的可读性和可维护性。

## 小结

未来的合约编写中,遵循这些原则将使你的代码更具稳健性和可靠性。随着区块链技术的发展,智能合约的性能和安全标准也会不断提升,而掌握这些基础原则将使你在未来的合约开发中游刃有余。

## 附加资源

- [Solidity Security Best Practices](https://consensys.github.io/smart-contract-best-practices/)
- [Ethereum Smart Contract Best Practices](https://ethereum.org/en/developers/docs/smart-contracts/security/)
- [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts/4.x/)
- [NFT的gas优化终极指南](https://learnblockchain.cn/article/3920)