以太坊开发基础
以太坊是最大的智能合约平台,为去中心化应用提供了强大的基础设施。本章将介绍以太坊的核心概念和开发基础。
以太坊架构
以太坊是一个去中心化的计算平台,其核心组件包括:
1. 账户系统
以太坊有两种类型的账户:
- 外部账户(EOA)
- 由私钥控制
- 可以发起交易
-
没有相关代码
-
合约账户
- 由代码控制
- 只能响应交易
- 包含智能合约代码
| // 创建钱包
const wallet = ethers.Wallet.createRandom();
console.log('地址:', wallet.address);
console.log('私钥:', wallet.privateKey);
console.log('助记词:', wallet.mnemonic.phrase);
// 导入私钥
const privateKey = '0x...';
const importedWallet = new ethers.Wallet(privateKey);
|
2. 交易模型
以太坊交易包含以下字段:
| const transaction = {
nonce: 0, // 交易序号
gasPrice: 20000000000, // Gas价格(wei)
gasLimit: 21000, // Gas限制
to: '0x...', // 接收地址
value: 1000000000, // 转账金额(wei)
data: '0x...', // 调用数据
chainId: 1 // 网络ID
};
|
3. 状态转换
| +----------------+ +----------------+ +----------------+
| 状态 S(n) | -> | 交易Tx | -> | 状态 S(n+1) |
+----------------+ +----------------+ +----------------+
|
Gas机制
Gas是以太坊的计算资源度量单位:
1. Gas计算
1
2
3
4
5
6
7
8
9
10
11
12 | // 估算Gas费用
async function estimateGas(transaction) {
const provider = new ethers.providers.JsonRpcProvider();
const gasEstimate = await provider.estimateGas(transaction);
// 获取当前Gas价格
const gasPrice = await provider.getGasPrice();
// 计算总费用
const totalCost = gasEstimate.mul(gasPrice);
return ethers.utils.formatEther(totalCost);
}
|
2. Gas优化技巧
- 存储优化
| // 不好的做法
uint256[] public numbers;
// 好的做法
uint256[10] public numbers;
|
- 循环优化
| // 不好的做法
for(uint i = 0; i < array.length; i++) {
// 操作
}
// 好的做法
uint length = array.length;
for(uint i = 0; i < length; i++) {
// 操作
}
|
以太坊虚拟机(EVM)
EVM是以太坊的核心计算引擎:
1. 指令集
EVM操作码示例:
| PUSH1 0x60 // 压入值0x60
PUSH1 0x40 // 压入值0x40
MSTORE // 存储操作
PUSH1 0x20 // 压入值0x20
PUSH1 0x00 // 压入值0x00
RETURN // 返回操作
|
2. 内存模型
EVM的内存分为三个区域:
- Stack
- 最大1024个元素
- 每个元素256位
-
PUSH/POP操作
-
Memory
- 线性寻址
- 按字节访问
-
临时存储
-
Storage
- 持久存储
- 键值对结构
- 高Gas成本
网络类型
以太坊有多个网络环境:
1. 主网(Mainnet)
| // 连接主网
const mainnet = new ethers.providers.JsonRpcProvider(
"https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
);
|
2. 测试网
| // 连接Goerli测试网
const goerli = new ethers.providers.JsonRpcProvider(
"https://goerli.infura.io/v3/YOUR-PROJECT-ID"
);
// 连接Sepolia测试网
const sepolia = new ethers.providers.JsonRpcProvider(
"https://sepolia.infura.io/v3/YOUR-PROJECT-ID"
);
|
3. 本地网络
| // 连接本地网络
const localhost = new ethers.providers.JsonRpcProvider(
"http://localhost:8545"
);
|
交易处理
1. 发送交易
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | async function sendTransaction(to, value) {
const wallet = new ethers.Wallet(privateKey, provider);
const tx = await wallet.sendTransaction({
to: to,
value: ethers.utils.parseEther(value)
});
console.log('交易哈希:', tx.hash);
// 等待交易确认
const receipt = await tx.wait();
console.log('交易确认:', receipt);
}
|
2. 监听交易
| // 监听新区块
provider.on('block', (blockNumber) => {
console.log('新区块:', blockNumber);
});
// 监听特定地址的交易
const address = '0x...';
provider.on(address, (tx) => {
console.log('新交易:', tx);
});
|
数据类型和单位
1. 常用数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13 | // 基本类型
uint256 number = 42;
address wallet = 0x123...;
bool flag = true;
string text = "Hello";
bytes32 hash = 0x456...;
// 数组
uint[] numbers = [1, 2, 3];
string[] texts = ["a", "b", "c"];
// 映射
mapping(address => uint) balances;
|
2. 单位转换
| // Wei转换
const oneEther = ethers.utils.parseEther("1.0");
console.log('1 ETH in Wei:', oneEther.toString());
// 格式化Wei为ETH
const wei = "1000000000000000000";
console.log('Wei to ETH:', ethers.utils.formatEther(wei));
// Gas单位
const gwei = ethers.utils.parseUnits("1.0", "gwei");
console.log('1 Gwei in Wei:', gwei.toString());
|
实用工具
1. 地址验证
1
2
3
4
5
6
7
8
9
10
11
12 | // 检查地址格式
function isValidAddress(address) {
return ethers.utils.isAddress(address);
}
// 计算合约地址
function getContractAddress(from, nonce) {
return ethers.utils.getContractAddress({
from: from,
nonce: nonce
});
}
|
2. 数据编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | // ABI编码
const abiCoder = new ethers.utils.AbiCoder();
// 编码函数调用
const encoded = abiCoder.encode(
['string', 'uint256'],
['Hello', 123]
);
// 解码数据
const decoded = abiCoder.decode(
['string', 'uint256'],
encoded
);
|
开发工具
1. 开发环境
- Hardhat
- Truffle
- Remix
- Ganache
2. 测试框架
- Mocha
- Chai
- Waffle
- Ethers.js
3. 开发库
- Web3.js
- Ethers.js
- OpenZeppelin
练习题
-
解释以太坊账户系统的两种类型及其区别。
-
实现一个函数来检查交易状态:
| // 练习:实现交易状态检查
async function checkTransaction(txHash) {
// 你的代码
}
|
- 编写一个Gas估算函数:
| // 练习:实现Gas估算
async function calculateGas(tx) {
// 你的代码
}
|
-
描述EVM的三种内存区域及其特点。
-
实现一个简单的代币转账函数:
| // 练习:实现代币转账
async function transferToken(tokenAddress, to, amount) {
// 你的代码
}
|
参考资源
- 以太坊黄皮书
- EVM深入解析
- Gas和费用
- 以太坊开发工具
- Ethers.js文档