[Ethereum] the reason behind writing “using Counters for Counters.counters” when using the Counters openzeppelin library

openzeppelinsolidity

While trying to understand the OpenZeppelin contracts that a contract inherited from, I stumbled upon the line using Counters for Counters.Counter; after the counters library of OpenZeppelin was imported: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Counters.sol

I realized that this way of using the library is "advised" in the source code on OpenZeppelin GitHub. However, I don't understand why. Quick researches taught me that the using keyword was used to allow specific types to use functions from a library as if they were properties from those types (https://medium.com/coinmonks/soliditys-using-keyword-c05c18aaa088).

I would have thought importing the library would have been enough. If anyone could explain to me the coding logic behind that, it would help me a lot. Thanks.

Best Answer

Counters is a library.

When we say using A for B, it means that we attach every function from library A to type B.

Libraries are an efficient way to reduce gas fees, because they are deployed only once at a specific address with its code being reused by various contracts.

So back to the question. Counter is a struct data type inside the Counters library.

Here is its source code:

library Counters {
    struct Counter {
        uint256 _value; // default: 0
    }

    function current(Counter storage counter) internal view returns (uint256) {
        return counter._value;
    }

    function increment(Counter storage counter) internal {
        unchecked {
            counter._value += 1;
        }
    }

    function decrement(Counter storage counter) internal {
        uint256 value = counter._value;
        require(value > 0, "Counter: decrement overflow");
        unchecked {
            counter._value = value - 1;
        }
    }

    function reset(Counter storage counter) internal {
        counter._value = 0;
    }
}

When you say using Counters for Counters.Counter, it means that we assign all the functions inside the Counters library, like current() or increment(), to the Counter struct.

When you call those functions, the first parameter to those functions are the type itself which, in this case, is the Counter struct.

So in the following code, when we call clock.current(), it passes the clock, which is a struct, as the first parameter of the current() function.

using Counters for Counters.Counter;

Counters.Counter clock;

clock.current() // results to 0