以太坊与EVM:从底层理解Web3的引擎
聊Web3钱包集成,绕不开以太坊。说实话,很多开发者一开始只把以太坊当成一个「发币平台」,这其实是个误解。以太坊最核心的价值,是它提供了一个去中心化的计算环境——EVM。今天我就带你把这套东西拆开看看。
以太坊网络:不只是主网
以太坊网络其实分好几层。我个人习惯把它分成三类:
- 主网(Mainnet):真正的生产环境,ETH有真实价值。你部署合约、用户转账,都在这里发生。
- 测试网(Testnet):比如Sepolia、Goerli。我刚开始做开发时,经常在测试网上跑合约,因为ETH是免费的,随便领。
- 本地网络(Localnet):像Hardhat Network、Ganache。我本地调试时最喜欢用这个,秒级出块,想怎么玩都行。
每个网络都有一个唯一的Chain ID。主网是1,Sepolia是11155111,Goerli是5。钱包集成时,Chain ID是必须校验的参数,否则容易出大问题。
EVM工作原理:一台全球共享的虚拟机
EVM,全称Ethereum Virtual Machine。说白了,它就是一个运行在全球数千个节点上的「超级计算机」。你写的Solidity代码,最终会被编译成EVM字节码,然后由每个节点执行。
EVM是图灵完备的,这意味着理论上它可以计算任何问题。但有个限制——Gas机制。没有Gas,EVM就会陷入无限循环,整个网络就瘫痪了。你想想看,如果某个恶意合约写了个死循环,所有节点都得陪着它跑,那还得了?
EVM执行交易时,大致流程是这样的:
- 节点收到交易,验证签名和nonce
- 扣除发送方的Gas上限费用
- 加载合约代码到EVM
- 逐条执行字节码指令
- 每执行一条指令,消耗对应的Gas
- 执行完毕,退还剩余Gas
Gas机制:为什么每笔交易都要花钱?
Gas是以太坊的「燃料」。每一条EVM指令,都有对应的Gas消耗。比如:
| 操作 | Gas消耗 | 说明 |
|---|---|---|
| ADD(加法) | 3 | 最便宜的算术运算 |
| SLOAD(读存储) | 2100 | 读取合约状态变量 |
| SSTORE(写存储) | 20000 / 5000 | 写入新值 vs 修改旧值 |
| CALL(调用合约) | 700 | 调用其他合约的基础费用 |
| CREATE(创建合约) | 32000 | 部署新合约 |
Gas价格(Gas Price)由市场供需决定。网络拥堵时,你出价高,矿工优先打包你的交易。我遇到过最夸张的一次,NFT mint高峰期,一笔简单的转账花了0.05 ETH的Gas费。嗯,那感觉就像在高速上堵车,眼睁睁看着计价器跳。
Gas的计算公式很简单:
交易手续费 = Gas Used × Gas Price
其中Gas Used是实际消耗的Gas量,Gas Price是你愿意支付的每单位Gas价格(单位是Gwei,1 ETH = 10^9 Gwei)。
账户模型:EOA vs 合约账户
以太坊有两种账户类型,这个区分非常重要。很多新手搞混,导致钱包集成时出bug。
EOA(外部拥有账户)
EOA就是普通用户账户。由私钥控制,可以发起交易。特点:
- 有私钥,有地址
- 余额是ETH
- 可以发起交易(转账、调用合约)
- 没有关联的代码
你用的MetaMask、WalletConnect,管理的都是EOA。私钥丢了,账户就没了。我见过有人把私钥截图发到群里问「这个怎么用」——嗯,那笔钱再也没回来过。
合约账户
合约账户就是智能合约本身。由代码控制,不能主动发起交易。特点:
- 没有私钥
- 有地址(由创建者地址和nonce计算得出)
- 有余额
- 关联了EVM字节码
- 只能被EOA或其他合约调用
合约账户的地址是确定的。比如你用同一个EOA部署两个合约,第一个合约地址是0x...A,第二个就是0x...B。这个计算规则是固定的,所以有些项目会提前计算合约地址来做一些骚操作。
实际开发中的注意事项
做钱包集成时,有几个点我反复踩坑,分享给你:
- Chain ID校验:用户连接钱包后,一定要检查当前网络是不是你预期的网络。我见过用户连到Polygon,结果你的合约部署在以太坊上,交易直接失败。
- Gas预估:永远不要写死Gas Limit。用ethers.js的
estimateGas方法动态计算。 - Nonce管理:每个EOA的nonce是递增的。如果你同时发多笔交易,nonce必须连续。我曾经因为nonce跳号,导致一笔交易卡了整整两天。
- 签名验证:EIP-191和EIP-712是两种常见的签名标准。我个人更推荐EIP-712,因为签名数据是结构化的,用户能看到自己签的是什么,体验更好。
好了,以太坊和EVM的基础就这些。下一章我们聊聊具体的钱包集成实现,包括如何连接MetaMask、如何签名、如何验证签名。到时候我会带上完整的代码示例,咱们手把手过一遍。