Commit e6b62839 by github-actions

Merge upstream openzeppelin-contracts into upstream-patched

parents 87bb8135 f7da53ce
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
* Tokens: Wrap definitely safe subtractions in `unchecked` blocks. * Tokens: Wrap definitely safe subtractions in `unchecked` blocks.
* `Math`: Add a `ceilDiv` method for performing ceiling division. * `Math`: Add a `ceilDiv` method for performing ceiling division.
* `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593)) * `ERC1155Supply`: add a new `ERC1155` extension that keeps track of the totalSupply of each tokenId. ([#2593](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2593))
* `BitMaps`: add a new `BitMaps` library that provides a storage efficient datastructure for `uint256` to `bool` mapping with contiguous keys. ([#2710](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2710))
### Breaking Changes ### Breaking Changes
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/structs/BitMaps.sol";
contract BitMapMock {
using BitMaps for BitMaps.BitMap;
BitMaps.BitMap private _bitmap;
function get(uint256 index) public view returns (bool) {
return _bitmap.get(index);
}
function setTo(uint256 index, bool value) public {
_bitmap.setTo(index, value);
}
function set(uint256 index) public {
_bitmap.set(index);
}
function unset(uint256 index) public {
_bitmap.unset(index);
}
}
...@@ -80,6 +80,8 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar ...@@ -80,6 +80,8 @@ Note that, in all cases, accounts simply _declare_ their interfaces, but they ar
== Data Structures == Data Structures
{{BitMaps}}
{{EnumerableMap}} {{EnumerableMap}}
{{EnumerableSet}} {{EnumerableSet}}
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, providing the keys are sequential.
* Largelly inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
*/
library BitMaps {
struct BitMap {
mapping(uint256 => uint256) _data;
}
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
uint256 bucket = index / 256;
uint256 mask = 1 << (index % 256);
return bitmap._data[bucket] & mask != 0;
}
/**
* @dev Sets the bit at `index` to the boolean `value`.
*/
function setTo(
BitMap storage bitmap,
uint256 index,
bool value
) internal {
if (value) {
set(bitmap, index);
} else {
unset(bitmap, index);
}
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index / 256;
uint256 mask = 1 << (index % 256);
bitmap._data[bucket] |= mask;
}
/**
* @dev Unsets the bit at `index`.
*/
function unset(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index / 256;
uint256 mask = 1 << (index % 256);
bitmap._data[bucket] &= ~mask;
}
}
...@@ -8,7 +8,12 @@ ...@@ -8,7 +8,12 @@
[.contract] [.contract]
[[{{anchor}}]] [[{{anchor}}]]
=== `++{{name}}++` === `++{{name}}++` link:{{github-link file.path}}[{github-icon},role=heading-link]
[.hljs-theme-light.nopadding]
```solidity
import "@openzeppelin/contracts/{{file.path}}";
```
{{natspec.devdoc}} {{natspec.devdoc}}
......
const { version } = require('../package.json');
module.exports = {
'github-link': (contractPath) => {
if (typeof contractPath !== 'string') {
throw new Error('Missing argument');
}
return `https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v${version}/contracts/${contractPath}`;
},
};
:github-icon: pass:[<svg class="icon"><use href="#github-icon"/></svg>]
{{#links}} {{#links}}
:{{slug target.fullName}}: pass:normal[xref:{{path}}#{{target.anchor}}[`{{target.fullName}}`]] :{{slug target.fullName}}: pass:normal[xref:{{path}}#{{target.anchor}}[`{{target.fullName}}`]]
:xref-{{slug target.anchor}}: xref:{{path}}#{{target.anchor}} :xref-{{slug target.anchor}}: xref:{{path}}#{{target.anchor}}
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
"compile": "hardhat compile", "compile": "hardhat compile",
"coverage": "hardhat coverage", "coverage": "hardhat coverage",
"docs": "oz-docs", "docs": "oz-docs",
"docs:watch": "npm run docs watch contracts 'docs/*.hbs'", "docs:watch": "npm run docs watch contracts 'docs/*.hbs' docs/helpers.js",
"prepare-docs": "scripts/prepare-docs.sh", "prepare-docs": "scripts/prepare-docs.sh",
"lint": "npm run lint:js && npm run lint:sol", "lint": "npm run lint:js && npm run lint:sol",
"lint:fix": "npm run lint:js:fix && npm run lint:sol:fix", "lint:fix": "npm run lint:js:fix && npm run lint:sol:fix",
......
...@@ -15,6 +15,7 @@ solidity-docgen \ ...@@ -15,6 +15,7 @@ solidity-docgen \
-o "$OUTDIR" \ -o "$OUTDIR" \
-e contracts/mocks,contracts/examples \ -e contracts/mocks,contracts/examples \
--output-structure readmes \ --output-structure readmes \
--helpers ./docs/helpers.js \
--solc-module ./scripts/prepare-docs-solc.js --solc-module ./scripts/prepare-docs-solc.js
node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc" node scripts/gen-nav.js "$OUTDIR" > "$OUTDIR/../nav.adoc"
const { BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const BitMap = artifacts.require('BitMapMock');
contract('BitMap', function (accounts) {
const keyA = new BN('7891');
const keyB = new BN('451');
const keyC = new BN('9592328');
beforeEach(async function () {
this.bitmap = await BitMap.new();
});
it('starts empty', async function () {
expect(await this.bitmap.get(keyA)).to.equal(false);
expect(await this.bitmap.get(keyB)).to.equal(false);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
describe('setTo', function () {
it('set a key to true', async function () {
await this.bitmap.setTo(keyA, true);
expect(await this.bitmap.get(keyA)).to.equal(true);
expect(await this.bitmap.get(keyB)).to.equal(false);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
it('set a key to false', async function () {
await this.bitmap.setTo(keyA, true);
await this.bitmap.setTo(keyA, false);
expect(await this.bitmap.get(keyA)).to.equal(false);
expect(await this.bitmap.get(keyB)).to.equal(false);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
it('set several consecutive keys', async function () {
await this.bitmap.setTo(keyA.addn(0), true);
await this.bitmap.setTo(keyA.addn(1), true);
await this.bitmap.setTo(keyA.addn(2), true);
await this.bitmap.setTo(keyA.addn(3), true);
await this.bitmap.setTo(keyA.addn(4), true);
await this.bitmap.setTo(keyA.addn(2), false);
await this.bitmap.setTo(keyA.addn(4), false);
expect(await this.bitmap.get(keyA.addn(0))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(1))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(2))).to.equal(false);
expect(await this.bitmap.get(keyA.addn(3))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(4))).to.equal(false);
});
});
describe('set', function () {
it('adds a key', async function () {
await this.bitmap.set(keyA);
expect(await this.bitmap.get(keyA)).to.equal(true);
expect(await this.bitmap.get(keyB)).to.equal(false);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
it('adds several keys', async function () {
await this.bitmap.set(keyA);
await this.bitmap.set(keyB);
expect(await this.bitmap.get(keyA)).to.equal(true);
expect(await this.bitmap.get(keyB)).to.equal(true);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
it('adds several consecutive keys', async function () {
await this.bitmap.set(keyA.addn(0));
await this.bitmap.set(keyA.addn(1));
await this.bitmap.set(keyA.addn(3));
expect(await this.bitmap.get(keyA.addn(0))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(1))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(2))).to.equal(false);
expect(await this.bitmap.get(keyA.addn(3))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(4))).to.equal(false);
});
});
describe('unset', function () {
it('removes added keys', async function () {
await this.bitmap.set(keyA);
await this.bitmap.set(keyB);
await this.bitmap.unset(keyA);
expect(await this.bitmap.get(keyA)).to.equal(false);
expect(await this.bitmap.get(keyB)).to.equal(true);
expect(await this.bitmap.get(keyC)).to.equal(false);
});
it('removes consecutive added keys', async function () {
await this.bitmap.set(keyA.addn(0));
await this.bitmap.set(keyA.addn(1));
await this.bitmap.set(keyA.addn(3));
await this.bitmap.unset(keyA.addn(1));
expect(await this.bitmap.get(keyA.addn(0))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(1))).to.equal(false);
expect(await this.bitmap.get(keyA.addn(2))).to.equal(false);
expect(await this.bitmap.get(keyA.addn(3))).to.equal(true);
expect(await this.bitmap.get(keyA.addn(4))).to.equal(false);
});
it('adds and removes multiple keys', async function () {
// []
await this.bitmap.set(keyA);
await this.bitmap.set(keyC);
// [A, C]
await this.bitmap.unset(keyA);
await this.bitmap.unset(keyB);
// [C]
await this.bitmap.set(keyB);
// [C, B]
await this.bitmap.set(keyA);
await this.bitmap.unset(keyC);
// [A, B]
await this.bitmap.set(keyA);
await this.bitmap.set(keyB);
// [A, B]
await this.bitmap.set(keyC);
await this.bitmap.unset(keyA);
// [B, C]
await this.bitmap.set(keyA);
await this.bitmap.unset(keyB);
// [A, C]
expect(await this.bitmap.get(keyA)).to.equal(true);
expect(await this.bitmap.get(keyB)).to.equal(false);
expect(await this.bitmap.get(keyC)).to.equal(true);
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment