Solidity

這樣的乙太坊字節碼是不是很浪費gas?

  • April 18, 2022

我正在嘗試編寫一些可靠的程式碼。編譯下面的程式碼,我得到了字節碼,但是字節碼中似乎有一些不必要的指令

堅固性:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract Coin {
   // The keyword "public" makes variables
   // accessible from other contracts
   address public minter;

   // Constructor code is only run when the contract
   // is created
   constructor() {
       minter = msg.sender;
   }
}

原始乙太坊字節碼:

608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060f78061005f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80630754617214602d575b600080fd5b60336047565b604051603e91906078565b60405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6072816091565b82525050565b6000602082019050608b6000830184606b565b92915050565b6000609a8260a1565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff8216905091905056fea2646970667358221220bc84b34b2a635493fca57cebf21be27de70a0cc9a1a42fbf4df0c0fc03f8d7c764736f6c63430008070033

反編譯:

contract Contract {
   function main() {
       memory[0x40:0x60] = 0x80;
       var var0 = msg.value;
   
       if (var0) { revert(memory[0x00:0x00]); }
   
       storage[0x00] = msg.sender | (storage[0x00] & ~0xffffffffffffffffffffffffffffffffffffffff);
       memory[0x00:0xf7] = code[0x5f:0x0156];
       return memory[0x00:0xf7];
   }
}

浪費?

我注意到了這行程式碼。

storage[0x00] = msg.sender | (storage[0x00] & ~0xffffffffffffffffffffffffffffffffffffffff);

我了解這些程式碼的目的是將交易發送者的地址儲存在合約的儲存中。但是不能這樣寫嗎?

storage[0x00] = msg.sender;

好像更省油。

在那種特定情況下,是的,但否則絕對有必要:

類型address為 20 字節長,而儲存槽為 32 字節長。這意味著其他狀態變數可能被打包minter到儲存槽 0 中。這種打包可能是從編譯器自己生成佈局的solidity 完成的,或者從開發人員直接處理它的程序集中完成。

storage[0x00] = msg.sender | (storage[0x00] & ~0xffffffffffffffffffffffffffffffffffffffff);

只需確保storage[0]最後 20 個字節被清除並替換為 msg.sender,保留前 12 個字節。

這個使用“更高效”方法的範例對打包變數有副作用:boolean變成false因為sstore用零覆蓋它。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;

contract Coin {
   
   address public minter;
   bool public boolean = true;

   constructor() {
       assembly {
           sstore(0, caller())
       }
   }
}

編譯器只是優先考慮生成程式碼的正確性,而不是潛在地引起優化的副作用,這是正確的決定。

引用自:https://ethereum.stackexchange.com/questions/126427