Solidity

我們可以在solidity 庫中使用儲存關鍵字嗎?

  • March 25, 2022

我正在閱讀有關圖書館的文章,並看到文章說圖書館不能有儲存空間。

但我見過使用儲存關鍵字的庫。

範例:在 Aave 協議 v3 中

function calculateUserAccountData(
   address user,
   mapping(address => DataTypes.ReserveData) storage reservesData,
   DataTypes.UserConfigurationMap memory userConfig,
   mapping(uint256 => address) storage reserves,
   uint256 reservesCount,
   address oracle
 )
   internal
   view
   returns (
     uint256,
     uint256,
     uint256,
     uint256,
     uint256
   )
 {
...........
DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];

這是否意味著在內部函式儲存中可以用作從庫中導入的函式?

我錯過了什麼嗎?

並不是庫中禁止使用 storage 關鍵字,而是它們不能擁有自己的狀態變數,因為它們是無狀態的。

狀態變數在合約範圍級別聲明,因此它是合約全域狀態的一部分。

在函式作用域級別聲明了一個局部儲存變數,只要函式正在執行,它就是“函式狀態”的一部分,那麼這個變數就會被丟棄。重要的一點是,本地儲存變數本身很可能是一個引用,因此,丟棄它並不意味著它所引用的內容也被丟棄。

所以這是禁止的:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

library TestLibrary {
   uint256 stateVariable;
}

因為stateVariable很好……一個狀態變數,因為它是在契約/庫級別聲明的。TypeError: Library cannot have non-constant state variables編譯按預期失敗。

另一方面,您提供的程式碼不同,storage只有當該參數是呼叫合約的狀態變數時,接收參數才真正有意義。這是對呼叫者狀態變數的引用,允許庫函式讀取/寫入呼叫合約的儲存。

如果庫被部署,這些庫呼叫實際上是編譯delegatecall的,如果庫被嵌入,它將簡單地包含在合約的程式碼中。如果我沒記錯的話,您的範例是一個嵌入式庫,但這並不重要。

第二種用法:

DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];

只需創建一個直接引用reservesData[vars.currentReserveAddress].

文件中:

從儲存到本地儲存變數的分配也只分配一個引用。

現在我可能錯過了一些東西,但在我看來,這只是一種避免寫入reservesData[vars.currentReserveAddress]訪問該變數的方法。現在可以使用currentReserve.

關鍵是這也不是庫的狀態變數,它只是對屬於呼叫者儲存/狀態的儲存變數的引用。

也許一個更精確的讀/寫範例(例如這個)是一種更好的方式來完全回答您關於使用(連結)庫的儲存參數/變數的問題:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

struct Wrapper {
   uint256 value;
}

library TestLibrary {

   function testLocalStorageParameter(Wrapper storage wrapper) public {
       // wrapper is a reference to a storage variable from the calling contract
       // Modifying wrapper here modifies the state variable of the calling contract.
       wrapper.value = 10;
   }

   function testLocalStorageVariable(Wrapper storage wrapper) public {
       // wrapper is a reference to a storage variable from the calling contract

       // secondWrapper is also a reference to a storage variable from the the calling contract
       // "Assignments from storage to a local storage variable also only assign a reference."
       Wrapper storage secondWrapper = wrapper;

       // Modifying secondWrapper here also modifies the state variable of the calling contract.
       secondWrapper.value = 20;
   }
}

contract TestContract{
   
   Wrapper wrapper; // wrapper.value = 0 by default

   function getValue() public view returns(uint256) {
       return wrapper.value;
   }

   function calltestLocalStorageParameter() public {
       // After this library call wrapper.value  = 10
       TestLibrary.testLocalStorageParameter(wrapper);
   }

   function calltestLocalStorageVariable() public {
       // After this library call wrapper.value  = 20
       TestLibrary.testLocalStorageVariable(wrapper);
   }
}

合約將其自己的狀態變數的儲存引用提供給庫,以便庫可以對其進行操作。該庫從來沒有自己的狀態變數,但是如果將其作為參數引用,則允許它對呼叫它的合約的狀態變數進行操作。

我希望這能回答你的問題。

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