Bitmap & Masks
Last updated
Was this helpful?
Last updated
Was this helpful?
A bitmap refers to a data structure that uses a sequence of bits to represent the status or presence of certain elements or flags. It is commonly used to efficiently store and manipulate binary data, such as flags, permissions, or states.
In Solidity, a bitmap is typically represented using an unsigned integer (uint256), where each bit within the integer represents a specific element or flag. Each bit can be either set (1) or unset (0), indicating the presence or absence of a particular attribute or state.
By using bitwise operations, such as bitwise AND (&), bitwise OR (|), bitwise XOR (^), and bitwise negation (~), it is possible to manipulate individual bits within the bitmap efficiently. These operations allow for checking and setting specific bits, querying the status of flags, and performing other bitwise operations.
Using bitmaps can provide several benefits in terms of storage efficiency and gas efficiency.
Compact Storage: Bitmaps allow for efficient storage of multiple boolean or binary attributes within a single variable. Instead of using separate boolean variables or larger data types, a bitmap can represent multiple flags or states using a minimal amount of storage.
Gas Efficiency: Working with bitmaps can lead to gas savings in contract execution. Since bitwise operations operate on the binary representation of the data, they are generally less costly in terms of gas consumption compared to other operations like conditional statements or looping through individual flags.
Aave uses two types of bitmaps:
ReserveConfiguration: to track information related to each asset within Aave
UserConfiguration: to track which assets a user is supplying as collateral and borrowing
UserConfiguration
The bitmask has a 256 bit size, it is divided in pairs of bits, one for each asset. The first bit of the pair indicates if an asset is used as collateral by the user, the second whether an asset is borrowed by the user.
Advantages
If you need to traverse the list of assets for a user, to check what is being borrowed, just need to sload the bitmap to memory once; then read from memory.
Able to do complex queries cheaply and efficiently.
Especially useful for checking isolation and e-mode -> if certain assets are being used by the user.
Disadvantages:
Only 128 assets can be supported, to add more, another uint256 needs to be used.
For the calculation of the account data, the protocol would still need to query all listed assets.
A bitmask has also been introduced to store the reserve configuration, defined in figure 5. A similar packing could have been achieved by using uint32 and booleans, the bitmask benefits from more gas efficiency, and more so when updating multiple configurations at once.
&
: ANDThe bitwise AND operator ( &
) compares each bit of the first operand to the corresponding bit of the second operand.
If both bits are 1, the corresponding result bit is set to 1.
Else, the result bit is set to 0.
~
: NOTThe bitwise NOT operator ( ~
) inverts the bits of the binary sequence.
input bit 1 -> 0
input bit 0 -> 1
<<
and >>
: Left shift and right shiftThe bitwise shift operators move the bit values of a binary object, by the defined number of bits.
<<
)When shifting left, the most-significant bit is lost, and a 0
bit is inserted on the other end. Essentially, all the bits take a step to the left, with the MSB falling off; right-padded with 0
.
A single left shift multiplies a binary number by 2:
When shifting right, the least-significant bit is lost and a 0
is inserted on the other end. Essentially, all the bits take a step to the right, with the LSB falling off; left-padded with 0
.
For positive numbers, a single logical right shift divides a number by 2, throwing out any remainders.
Aave utilizes 2 bitmaps, ReserveConfigurationMap and UserConfigurationMap, both defined within the library DataTypes
.
Let's examine how reserve factor (bit 64-79) can be retrieved from this bitmap.
The mask is a 64 digit hexadecimal number, representing 256 bits.
each hexadecimal digit represents 4 bits => 64 * 4 = 256 bits
first 64 digits are set to 1
, followed by 16 bits set to 0
, then 176 bits set to 1
remember, binary is read from right to left. The rightmost place number being zero.
And that is how the reserve factor is extracted from the bitmap as a uint256 value.
consider isUsingAsCollateralOrBorrowing
From :