Interfaces and libraries

Interfaces

An interface is a contract sugar type with restrictions. This type cannot be instantiated; it can only define the functions prototypes for a contract. This is useful as a generic interface.

// SPDX-License-Identifier: MIT

// Interface for an operator that performs an operation on two int32 values.
interface Operator {
    function performOperation(int32 a, int32 b) external returns (int32);
}

contract Ferqu {
    Operator public operator;

    // Constructor that takes a boolean parameter 'doAdd'.
    constructor(bool doAdd) {
        if (doAdd) {
            operator = new Adder();
        } else {
            operator = new Subtractor();
        }
    }

    // Function to calculate the result of the operation performed by the chosen operator.
    function calculate(int32 a, int32 b) public returns (int32) {
        return operator.performOperation(a, b);
    }
}

// Contract for addition, implementing the 'Operator' interface.
contract Adder is Operator {
    function performOperation(int32 a, int32 b) public pure override returns (int32) {
        return a + b;
    }
}

// Contract for subtraction, implementing the 'Operator' interface.
contract Subtractor is Operator {
    function performOperation(int32 a, int32 b) public pure override returns (int32) {
        return a - b;
    }
}
  • Interfaces can only have other interfaces as a base contract

  • All functions must the external visibilty

  • No constructor can be declared

  • No contract storage variables can exist (however constants are allowed)

  • No function can have a body or implementation

Libraries

Libraries are a special type of contract which can be reused in multiple contracts. Functions declared in a library can be called with the library.function() syntax. When the library has been imported or declared, any contract can use its functions simply by using its name.

library InstanceLibrary {
    function getMax(uint64 a, uint64 b) external pure returns (uint64) {
        return a > b ? a : b; // If 'a' is greater than 'b', it returns 'a'; otherwise, it returns 'b'.
    }
}

contract TestContract {
    using InstanceLibrary for uint64;

    // Calculate and return the maximum value between x and 65536 using the InstanceLibrary.
    function calculateMax(uint64 x) public pure returns (uint64) {
        return x.getMax(65536);
    }
}

When writing libraries there are restrictions compared to contracts:

  • A library cannot have constructors, fallback or receive function

  • A library cannot have base contracts

  • A library cannot be a base contract

  • A library cannot have virtual or override functions

  • A library cannot have payable functions

Note

When using the Ethereum Foundation Solidity compiler, libraries are a special contract type and are called using delegatecall. Solang statically links the library calls into your contract code. This generates larger contract code, however it reduces the call overhead and make it possible to do compiler optimizations across library and contract code.

Library Using For

Libraries can be used as method calls on variables. The type of the variable needs to be bound to the library, and the type of the first parameter of the function of the library must match the type of a variable.

library Library {
    function set(
        int32[100] storage data,
        uint256 index,
        int32 value
    ) internal  {
        data[index] = value;
    }
}

contract TestContract {
    using Library for int32[100];

    int32[100] public dataArray;

    function setElement(uint256 index, int32 value) public {
        dataArray.set(index, value);
    }
}

The syntax using library for Type ; is the syntax that binds the library to the type. This must be specified on the contract. This binds library lib to any variable with type int32[100]. As a result of this, any method call on a variable of type int32[100] will be matched to library lib.

For the call to match, the first argument of the function must match the variable; note that here, bar is of type storage, since all contract variables are implicitly storage.

There is an alternative syntax using library for *; which binds the library functions to any variable that will match according to these rules.