1

Here is what am i doing:

There is a library, accept storage argument as parameter, then do some math and return the result.

And there is a contract to call the function in the library, and an extra thing I want to do is maybe the implementation of the library should upgrade and accept by the contract, so in the constructor, the address of library has passed in.

Here are the example codes.

pragma solidity ^0.5.2;

library ChangeableLib
{
    function callme(uint x, uint y) external pure returns(uint) {
        return x+y;
    }
    function callme2(uint[] storage x, uint y) external returns(uint) {
        x.push(x[0]+x[1]+y);
        return x[3];
    }
}

interface interfaceLib {
    function callme(uint x, uint y) external view returns(uint);
    function callme2(uint[] calldata x, uint y) external returns(uint);
}

contract Test {
    interfaceLib _interface;
    constructor(address i) public {
        _interface = interfaceLib(i);
    }

    uint[] public xs;

    function callLibFunc(uint x, uint y) public view returns(uint) {
        return _interface.callme(x, y);
    }

    function callLibFunc2(uint y) public returns(uint) {
        xs.push(1);
        xs.push(2);
        return _interface.callme2(xs, y);
    }
}

"callLibFunc" return correct result, however "callLibFunc2" will return with error, it supposed that the storage could not pass in this way? Thanks.

1 Answer 1

0

Interfaces are for talking to other contracts. They do not apply to libraries.

Linking of libraries and contracts is still a concern but it is addressed by different means. The library address is baked into the bytecode. Remix will do this for you, which is very convenient for experimenting with the example below. Truffle uses deployer.link(Library, Contract) to actually manipulate the Contract's pre-deployment, compiler-outputted bytecode. In other words, the linking is a deployment concern rather than a contract coding concern, so you can safely set that concern aside until later. In case that sounds mysterious, have a look here: what does `deploy.link` exactly do in `truffle`

The compiler doesn't need an interface for a library, but it does need to see the Library code. Use import or a flat code structure (as below) so the compiler can see ChangeableLib when Test is compiled. That's it.

Another difference to get acquainted with is the way library Methods can be attached to Types in a contract. In the example below, it would be valid to call a library method with ChangeableLib.write(uint[] storage pointer, uint value) (or read). There is a little compiler sugar that can make it more concise.

using attaches the library methods to a type or types. The instance that invokes the library method will be passed in as the first argument, which is often what you want.

As a PSA, the example includes an iterative process which is an anti-pattern. You should not do that in production code unless you know what you are doing. You can load this in Remix to see it in action.

pragma solidity ^0.5.2;

library ChangeableLib
{

    function write(uint[] storage x, uint y) internal {
        x.push(y);
    }

    function read(uint[] storage x) internal view returns(uint sum) {
        for(uint i=0; i<x.length; i++) {
            sum += x[i];
        }
    }
}

contract Test {

    using ChangeableLib for uint[];

    uint[] public numbers;

    function push(uint y) public {
        numbers.push(y); // this works because: using Changeable for uint[]
    }

    function adder() public view returns(uint) {
        // return ChangeableLib.read(numbers); // <== the long way
        return numbers.read(); // <== the concise way
   }
}

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.