
web3.js 1.x 到 4.x 合约迁移指南web3.eth.Contract 全面升级解析【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js本文基于 web3.js 仓库中的 contracts_migration_guide.md 编写系统梳理从 web3.js 1.x 升级到 4.x 时web3.eth.Contract模块的全部破坏性变更Breaking Changes并结合web3-eth-contract包的源码实现解释每个变更背后的原理与应对策略。读完本文你将能够快速定位升级后合约代码中的报错点理解BigInt、receipt、confirmations等返回值的新形态并写出完全兼容 web3.js 4.x 的合约交互代码。web3.eth.Contract是 web3.js 中与智能合约交互的核心 API覆盖合约部署deploy、方法调用call/send、事件订阅events与历史事件查询getPastEvents等全部场景。在 web3.js 4.x 中该模块发生了大量破坏性变更本文逐一展开讲解并在每个变更点给出 1.x 与 4.x 的代码对照。升级前的完整背景可参考升级指南总览其余模块的迁移说明见同目录下的 web3.eth 迁移指南、web3.utils 迁移指南 与 web3.eth.subscribe 迁移指南。1. 总览web3.eth.Contract 的破坏性变更清单从 1.x 升级到 4.x合约模块的破坏性变更集中在以下 8 个方面#变更点1.x 行为4.x 行为1receipt.statusbooleantrue/false无符号整数默认BigInt(0)/BigInt(1)2deploy().send()的sending/sent事件回调完整的 JSON-RPC Payload只回调待发送的交易对象3confirmations处理器多个独立参数单个对象参数属性不变4encodeABIABI 类型校验宽松对 ABI 类型进行严格校验5无new关键字实例化提示使用new关键字抛出原生Class constructor ... cannot be invoked without new6事件订阅传入toBlock输出警告消息无警告但toBlock仍然无效7.send()的 Promise 结果解析为transactionHash解析为receipt对象8函数与事件参数解码string类型BigInt类型其中第 1、7、8 条对业务代码的影响最大会在下文重点展开。2. Receipt 状态从布尔值变为无符号整数2.1 变更内容在 1.x 中交易收据的receipt.status是boolean类型而在 4.x 中为了遵循 JSON-RPC 规范status被改为无符号整数。默认返回类型下它是一个BigInt// 1.x myContract.methods .MyMethod() .send() .on(receipt, receipt { console.log(receipt.status); // true | false }); // 4.x myContract.methods .MyMethod() .send() .on(receipt, receipt { console.log(receipt.status); // BigInt(0) | BigInt(1) });:::notestatus的具体返回类型取决于你在Web3Config中配置的defaultReturnFormat数据格式。默认格式下为BigInt如果你把默认返回格式配置为number或string那么status也会相应地以数字或字符串形式返回。相关配置方式见 web3 配置指南。 :::2.2 源码印证在仓库的部署实现 contract-deployer-method-class.ts 中部署交易的transactionResolver会直接以BigInt(0)判断交易是否失败transactionResolver: (receipt: TransactionReceipt) { if (receipt.status BigInt(0)) { throw new Web3ContractError(code couldnt be stored, receipt); } // ... }从源码结构看4.x 内部已经完全以BigInt(0)/BigInt(1)作为交易成功与否的判定依据因此在你的业务代码中凡是依赖receipt.status的地方都需要同步升级// 1.x 写法迁移后失效 if (receipt.status) { /* 成功 */ } // 4.x 推荐写法 if (receipt.status BigInt(1)) { /* 成功 */ } // 或结合默认返回格式使用 Number(receipt.status) 1 进行兼容判断3.deploy().send()的sending/sent事件只传递交易参数3.1 变更内容在 1.x 中执行deploy().send().on(sending, payload {})时事件处理器拿到的是完整的 JSON-RPC Payload真正的交易对象藏在payload.params[0]里。而在 4.x 中事件处理器直接拿到即将发送的交易对象无需再层层剥壳// 1.x myContract .deploy() .send() .on(send, payload { console.log(payload); // {id: 1, jsonrpc: 2.0, method: eth_sendTransaction, params: [txObject] } }); // 4.x myContract .deploy() .send() .on(send, txObject { console.log(txObject); // {id: , gas: ,...} });3.2 迁移建议如果你在 1.x 中通过payload.params[0]读取交易字段如from、gas、data升级后直接访问事件参数本身即可// 迁移后的等价代码 myContract.deploy({ data, arguments }).send({ from, gas: 1500000 }) .on(sending, tx { console.log(tx.from, tx.gas, tx.data); });注意这一变更对普通method().send()的sending/sent事件同样成立属于 4.x 统一的事件回调形态调整。除事件监听外4.x 已不支持向函数传递回调详见升级指南总览。4.confirmations处理器改为接收单个对象在 1.x 中confirmations事件处理器以多个独立参数的形式被调用// 1.x myContract.send().on(confirmation, (confirmations: number, receipt: object, latestBlockHash: string) {});在 4.x 中三个值被合并进一个对象传入属性名保持一致// 4.x myContract.send().on(confirmation, ({ confirmations: bigint, receipt: object, latestBlockHash: string }) {});迁移时只需把「位置参数」改为「解构对象」即可字段语义不变myContract.methods.MyMethod().send() .on(confirmation, ({ confirmations, receipt, latestBlockHash }) { console.log(confirmations, receipt.transactionHash, latestBlockHash); });需要注意confirmations字段在 4.x 中同样是bigint类型如果需要与数字比较建议显式转换为Number(confirmations)。5.encodeABI启用严格 ABI 类型校验5.1 变更内容encodeABI用于生成方法调用的 ABI 编码数据函数签名哈希 紧打包参数常被用于多签钱包、离线钱包 / 冷存储以及复杂代理合约的 payload 构造。在 4.x 中该方法的 ABI 类型校验变得严格以下是两个典型场景字节数不足也会报错1.x 中bytes32类型即使输入的字节数不足也能成功编码4.x 会直接抛出错误。空字节也会报错1.x 中bytes32类型传入空字节仍能编码成功4.x 会抛出错误。5.2 源码印证从源码结构看encodeMethodABI见 encoding.ts在编码前会先校验参数个数const inputLength Array.isArray(abi.inputs) ? abi.inputs.length : 0; if (abi.inputs inputLength ! args.length) { throw new Web3ContractError( The number of arguments is not matching the methods required number. You need to pass ${inputLength} arguments., ); }随后调用web3-eth-abi包的encodeParameters/inferTypesAndEncodeParameters完成参数编码再由encodeFunctionSignature拼接函数签名最终拼出完整的调用数据return ${encodeFunctionSignature(abi)}${params};也就是说严格校验贯穿「参数个数 → 参数类型 → 参数值」全链路。函数签名与参数编码的实现位于 packages/web3-eth-abiencodeABI的公开入口见 contract.ts 的文档注释。5.3 迁移建议升级后请确保传给encodeABI()的参数在个数、类型、长度上都与 ABI 声明完全一致。例如声明为bytes32的参数必须传入完整的 32 字节数据// 错误4.x 下抛出异常 myContract.methods.setHash(0x1234).encodeABI(); // bytes32 却只给了 2 字节 // 正确 const fullHash 0x 00.repeat(32); // 32 字节 myContract.methods.setHash(fullHash).encodeABI();同样的严格校验也作用于deploy().encodeABI()构造参数必须与constructor的 ABI 声明完全匹配。6. 未使用new关键字实例化时的错误信息变化1.x 中如果忘记new直接调用Contract(...)会得到 web3.js 自定义的友好提示// 1.x const contract Contract(jsonInterface, address); // Please use the new keyword to instantiate a web3.eth.Contract() object!4.x 中内部实现改用了原生class语法因此错误信息变为 JavaScript 引擎原生的报错// 4.x const contract Contract(jsonInterface, address); // Class constructor ContractBuilder cannot be invoked without new从源码结构看contract.ts4.x 的合约类封装为ContractBuilder并以原生 class 的形式对外暴露因此直接调用会触发引擎级错误。迁移时无需特殊处理只要保证实例化始终使用new关键字即可const contract new Contract(jsonInterface, address); // 推荐7. 事件订阅传入toBlock不再有警告1.x 中如果在事件订阅选项里传入toBlock会收到一条警告Invalid option: toBlock. Use getPastEvents for specific range.这条警告的本意是提醒你toBlock只对历史区块查询有意义订阅是持续性的不应该限定结束区块。4.x 中该警告被移除但行为不变——toBlock依然没有任何效果。如果你确实需要查询某个区块区间内的事件请使用getPastEvents而不是事件订阅// 查询历史事件支持 fromBlock / toBlock const events await myContract.getPastEvents(Transfer, { filter: { from: 0x... }, fromBlock: 0, toBlock: latest, }); // 持续监听不要传 toBlock myContract.events.Transfer().on(data, event { console.log(event); });8..send()现在解析为receipt对象8.1 变更内容这是合约升级中最影响代码结构的一条。1.x 中send()返回的 Promise 解析为transactionHash字符串想拿收据信息还得再发一次web3.eth.getTransactionReceipt(transactionHash)4.x 中send()直接解析为完整的receipt对象// 1.x const transactionHash await myContract.methods.MyMethod().send(); // 需要额外调用 web3.eth.getTransactionReceipt(transactionHash) 才能拿到 receipt // 4.x const receipt await myContract.methods.MyMethod().send(); const transactionHash receipt.transactionHash;8.2 源码印证该行为与第 2 节提到的transactionResolver设计一脉相承在 contract-deployer-method-class.ts 中transactionResolver接收的参数类型就是TransactionReceipt部署成功后会基于receipt.contractAddress克隆出一个绑定新地址的合约实例返回transactionResolver: (receipt: TransactionReceipt) { if (receipt.status BigInt(0)) { throw new Web3ContractError(code couldnt be stored, receipt); } const newContract this.parent.clone(); newContract.options.address receipt.contractAddress; return newContract; }同理普通合约方法send()的 Promise 解析结果也是receipt其中包含transactionHash、blockNumber、gasUsed、logs、contractAddress部署场景等字段。8.3 迁移建议升级后所有依赖send()返回值的代码都要从「哈希」改为「收据」// 1.x 写法 const txHash await myContract.methods.setValue(1).send({ from }); await waitForConfirmation(txHash); // 需要自行轮询 // 4.x 写法receipt 自带全部信息 const receipt await myContract.methods.setValue(1).send({ from }); console.log(receipt.transactionHash, receipt.blockNumber, receipt.gasUsed);这一变更同时减少了网络往返是 4.x 在合约交互体验上的重要改进。9. 函数与事件的参数解码全面转向BigInt9.1 变更内容1.x 中解码后的函数返回值和事件参数均为string类型4.x 中数值型参数统一改为BigInt。先看事件侧// 1.x instance.events.BasicEvent().on(data, function (event) { console.log(event); }); await instance.methods.firesEvent(acc, 1).send(); /** { address: 0x607A075cB7710AA8544c4E0F929e344Bf91AB631, blockNumber: 9, logIndex: 0, removed: false, returnValues: {0: 0xd0731FAE14781104c42B8914b4cc6634b6038daC, 1: 1, addr: 0xd0731FAE14781104c42B8914b4cc6634b6038daC, val: 1} } */ // 4.x instance.events .MultiValueIndexedEvent({ filter: { val: 100 } }) .on(data, console.log); await instance.methods.firesMultiValueIndexedEvent(value, 100, true).send(sendOptions); /** { address: 0x0c1b54fb6fdf63dee15e65cadba8f2e028e26bd0, topics: [...], data: 0x0000..., blockNumber: 23n, transactionIndex: 0n, blockHash: 0x15a7..., logIndex: 0n, removed: false, returnValues: { 0: value, 1: 100n, // BigInt 2: true, __length__: 3, str: value, val: 100n, // 注意这里是 BigInt flag: true }, event: MultiValueIndexedEvent, signature: 0x553c..., raw: ... } */再看函数侧// 1.x await instance.methods.setValue(1).send(); var value await instance.methods.getValue().call(); console.log(value); // 1 // 4.x await instance.methods.setValue(10).send(); var value await instance.methods.getValue().call(); console.log(value); // 10n // 注意返回 BigInt注意 4.x 的示例中还出现了blockNumber: 23n、transactionIndex: 0n、logIndex: 0n这类区块元数据字段也变为 BigInt的现象——这与升级总览中的全局警告一致4.x 中所有数字都以 BigInt 返回不仅是函数与事件的解码参数还包括web3.eth.getBalance等 RPC 方法的返回值见升级指南总览中的相关警告。9.2 迁移建议涉及数值比较如金额、区块号时把BigInt与BigInt比较避免与number/string混用if (event.returnValues.val BigInt(100)) { /* 命中过滤条件 */ }需要输出或传给不支持 BigInt 的库时显式转换const valueStr value.toString(); const valueNum Number(value); // 注意精度风险仅用于展示或小数值场景若希望整体维持 1.x 的number/string返回习惯可在Web3Config中设置defaultReturnFormat参考 web3 配置指南 的defaultReturnFormat章节但这属于全局配置需评估对全项目的影响。解码相关的底层实现位于 web3-eth-abi 包decodeFunctionCall、decodeFunctionReturn等并在 encoding.ts 中被web3-eth-contract复用是理解返回值形态的关键代码。10. 升级自检清单完成本文的阅读后建议对存量合约代码逐项自查检索receipt.status所有布尔判断改为BigInt(0)/BigInt(1)或与defaultReturnFormat对应的类型检索.send()的返回值transactionHash改为从receipt.transactionHash获取检索confirmations事件位置参数改为解构对象并注意confirmations为bigint检索returnValues与call()返回值确认数值型字段的BigInt转换逻辑检查encodeABI调用确保参数个数、类型、字节长度与 ABI 声明完全一致检查部署代码确认new Contract(...)实例化写法并利用deploy().send()解析出的receipt.contractAddress拿到新合约地址检查事件订阅选项移除无意义的toBlock区间查询改用getPastEvents。如需对照其他模块的迁移细节可继续阅读同目录下的 web3.eth 迁移指南、accounts 迁移指南 与 providers 迁移指南合约模块的源码与测试用例可在 packages/web3-eth-contract 中进一步探索。【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考