如何降低Polygon投票智能合约函数的Gas成本?
降低Polygon投票合约Gas成本的优化方案
1. 移除合约内字符串拼接,将格式化逻辑移至前端
Solidity中字符串拼接(尤其是循环内反复调用append)会频繁创建新内存对象,消耗大量Gas。当前合约中拼接artistas仅用于事件输出,完全可以把格式化为[ , 候选人1, 候选人2 ]的逻辑放到前端处理,合约只传递原始数组数据。
修改事件定义和投票函数:
// 原事件定义 // event Vote(string indexed name, string candidates); // 修改后的事件定义,直接传递候选人数组 event Vote(string indexed name, string[] candidates); function voteForCandidate(string[] calldata names, string[][] calldata vote ) public returns (bool){ require(owner == msg.sender, "Only owner can call this function"); uint voteLen = vote.length; // 缓存长度,避免重复读取calldata for(uint i = 0; i < voteLen; i++) { string[] calldata candidates = vote[i]; uint candidateLen = candidates.length; for(uint y = 0; y < candidateLen; y++) { votesReceived[candidates[y]] += 1; } // 直接传递数组,无需拼接字符串 emit Vote(names[i], candidates); } return true; }
2. 合并批量事件触发(可选)
如果业务允许,可将100组投票的事件合并为一次触发,进一步减少事件发射的Gas开销。比如定义一个批量事件:
event BatchVote(string[] names, string[][] allCandidates); // 在函数末尾触发一次批量事件,替代100次单个事件 emit BatchVote(names, vote);
前端监听该事件后,再拆分每组数据进行展示,这样能节省大量事件发射的固定Gas开销。
3. 优化存储操作
votesReceived[vote[i][y]] += 1是SSTORE操作(修改链上存储),Gas成本极高。如果同一候选人在单次交易中被多次投票,可以先在内存中累加票数,再一次性写入存储:
function voteForCandidate(string[] calldata names, string[][] calldata vote ) public returns (bool){ require(owner == msg.sender, "Only owner can call this function"); // 用内存映射临时存储单次交易内的票数累加 mapping(string => uint) memory tempVotes; uint voteLen = vote.length; for(uint i = 0; i < voteLen; i++) { string[] calldata candidates = vote[i]; uint candidateLen = candidates.length; for(uint y = 0; y < candidateLen; y++) { tempVotes[candidates[y]] += 1; } emit Vote(names[i], candidates); } // 批量写入链上存储 // 注:Solidity 0.8.13+支持遍历内存映射,低版本需改用数组存储候选人列表 for(string memory candidate in tempVotes) { votesReceived[candidate] += tempVotes[candidate]; } return true; }
4. 优化权限控制的Gas开销
将owner变量改为immutable类型(部署时赋值,后续不可修改),这样读取owner时直接从合约代码中获取,而非读取链上存储(SLOAD),能减少每次调用的Gas:
address public immutable owner; constructor() { owner = msg.sender; }
5. 动态设置Gas价格,避免固定高价
放弃固定500 GWEI的Gas价格,改用Polygon网络的动态Gas定价策略:
- 使用EIP-1559交易类型,通过工具(如ethers.js)获取当前网络的
maxFeePerGas和maxPriorityFeePerGas,设置略高于当前值的价格即可,无需固定高价。 - 示例代码(ethers.js):
const feeData = await provider.getFeeData(); const tx = await contract.voteForCandidate(names, vote, { maxFeePerGas: feeData.maxFeePerGas?.add(ethers.utils.parseUnits("10", "gwei")), maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.add(ethers.utils.parseUnits("5", "gwei")) });
这样在Gas价格低谷时能大幅降低交易成本,同时避免定价过低导致交易卡住。
内容的提问来源于stack exchange,提问作者Leo Pickler
相关产品推荐
相关产品推荐

