Solidity Tutorial: all about Libraries

Table of Contents

References

1. What are Libraries in Solidity?

Libraries can be seen as implicit base contracts of the contracts that use them (Solidity doc)

A library in Solidity is a different type of smart contract that contains reusable code. Once deployed on the blockchain (only once), it is assigned a specific address and its properties / methods can be reused many times by other contracts in the Ethereum network.

They enable to develop in a more modular way. Sometimes, it is helpful to think of a library as a singleton in the EVM, a piece of code that can be called from any contract without the need to deploy it again.

Benefits

However, libraries in Solidity are not only limited to reusability. Here are some of their advantages :

Limitations

Libraries in Solidity are considered stateless, and hence have the following restrictions

“Ideally, libraries are not meant to change state of contract, it should only be used to perform simple operations based on input and returns result”

However, libraries have the benefits to save substantial amount of gas (and therefore, not contaminating the blockchain with repetitive code), since the same code doesn’t have to be deployed over and over. Different contract on Ethereum can just rely on the same already deployed library.

The fact that multiple contracts depend on the exact piece of code, can make for a more secure environment. Imagine not only having well audited code for common endeavours (like the tremendous job the guys at Zeppelin are doing), but relying on the same deployed library code that other contracts are already using. It would certainly have helped in this case, where all balances of an ERC20 token (nothing too fancy), that was intended to raise a maximum of $50M, were whipped out.

2. How to create a library contract?

Define a library and variables allowed

You define a library contract with the keyword library instead of the traditional contract keyword used for standard smart contracts. Just declare libary under the pragma solidity statement (compiler version). See our code example below.

pragma solidity ^0.5.0;library libraryName {    // struct, enum or constant variable declaration    // function definition with body
}

As we have seen, libraries contracts do not have storage. Therefore, they can’t hold state variables (state variables that are non-constant). However, libraries can still implement some data type :

Let’s start with a simple example : a library for mathematical operation.

We have created below a simple math library called MathLib, that contains basic arithmetic operation which takes 2 unsigned integer as input and returns the arithmetic operation result.

pragma solidity ^0.5.0;library MathLib {

struct MathConstant {
uint Pi; // π (Pi) ≈ 3.1415926535...
uint Phi; // Golden ratio ≈ 1.6180339887...
uint Tau; // Tau (2pi) ≈ 6.283185...
uint Omega; // Ω (Omega) ≈ 0.5671432904...
uint ImaginaryUnit; // i (Imaginary Unit) = √–1
uint EulerNb; // Euler number ≈ 2.7182818284590452...
uint PythagoraConst; // Pythagora constant (√2) ≈ 1.41421...
uint TheodorusConst; // Theodorus constant (√3) ≈ 1.73205...
}

}

SafeMath library available in open zeppelin smart contracts collection is a popular solidity library that is used to protect from overflow.

3. How to deploy libraries?

Library deployment is a bit different from regular smart contract deployment. Here are two scenarios :

4. How to use a Library in a smart contract?

Step 1 : Importing the library

Under the pragma , just specify the following statement :

import LibraryName from “./library-file.sol”;

Your file could also contain multiple libraries. Using curly braces {} in the import statement, you can specify which library you would like to import. Imagine you have a file my-lib.sol like this :

pragma solidity >0.5.0;library Library1 {
// Code from library 1
}
library Library2 {
// Code from library 2
}
library Library3 {
// Code fom library 3
}

You can specify which library you want to use in your main contract as follow :

pragma solidity ^0.5.0;// We choose to use only Library 1 and 3 here, and exclude Library 2
import {Library1, Library3} from "./library-file.sol";
contract MyContract {

// Your contract code here
}

Step 2 : Using the Library

To use a library within a smart contract, we use the syntax using LibraryName for Type . This directive can be use to attach library functions (from the library LibraryName) to any type (Type).

// Use the library for unsigned integers
using MathLib for uint;
// Use the library for any data type
using MathLib for *;

In both previous situations, all functions from the library are attached to the contract, including those where the type of the first parameter does not match the type of the object. The type is checked at the point the function is called and function overload resolution is performed.

By including a library, its data types including library functions are available without having to add further code. When you call a library function, these functions will receive the object they are called on as their first parameter, much like the variable self in Python

import {MathLib} from  './lib-file.sol';using MathLib for uint;uint a = 10;
uint b= 10;
uint c = a.subUint(b);

We could still do uint c = a - b; It will return the same result which is 0. However our library has some added properties to protect from overflow for example; assert(a >= b); which checks to make sure the first operand ais greater than or equal to the second operand b so that the subtraction operation doesn’t result to a negative value.

Another good syntax that makes our code easily understandable is to use the keyword using with a library function as a method of its first parameter. With our MathConstant example, it would be :

using MathLib for MathLib.MathConstant

The using keyword allows for calling functions in MathLib for all functions that take a MathConstant as a first argument, as if they were a method of the struct.

This construct is pretty similar of how you can execute methods on Go structs, without them being fully-fledged objects.

5. How to interact with libraries?

Introduction : a bit of theory

If library functions are called, their code is executed in the context of the calling contract

Let’s dive a bit deeper into the Solidity documentation. The doc states the following : “Libraries can be seen as implicit base contracts of the contracts that use them” :

Calling a function from a library will use a special instruction in the EVM: the DELEGATECALL opcode (CALLCODE until Homestead, version 0.4.20). This will cause the calling context to be passed to the library, like if it was some code running in the contract itself. Let’s look at a code example :

pragma solidity ^0.5.0;library MathLib {

function multiply(uint a, uint b) public view returns (uint, address) {
return (a * b, address(this));
}
}
contract Example {

using MathLib for uint;
address owner = address(this);

function multiplyExample(uint _a, uint _b) public view returns (uint, address) {
return _a.mult(_b);
}
}

If we call multiply() function of the MathLib library from our contract (via the function multiplyExample), the address of MyContract will be returned and not the library address. The multiply() function is compiled as an external call ( DELEGATECALL ) but if we check the address returned in the return statement, we will see our contract address (not the library contract’s address).

The same applies for all other msg properties ( msg.value, msg.data and msg.gas).

Warning : prior to Homestead , msg.sender and msg.value changed ! (because of the use of CALLCODE)

Exception : Library functions can only be called directly (without the use of DELEGATECALL) if they do not modify the state (view or pure functions)

Furthermore, internal functions of libraries are visible in all contracts, just as if the library were a base contract.

Let’s unpack the process of calling an internal library function, to understand what happen exactly is the following :

Calls to internal functions use the same internal calling convention :

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Despite the fact libraries do not have storage, they can modify their linked contract’s storage, by passing a storage argument to their function parameters. Hence any modification made by the library does via its function will be saved in the contract’s own storage.

Therefore, the keyword this will point to the calling contract, and refers to the storage of the calling contract to be precise.

Since a library is an isolated piece of code, it can only access state variables from the calling contract if they are explicitly supplied. In fact, it would have no way to name them, otherwise.

We want to use a library function !

Ok enough theory, let’s look at some practical examples ! :)

To use a library function, we select the variable we want to apply the library function to and add a . followed by the library function name we want to use. Type variable = variable.libraryFunction(Type argument).

Here is an example below.

pragma solidity ^0.5.0;contract MyContract {

using MathLib for uint;

uint256 a = 10;
uint256 b= 10;
uint256 c = a.add(b);

}
library MathLib {

function add(uint256 a, uint256 b) external pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}

}

6. Understanding functions in libraries

A library can have functions which are not implemented (like in interfaces). As a result, these type of functions have to be declared as public or external. They can’t be private or internal.

A picture is worth a thousand words !

Here is our internal function implemented in our testConnection() function :

function testConnection(address _from, address _to) internal returns(bool) {
if (true) {
emit testConnection(_from, _to, connection_success_message);
return true;
}
}

An example of not implemented functions in our situation would be callback functions on connect and disconnect. You would implement your own custom code in the function body.

function onConnect() public;
function onDisconnect() public;

7. Events and Libraries

In the same way that libraries don’t have storage, they don’t have an event log. However, libraries can dispatch events.

Any event created by the library will be saved in the event log of the contract that calls the event emitting function in the library.

Only problem is, as of right now, the contract ABI does not reflect the events that the libraries it uses may emit. This confuses clients such as web3, that won’t be able to decode what event was called or figure out how to decode its arguments.

This topic is discussed in depth in an article from Gnosis about DelegateProxy contracts.

8. Mappings in Libraries = more functionalities !

Using the mapping type inside libraries differ compared to its usage in traditional Solidity smart contracts. Here we will discuss about using it as a parameter type inside a function

Extended functionality :

In comparison, mappings can only be passed as a parameter for internal or private functions inside contracts.

Warning : The data location can only be storage if a mapping is passed as function parameters

Since we are using a mapping inside a library, we can’t instantiate it inside the library (remember, libraries can’t hold state variables). Let’s look at a “possible” an example of what it could look like:

pragma solidity ^0.5.0;library Messenger {    event LogFunctionWithMappingAsInput(
address from,
address to,
string message
);
function sendMessage(
address to,
mapping (string => string) storage aMapping
) public {
emit LogFunctionWithMappingAsInput(
msg.sender,
to,
aMapping[“test1”]
);
}
}

9. Using Structs inside Libraries

library Library3 {

struct hold{
uint a;
}

function subUint(hold storage s, uint b) public view returns(uint){

// Make sure it doesn’t return a negative value.
require(s.a >= b);
return s.a — b;

}
}

Notice how the function subUint receives a struct as argument. In Solidity v0.4.24, this is not possible in contracts, but possible in Solidity libraries

10. Linking libraries with other libraries

Libraries can be linked with other libraries and use them in the same way a contract would. This come however with the natural limitations of libraries.

It is then possible to include libraries inside other libraries, as explained here :

https://ethereum.stackexchange.com/questions/45032/is-it-possible-to-use-a-solidity-library-inside-another

11. Using modifiers inside libraries

It is possible to use modifiers in libraries. However, we can’t export them in our contract, because modifiers are compile-time features (a sort of macros).

See this : https://ethereum.stackexchange.com/questions/68529/solidity-modifiers-in-library

Therefore, modifiers only apply to the functions of the library itself, and cannot be used by an external contract of the library.

12. What you can’t do with Libraries in Solidity?

13. Some popular existing libraries

Here is a curated list of some already libraries written in Solidity and their authors :

Full Stack Developer, Web / Mobile and Blockchain. https://github.com/CJ42