Solidity中l(wèi)ibrary的機制和內(nèi)幕

1.library的簡單案例

有兩個文件:

IterableMapping.sol
TestIterableMapping.sol

IterableMapping.sol為庫文件,代碼如下:

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

library IterableMapping {
    // Iterable mapping from address to uint;
    struct Map {
        address[] keys;
        mapping(address => uint) values;
        mapping(address => uint) indexOf;
        mapping(address => bool) inserted;
    }

    function get(Map storage map, address key) public view returns (uint) {
        return map.values[key];
    }

    function getKeyAtIndex(Map storage map, uint index) public view returns (address) {
        return map.keys[index];
    }

    function size(Map storage map) public view returns (uint) {
        return map.keys.length;
    }

    function set(
        Map storage map,
        address key,
        uint val
    ) public {
        if (map.inserted[key]) {
            map.values[key] = val;
        } else {
            map.inserted[key] = true;
            map.values[key] = val;
            map.indexOf[key] = map.keys.length;
            map.keys.push(key);
        }
    }

    function remove(Map storage map, address key) public {
        if (!map.inserted[key]) {
            return;
        }

        delete map.inserted[key];
        delete map.values[key];

        uint index = map.indexOf[key];
        uint lastIndex = map.keys.length - 1;
        address lastKey = map.keys[lastIndex];

        map.indexOf[lastKey] = index;
        delete map.indexOf[key];

        map.keys[index] = lastKey;
        map.keys.pop();
    }
}

TestIterableMapping.sol為合約文件,代碼如下:

contract TestIterableMapping {
    using IterableMapping for IterableMapping.Map;

    IterableMapping.Map private map;

    function testIterableMap() public {
        map.set(address(0), 0);
        map.set(address(1), 100);
        map.set(address(2), 200); // insert
        map.set(address(2), 200); // update
        map.set(address(3), 300);

        for (uint i = 0; i < map.size(); i++) {
            address key = map.getKeyAtIndex(i);

            assert(map.get(key) == i * 100);
        }

        map.remove(address(1));

        // keys = [address(0), address(3), address(2)]
        assert(map.size() == 3);
        assert(map.getKeyAtIndex(0) == address(0));
        assert(map.getKeyAtIndex(1) == address(3));
        assert(map.getKeyAtIndex(2) == address(2));
    }
}

可以看到,開頭通過

using IterableMapping for IterableMapping.Map;

這行關(guān)鍵代碼使得庫的方法與它操作的第一個參數(shù)(這里就是IterableMapping.Map)綁定,成為第一個參數(shù)的數(shù)據(jù)類型的方法。
這里復雜變量Map結(jié)構(gòu)體類型的定義是在library中完成的,庫方法的第一個參數(shù)是來自storage存儲空間的Map類型。

2.機制與內(nèi)幕

  • 庫的internal是inline到調(diào)用者代碼中的。
  • 庫的public方法是library單獨部署為合約,然后用delegatecall調(diào)用。所以,如果library有public方法,則必然是需要單獨部署的(Linked Library)。如果全是internal方法,則可以不部署(Embedded Library)。
  • 當library因為有public而單獨部署時,相比proxy pattern,都是利用另一個合約承載邏輯,但方式不同,一個是利用存儲布局,一個是直接傳遞storage的引用,上下文變量都保持在調(diào)用者一邊。調(diào)用者以delegatecall調(diào)用,由于library沒有成員,被調(diào)用者只操作傳入的參數(shù),因此delegatecall不是像proxy pattern中的那樣通過兼容存儲布局利用另一合約邏輯的作用,而是通過操作storage屬性的參數(shù)利用另一合約的邏輯。
  • library的public、external函數(shù)傳入storage參數(shù),從這一點看,說它時普通合約并不準確。
  • 調(diào)用者對單獨部署的library的引用是在編譯期完成的,不是在運行時,因此無法實現(xiàn)動態(tài)升級。
  • internal:對調(diào)用者可見,并且內(nèi)聯(lián)編譯;
    private:library內(nèi)部用,對library使用者不可見(可見性上,library類似于超類);
    public:對library使用者可見,但是合約要單獨部署。
  • 交易屬性:解釋為對storage類型參數(shù)的操作,而不是成員,或者解釋為對調(diào)用者成員變量的操作。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容