
原文作者PaperMoon 团队以太坊开发者对预编译合约不陌生——ecrecover 在 0x01sha256 在 0x02用了很多年。Polkadot 也有预编译调用方式完全一样构造一个 call目标地址填预编译地址传入参数拿回结果。但当你把两边的预编译清单摆在一起差异就很明显了。以太坊的预编译像一个密码学工具箱——哈希、签名、椭圆曲线运算全是底层数学原语。Polkadot 的预编译更像一个系统调用接口——跨链消息、原生资产、底层存储、账户转换直接连通链的核心功能。这不是谁多谁少的问题是设计哲学不一样。一、先看清单两边各有什么以太坊预编译14 个截至 Prague 升级地址名称引入时间功能0x01ECRECOVERFrontier2015从 ECDSA 签名恢复签名者地址0x02SHA2-256FrontierSHA-256 哈希0x03RIPEMD-160FrontierRIPEMD-160 哈希0x04IDENTITYFrontier原样返回输入数据内存复制0x05MODEXPByzantium2017大整数模幂运算0x06ECADDByzantiumalt_bn128 椭圆曲线点加0x07ECMULByzantiumalt_bn128 椭圆曲线标量乘0x08ECPAIRINGByzantiumalt_bn128 配对检查0x09BLAKE2FIstanbul2019BLAKE2b 压缩函数0x0AKZG_POINT_EVALCancun2024EIP-4844 blob 承诺验证0x0B-0x0EBLS12-381 系列Prague2025BLS 签名相关的 G1/G2 点运算14 个预编译11 个是密码学运算2 个是椭圆曲线配对/验证1 个是内存复制工具函数。Polkadot 预编译4 大类类型地址核心能力ERC20按 Asset ID 动态生成操作链上原生资产USDT、USDC 等System0x...0900BLAKE2 哈希、SR25519 签名验证、地址转换、合约终止、剩余 Weight 查询Storage0x...0901底层存储的键值读写、部分读取、存储检查XCM0x...0a0000跨链消息发送、本地执行、费用预估数量看起来少但每一类包含多个函数实际覆盖的功能面比以太坊宽得多。二、密码学重叠不多各有侧重这是两边唯一有交集的领域。哈希算法算法以太坊PolkadotSHA-256有0x02没有预编译可用 Solidity 库RIPEMD-160有0x03没有BLAKE2b有0x09压缩函数有BLAKE2-256 BLAKE2-128完整哈希Keccak-256是 EVM 原生操作码不需要预编译同上以太坊的 BLAKE2 预编译0x09只暴露了压缩函数BLAKE2F你想算一个完整的 BLAKE2b 哈希还得自己在合约里拼装调用。Polkadot 的 System 预编译直接给了 hashBlake256 和 hashBlake128 两个完整的哈希函数——传入数据拿回哈希值不需要自己管轮次和状态。BLAKE2 是 Substrate 的默认哈希算法所以 Polkadot 把它做成一等公民是合理的。以太坊生态更多用 Keccak-256已是操作码级别BLAKE2 只在特定场景比如 Zcash 验证才用到。签名验证签名方案以太坊PolkadotECDSA (secp256k1)ECRECOVER0x01ecdsaToEthAddressSystem 预编译SR25519没有sr25519VerifySystem 预编译BLS12-381有0x0B-0x0EPrague 新增没有预编译Ed25519没有预编译没有预编译但 Substrate 原生支持这里的差异很能说明问题。以太坊的 ECRECOVER 是最早的预编译之一因为 secp256k1 签名恢复是以太坊交易验证的基础。没有它合约里就没法验证这条消息真的是某个地址签的。2025 年 Prague 升级又加了 BLS12-381 系列为的是支持信标链验证者签名的链上验证。Polkadot 这边sr25519Verify 是独有的。SR25519 是 Polkadot 原生钱包Polkadot.js的默认签名算法以太坊生态不用这个。有了这个预编译你的 Solidity 合约能直接验证 Polkadot 原生钱包签的消息——这在以太坊合约需要跟 Polkadot 原生账户交互的场景下非常关键。以太坊没有 SR25519Polkadot 没有 BLS12-381。各自补的是自己生态最需要的那块。三、以太坊完全没有的东西资产、存储、跨链从这里开始两边不在一个维度上了。ERC20 预编译合约直接操作链上原生资产以太坊上USDT、USDC 是**部署在链上的 ERC20 合约**。你想在自己的合约里操作 USDT得 import 它的接口然后 call 它的合约地址。USDT 的余额、转账逻辑、授权机制——全在 Tether 部署的那个合约里。Polkadot 上USDT、USDC 是 **Assets pallet 管理的原生资产**——不是一个 ERC20 合约而是写在链的 runtime 里的资产。ERC20 预编译的作用是给这些原生资产套上一层标准 ERC20 接口让 Solidity 合约可以用 transfer()、approve()、balanceOf() 这些熟悉的函数来操作它们。solidity // 在 Polkadot 上用 Solidity 操作原生 USDT // On Polkadot, interact with native USDT using Solidity IERC20 usdt IERC20(0x000007C000000000000000000000000001200000); uint256 balance usdt.balanceOf(msg.sender); usdt.transfer(recipient, amount); 每个原生资产有一个唯一的 Asset ID预编译地址根据 Asset ID 动态计算。不是一个固定地址而是一个地址空间——有多少原生资产就有多少预编译地址。这意味着什么在以太坊上一个 ERC20 代币的转账要经过两层合约调用你的合约 → ERC20 合约。在 Polkadot 上ERC20 预编译直接调用 Assets pallet 的原生逻辑少了一层合约间跳转执行效率更高。但也有代价name()、symbol()、decimals() 这三个 ERC20 元数据函数没有实现。如果你的前端依赖这些函数来显示代币信息需要另想办法。Storage 预编译合约直接读写底层存储以太坊的合约存储模型很简单每个合约有自己的 256 位 key-value 存储空间通过 SSTORE/SLOAD 操作码读写。合约只能访问自己的存储。Polkadot 的 Storage 预编译0x...0901打开了一扇不一样的门solidity // 精确读取只取存储值的某一段不用把整个值加载到内存 // Partial read: fetch only a segment of the stored value function get_range(bytes32 key, uint32 offset, uint32 length) external view returns (bytes memory); // 存储前先检查 key 是否存在避免读不存在的 key 导致 revert // Check key existence before reading to avoid revert function has_key(bytes32 key) external view returns (bool); // 读取并删除一步完成取出来然后清掉 // Read and remove in one step function takeStorage(uint32 flags, bool isFixedKey, bytes memory key) external; get_range 是最实用的如果一个 key 下存了 10KB 的数据你只需要其中 200 字节在以太坊上你得把整个 10KB 加载进 EVM 内存再截取。在 Polkadot 上get_range 直接从底层取那 200 字节省 Gas准确说是省 ref_time 和 proof_size。以太坊没有这种精细度的存储操作预编译。EVM 的存储模型是一个 slot 固定 32 字节虽然简单但不灵活。XCM 预编译合约里直接发跨链消息这是差距最大的地方。以太坊没有跨链预编译。想在以太坊合约里发一条消息给另一条链你得接入第三方桥协议LayerZero、Wormhole、Axelar每个桥有自己的接口、自己的信任假设、自己的手续费结构。没有标准。Polkadot 的 XCM 预编译直接把跨链通信变成了三个函数调用solidity // 在本链执行一条 XCM 消息 // Execute an XCM message locally function execute(bytes calldata message, Weight calldata weight) external; // 发送 XCM 消息到另一条平行链 // Send an XCM message to another parachain function send(bytes calldata destination, bytes calldata message) external; // 预估执行一条 XCM 消息需要多少资源 // Estimate weight required for an XCM message function weighMessage(bytes calldata message) external view returns (Weight memory); 你的 Solidity 合约可以直接往另一条平行链发消息——转资产、触发远程合约调用、查询跨链状态。不需要桥不需要中间人走的是 Polkadot 原生的跨共识消息协议。当然XCM 消息需要用 SCALE 编码Polkadot 的原生序列化格式对以太坊开发者来说是个额外的学习成本。而且当前的 XCM 预编译提供的是裸接口——execute 和 send 需要你自己构造完整的 XCM 指令序列没有封装好的高级函数。实际用起来大概率需要一层 Solidity wrapper 来简化操作。四、设计哲学工具箱 vs 系统调用把两边的差异抽象一层来看以太坊的预编译是补全 EVM 能力短板的工具箱。** EVM 本身是一个通用计算环境密码学运算如果用纯 Solidity 写会极其昂贵椭圆曲线运算可能消耗数百万 Gas所以把最常用的密码学原语做成预编译降低开销。9 年来每次硬分叉加几个新的密码学函数——BN128 配对为 zk-SNARKs 服务、BLAKE2为 Zcash 互操作服务、BLS12-381为信标链服务、KZG为 blob 服务。需求驱动逐个补丁。Polkadot 的预编译是把链的原生能力暴露给合约的系统调用接口。** Polkadot 的智能合约不是链的核心功能而是通过 pallet_revive 后加的一层。链本身的能力——资产管理、存储系统、跨链通信、密码学验证——都已经存在于 runtime 里。预编译做的事情是在 PVM 和 runtime 之间架一座桥让 Solidity 合约能打电话给这些原生功能。这解释了一个有趣的反差以太坊有 14 个预编译但几乎全是密码学Polkadot 只有 4 大类但覆盖了资产、存储、跨链。因为以太坊的预编译在补底层能力Polkadot 的预编译在开上层接口。五、对迁移开发者的实际影响如果你正在把以太坊合约迁移到 Polkadot以下几点跟预编译直接相关你熟悉的以太坊预编译还在。 ECRECOVER、SHA-256 这些以太坊原生预编译在 Polkadot 的 EVM 兼容层中仍然可用。依赖这些预编译的合约不需要修改。ERC20 代币交互逻辑可能要调整。如果你的合约在以太坊上通过合约地址调用 USDT/USDC迁移到 Polkadot 后需要改成调用对应的预编译地址。接口一样还是 transfer、approve但地址不同。**跨链功能是新增项。** 以太坊合约里没有跨链预编译这个概念。如果你想利用 Polkadot 的跨链能力XCM 预编译是全新的东西需要学习 XCM 指令集和 SCALE 编码。**存储优化有新手段。** Storage 预编译的 get_range 让你能做以太坊上做不到的部分存储读取。如果你的合约有大量存储操作这是一个值得探索的优化方向。**weightLeft() 替代了 gasleft()。** 以太坊里用 gasleft() 检查剩余 Gas。Polkadot 的 System 预编译提供 weightLeft()返回的是 refTime 和 proofSize 两个值——对应 Polkadot 的三维资源计量模型。如果你的合约里有基于 gasleft() 的逻辑比如在 Gas 不足时提前退出需要适配成两个维度的判断。小结两套预编译体系的差异本质上反映的是两条链对智能合约该做多少事这个问题的不同回答。以太坊认为合约是链的核心所以给合约补密码学工具让它自己搞定一切。Polkadot 认为合约是链的一部分链本身有很多现成的能力合约只需要有一条通道调过去就行。哪种设计更好没有标准答案但对开发者来说理解这个差异比记住地址表有用得多。