{"content":{"title":"Solidity如何优化Gas费用","body":"  在区块链中，每一笔智能合约的运行，都要根据复杂度消耗一笔GAS费；如果你要将值写入存储，则需要花费很多。如果你只是使用堆栈，它的成本会低一些；智能合约solidity语言的编写，不仅要考虑安全，也要考虑语言的优化，在以太坊上的交易gas是有上限的，特别是针对一些复杂业务的处理，有可能会导致gas超高，严重者导致此笔交易失败。\r\n\r\n## **0、Struct结构**\r\n\r\n      如果一个struct中有多个uint，则尽可能使用较小的uint，solidity会将这些uint打包在一起，从而占用较小的存储空间\r\n\r\n## **1、选择变量数据类型**\r\n\r\n       不同数据类型的存储GAS消耗不同，我们应该选择 gas 消耗更小的数据类型。\r\n\r\n       尽量使用256位的变量，如果你只存储一个uint8，则EVM将用零填充所有缺少的数字，这会耗费gas、此外，EVM执行计算也会转化为 uint256 ，因此除uint256之外的任何其他类型也必须进行转换。\r\n\r\n       通常情况下我们不会考虑使用 uint 变种，因为无论如何定义 uint的大小，Solidity 为它保留256位的存储空间。例如，使用 uint8 而不是uint（uint256）不会节省任何 gas。\r\n\r\n        有一种情况例外：把 uint 绑定到 struct 里面。如果一个 struct 中有多个 uint，则尽可能使用较小的 uint, Solidity 会将这些 uint 打包在一起，从而占用较少的存储空间。\r\n\r\n        当 uint 定义在一个 struct 中的时候，尽量使用最小的整数子类型以节约空间。 并且把同样类型的变量放一起（即在 struct 中将把变量按照类型依次放置），这样 Solidity 可以将存储空间最小化。\r\n\r\n        为了避免存储操作，可以在数组后面加上 memory关键字， 表明这个数组是仅仅在内存中创建，不需要写入外部存储，并且在函数调用结束时它就解散了。与在程序结束时把数据保存进 storage 的做法相比，内存运算可以大大节省gas开销 – 把这数组放在view里用，完全不用花钱。\r\n\r\n## **2、布尔类型**\r\n         布尔类型 bool 实际为 uint8，即使用 8 位的存储空间，每个存储插槽能装入 32 个布尔类型值。而布尔值只能有两个值：True 或 False，其实只需要在单个存储位中就可以保存布尔值。\r\n\r\n## **3、使用短路规则**\r\n\r\n操作符 || 和 && 适用常见的短路规则。\r\n\r\n这意味着，假设f(x) 和 g(y) 返回 true 的概率一样，那么：\r\n\r\n在表达式 f(x) || g(y) 中，如果 f(x) 的计算结果为真，则不会执行 g(y)。因此应该将贵的方法放在后面。\r\n\r\n在表达式 f(x) && g(y) 中，如果 f(x) 的计算结果为假，则不会执行 g(y)。因此应该将贵的方法放在后面\r\n\r\n## **4、通过汇编将变量打包到单个插槽中**\r\n\r\n将多个变量堆叠在一起\r\n\r\n## **5、使用简短的原因字符串**\r\n\r\n       将错误原因字符串与require语句一起附加，以便更容易理解contract调用还原的原因。但是，这些字符串在部署的字节码中占用空间。每个原因字符串至少需要32个字节，因此请确保您的字符串符合32个字节，否则会变得更加昂贵。\r\n\r\n## **6、调用内部函数更便宜**\r\n\r\n        从智能合约内部调用其内部函数比调用其公共函数便宜，因为当您调用公共函数时，所有参数将再次复制到内存中并传递给该函数。相反，当您调用内部函数时，将传递这些参数的引用，并且它们不会再次复制到内存中。这节省了一点gas。\r\n\r\n## **7、使用代理模式进行大规模部署**\r\n\r\n        如果您希望部署同一contract的多个副本，则考虑仅部署一个实现contract和多个代理contract，将其逻辑委派给实现contract。这将允许这些contract共享相同的逻辑但不同的数据。\r\n\r\n## **8、尽量使用批量操作**\r\n\r\n        因为一笔交易的基础 gas 消耗是 21000，批量操作相比多次操作，能减少多次的交易带来的基础 gas 消耗。\r\n\r\n## **9、事件**\r\n\r\n没有参数的事件是750 GAS。理论上每个附加参数将增加256个GAS，但事实上，它会更多。\r\n\r\n## **10、哈希**\r\n\r\n       你可以使用智能合约中的几个内置哈希函数：keccak256，sha256和ripemd160。参数越多，消耗的气体越多。耗气量：ripemd160> sha256> keccak256。因此，如果没有其他目的，建议使用keccak256函数。\r\n\r\n## **11、删减不必要的Solidity库**\r\n\r\n        我们引入的库通常只需要用到其中的部分功能，这意味着其中可能会包含大量对于你的智能合约而言其实是冗余的solidity代码。如果可以在你自己的合约里安全有效地实现所依赖的库功能，那么就能够达到优化solidity合约的gas利用的目的。\r\n\r\n## **12、显式声明Solidity合约函数的可见性**\r\n\r\n\r\n\r\n        显式声明函数的可见性不仅可以提高智能合约的安全性，同时也有利于优化合约执行的gas成本。例如，通过显式地标记函数为外部函数（External），可以强制将函数参数的存储位置设置为calldata，这会节约每次函数执行时所需的以太坊gas成本。\r\n\r\n## **13、编译合约时使用优化器**\r\n\r\n\r\n\r\n       使用编译器 solc 编译合约时启动优化器 optimizer，它将简化复杂的表达方式，能减小编译后的合约字节码大小，从而减少部署时的 gas 消耗，同时也能减少合约调用时的消耗。\r\n\r\n## **14、常量**\r\n\r\n      在 solidity 中，声明为 constant 或者 immutable 的状态变量即常量。constant 修饰的常量的值在编译时确定，而 immutable 修饰的常量的值在部署时确定。尽量使用常量，常量是合约字节码的一部分，不占用存储插槽，使用常量比变量更省 gas。在部署时，常量消耗的 gas 更少。\r\n\r\n## **15、默克尔树**\r\n\r\n        使用默克尔树。在合约中保存一组数据的 merkleRoot，提供 verify 方法验证某条数据在这组数据中。相比使用一个 mapping 或数组来保存全部数据，减少了 gas 消耗。\r\n        \r\n        \r\n*多链钱包充提系统*\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/12/qhjRDP2y63a2e791f0047.png)\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2022/12/yUA4VSLY63a2e79d351e4.png)\r\n\r\n学如逆水行舟，不进则退。心似平原跑马，易放难收。【区块链】【系统/网络/运维】【云计算/  大数据】【数据库】【移动开发】【后端开发】【游戏开发】【UI设计】【微服务】【爬虫】【Java】【Go】【C++】【PHP】【Python】【Android/IOS】【HTML/CSS】【JavaScript】【Node】【VUE】【ReactNaive】。。。\r\n\r\n欢迎各位大神萌新一起专研分享各行各业技术!\r\n\r\nChain区块链开发社区：593674370"},"author":{"user":"https://learnblockchain.cn/people/12241","address":null},"history":"QmYPXwSbgYwy9aNjUy2MrqfVAMgLAiETbWu66UzhuZRwrf","timestamp":1671672893,"version":1}