Solidity Gas优化鲜为人知的汇编技巧

  • zealynx
  • 发布于 2023-04-24 19:56
  • 阅读 36

这篇文章详细探讨了如何在以太坊智能合约中,通过使用内联汇编(Assembly)代码来显著优化Gas消耗。文章通过对比Solidity代码与对应汇编实现的Gas成本,展示了在哈希、循环、数学运算、存储操作及地址零地址检查等多个场景下的具体节省效果。

在以太坊区块链上编写高效且经济实惠的智能合约时,哪怕是节省一点 Gas 也很重要。优化 Gas 使用量的一种方法是在合约的某些部分使用汇编代码而不是 Solidity。

在本文中,我们将探讨在各种场景中,从哈希和数学运算到写入存储和检查零地址,使用汇编代码如何帮助你节省 Gas。我们将提供 Solidity 代码示例及其对应的汇编实现,以及 Gas 使用量比较,以便你亲眼看到潜在的 Gas 节省。


1. 使用汇编进行哈希而非 Solidity

1function solidityHash(uint256 a, uint256 b) public view {
2    //unoptimized
3    keccak256(abi.encodePacked(a, b));
4}

Gas: 313

1function assemblyHash(uint256 a, uint256 b) public view {
2    //optimized
3    assembly {
4        mstore(0x00, a)
5        mstore(0x20, b)
6        let hashedVal := keccak256(0x00, 0x40)
7    }
8}

Gas: 231

让我们看看所使用的 Yul 指令及其解释。

Yul hash instructions


2. 在可能的情况下,使用汇编而非 unchecked{++i}

你也可以使用 unchecked{++i;} 来节省更多 Gas,但这不会检查 i 是否溢出。为了获得最佳 Gas 节省,请使用内联汇编,但这会限制你可以实现的功能。

1//loop with unchecked{++i}
2function uncheckedPlusPlusI() public pure {
3    uint256 j = 0;
4    for (uint256 i; i < 10; ) {
5        j++;
6        unchecked {
7            ++i;
8        }
9    }
10}

Gas: 1329

1//loop with inline assembly
2function inlineAssemblyLoop() public pure {
3    assembly {
4        let j := 0
5        for {
            let i := 0
        } lt(i, 10) {
            i := add(i, 0x01)
        } {
            j := add(j, 0x01)
        }
    }
}

Gas: 709

现在,解释一下 lt Yul 指令以及类似的比较操作码。

Yul comparison instructions


3. 使用汇编进行数学运算 (add, sub, mul, div)

使用汇编进行数学运算而不是 Solidity。你可以在汇编中检查溢出/下溢以确保安全。

加法

1//addition in Solidity
2function addTest(uint256 a, uint256 b) public pure {
3    uint256 c = a + b;
4}

Gas: 303

1//addition in assembly
2function addAssemblyTest(uint256 a, uint256 b) public pure {
3    assembly {
4        let c := add(a, b)
5        if lt(c, a) {
6            mstore(0x00, "overflow")
7            revert(0x00, 0x20)
8        }
9    }
10}

Gas: 263

减法

1//subtraction in Solidity
2function subTest(uint256 a, uint256 b) public pure {
3    uint256 c = a - b;
4}

Gas: 300

1//subtraction in assembly
2function subAssemblyTest(uint256 a, uint256 b) public pure {
3    assembly {
4        let c := sub(a, b)
5        if gt(c, a) {
6            mstore(0x00, "underflow")
7            revert(0x00, 0x20)
8        }
9    }
10}

Gas: 263

乘法

1//multiplication in Solidity
2function mulTest(uint256 a, uint256 b) public pure {
3    uint256 c = a * b;
4}

Gas: 325

1//multiplication in assembly
2function mulAssemblyTest(uint256 a, uint256 b) public pure {
3    assembly {
4        let c := mul(a, b)
5        if lt(c, a) {
6            mstore(0x00, "overflow")
7            revert(0x00, 0x20)
8        }
9    }
10}

Gas: 265

除法

1//division in Solidity
2function divTest(uint256 a, uint256 b) public pure {
3    uint256 c = a * b;
4}

Gas: 325

1//division in assembly
2function divAssemblyTest(uint256 a, uint256 b) public pure {
3    assembly {
4        let c := div(a, b)
5        if gt(c, a) {
6            mstore(0x00, "underflow")
7            revert(0x00, 0x20)
8        }
9    }
10}

Gas: 265

所以,现在让我们看看这些 Yul 指令的解释。

Yul storage instructions


4. 使用汇编写入存储值

1address owner = 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84;
2
3function updateOwner(address newOwner) public {
4    owner = newOwner;
5}

Gas: 5302

1address owner = 0xb4c79daB8f259C7Aee6E5b2Aa729821864227e84;
2
3function assemblyUpdateOwner(address newOwner) public {
4    assembly {
5        sstore(owner.slot, newOwner)
6    }
7}

Gas: 5236

我们之前已经看到了一些这些 Yul 指令,但让我们回顾一下它们的解释。

Yul zero check instructions


5. 使用汇编检查 address(0)

1function ownerNotZero(address _addr) public pure {
2    require(_addr != address(0), "zero address)");
3}

Gas: 258

1function assemblyOwnerNotZero(address _addr) public pure {
2    assembly {
3        if iszero(_addr) {
4            mstore(0x00, "zero address")
5            revert(0x00, 0x20)
6        }
7    }
8}

Gas: 252

iszeromstore 我们已经见过,现在让我们检查一下 revert 的解释。

Yul balance instructions


6. 获取合约的 ETH 余额时使用汇编

当获取合约的 ETH 余额时,你可以使用 selfbalance() 而不是 address(this).balance 来节省 Gas。

1function addressInternalBalance() public returns (uint256) {
2    return address(this).balance;
3}

Gas: 148

1function assemblyInternalBalance() public returns (uint256) {
2    assembly {
3        let c := selfbalance()
4        mstore(0x00, c)
5        return(0x00, 0x20)
6    }
7}

Gas: 133

return 我们刚刚见过,现在让我们看看 selfbalance

Smart contract hacking course


联系我们

在 Zealynx,我们专注于智能合约安全审计——包括超越表面检查的 Gas 优化审查。无论你需要全面的协议审计还是有针对性的 Gas 效率审查,我们的团队都随时准备帮助你交付安全、经济高效的代码。联系我们以开始对话。

想要通过更多此类深入分析保持领先吗?订阅我们的通讯,确保你不会错过未来的见解。

  • 原文链接: zealynx.io/blogs/solidit...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zealynx
zealynx
江湖只有他的大名,没有他的介绍。