One reason to start using Solidity Libraries

There's a design pattern that arose out of my most recent refactoring that is worth sharing.  It involves pushing all of the functionality for a contract into a library and having the contract functions merely delegate out to the library functions.  By doing this, you can make significant reductions in the deploy costs for contracts which are going to be deployed a lot of times.

Lets look at how this would work for a modified version of the greeter contract.

contract GreeterA {
        bytes32 greeting;
        uint count;
        address[] greeted;

        function GreeterA(bytes32 _greeting) {
                greeting = _greeting;
        }

        function greet() public returns (bytes32) {
                count += 1;
                greeted.push(msg.sender);
                return greeting;
        }
}

This is a pretty simple contract, but it can be refactored to delegate it's functionality out to a library.

library GreeterLib {
        struct Greeting {
                bytes32 greeting;
                uint count;
                address[] greeted;
        }

        function greet(Greeting storage self, address who) public returns (bytes32) {
                self.count += 1;
                self.greeted.push(who);
                return self.greeting;
        }
}


contract Greeter {
        GreeterLib.Greeting greeting;

        function Greeter(bytes32 _greeting) {
                greeting.greeting = _greeting;
        }

        function greet() public returns (bytes32) {
                return GreeterLib.greet(greeting, msg.sender);
        }
}

Benchmarking these two contracts yields the following results.

  • Call gas cost
    • Normal: 81883
    • Library: 82185
  • Deploy gas cost
    • Normal: 87856
    • Library: 80482

The library contract costs an additional 302 gas to call, but it's 7,374 gas cheaper to deploy.  For small contracts which are deployed often and only called a small number of times, the gas savings are significant.  The reason for this difference comes from the fact that the library version doesn't include much more than CALLCODE operations that delegate to the deployed library contract.

It's worth pointing out that the library version of the contract passes msg.sender into the library call.  When you access msg.sender from within a library, the address is that of the contract which called the library function as opposed to the address that called the contract itself.