美文网首页Solidity智能合约专题
Solidity中library的机制和内幕

Solidity中library的机制和内幕

作者: 梁帆 | 来源:发表于2022-11-09 18:21 被阅读0次

    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;
    

    这行关键代码使得库的方法与它操作的第一个参数(这里就是IterableMapping.Map)绑定,成为第一个参数的数据类型的方法。
    这里复杂变量Map结构体类型的定义是在library中完成的,库方法的第一个参数是来自storage存储空间的Map类型。

    2.机制与内幕

    • 库的internal是inline到调用者代码中的。
    • 库的public方法是library单独部署为合约,然后用delegatecall调用。所以,如果library有public方法,则必然是需要单独部署的(Linked Library)。如果全是internal方法,则可以不部署(Embedded Library)。
    • 当library因为有public而单独部署时,相比proxy pattern,都是利用另一个合约承载逻辑,但方式不同,一个是利用存储布局,一个是直接传递storage的引用,上下文变量都保持在调用者一边。调用者以delegatecall调用,由于library没有成员,被调用者只操作传入的参数,因此delegatecall不是像proxy pattern中的那样通过兼容存储布局利用另一合约逻辑的作用,而是通过操作storage属性的参数利用另一合约的逻辑。
    • library的public、external函数传入storage参数,从这一点看,说它时普通合约并不准确。
    • 调用者对单独部署的library的引用是在编译期完成的,不是在运行时,因此无法实现动态升级。
    • internal:对调用者可见,并且内联编译;
      private:library内部用,对library使用者不可见(可见性上,library类似于超类);
      public:对library使用者可见,但是合约要单独部署。
    • 交易属性:解释为对storage类型参数的操作,而不是成员,或者解释为对调用者成员变量的操作。

    相关文章

      网友评论

        本文标题:Solidity中library的机制和内幕

        本文链接:https://www.haomeiwen.com/subject/gedatdtx.html