Update docs

This commit is contained in:
github-actions
2022-04-26 16:51:43 +00:00
parent d75a9e5480
commit 84526a0944
151 changed files with 17094 additions and 11955 deletions

View File

@ -47,7 +47,7 @@ function split (signature) {
web3.utils.bytesToHex(raw.slice(32, 64)), // s
];
default:
expect.fail('Invalid siganture length, cannot split');
expect.fail('Invalid signature length, cannot split');
}
}

View File

@ -24,6 +24,12 @@ contract('MerkleProof', function (accounts) {
const proof = merkleTree.getHexProof(leaf);
expect(await this.merkleProof.verify(proof, root, leaf)).to.equal(true);
// For demonstration, it is also possible to create valid proofs for certain 64-byte values *not* in elements:
const noSuchLeaf = keccak256(
Buffer.concat([keccak256(elements[0]), keccak256(elements[1])].sort(Buffer.compare)),
);
expect(await this.merkleProof.verify(proof.slice(1), root, noSuchLeaf)).to.equal(true);
});
it('returns false for an invalid Merkle proof', async function () {

View File

@ -69,6 +69,28 @@ const INTERFACES = {
'castVoteWithReason(uint256,uint8,string)',
'castVoteBySig(uint256,uint8,uint8,bytes32,bytes32)',
],
GovernorWithParams: [
'name()',
'version()',
'COUNTING_MODE()',
'hashProposal(address[],uint256[],bytes[],bytes32)',
'state(uint256)',
'proposalSnapshot(uint256)',
'proposalDeadline(uint256)',
'votingDelay()',
'votingPeriod()',
'quorum(uint256)',
'getVotes(address,uint256)',
'getVotesWithParams(address,uint256,bytes)',
'hasVoted(uint256,address)',
'propose(address[],uint256[],bytes[],string)',
'execute(address[],uint256[],bytes[],bytes32)',
'castVote(uint256,uint8)',
'castVoteWithReason(uint256,uint8,string)',
'castVoteWithReasonAndParams(uint256,uint8,string,bytes)',
'castVoteBySig(uint256,uint8,uint8,bytes32,bytes32)',
'castVoteWithReasonAndParamsBySig(uint256,uint8,string,bytes,uint8,bytes32,bytes32)',
],
GovernorTimelock: [
'timelock()',
'proposalEta(uint256)',
@ -90,34 +112,33 @@ for (const k of Object.getOwnPropertyNames(INTERFACES)) {
}
function shouldSupportInterfaces (interfaces = []) {
describe('Contract interface', function () {
describe('ERC165', function () {
beforeEach(function () {
this.contractUnderTest = this.mock || this.token || this.holder || this.accessControl;
});
for (const k of interfaces) {
const interfaceId = INTERFACE_IDS[k];
describe(k, function () {
describe('ERC165\'s supportsInterface(bytes4)', function () {
it('uses less than 30k gas [skip-on-coverage]', async function () {
expect(await this.contractUnderTest.supportsInterface.estimateGas(interfaceId)).to.be.lte(30000);
});
it('supportsInterface uses less than 30k gas', async function () {
for (const k of interfaces) {
const interfaceId = INTERFACE_IDS[k];
expect(await this.contractUnderTest.supportsInterface.estimateGas(interfaceId)).to.be.lte(30000);
}
});
it('claims support [skip-on-coverage]', async function () {
expect(await this.contractUnderTest.supportsInterface(interfaceId)).to.equal(true);
});
});
it('all interfaces are reported as supported', async function () {
for (const k of interfaces) {
const interfaceId = INTERFACE_IDS[k];
expect(await this.contractUnderTest.supportsInterface(interfaceId)).to.equal(true);
}
});
it('all interface functions are in ABI', async function () {
for (const k of interfaces) {
for (const fnName of INTERFACES[k]) {
const fnSig = FN_SIGNATURES[fnName];
describe(fnName, function () {
it('has to be implemented', function () {
expect(this.contractUnderTest.abi.filter(fn => fn.signature === fnSig).length).to.equal(1);
});
});
expect(this.contractUnderTest.abi.filter(fn => fn.signature === fnSig).length).to.equal(1);
}
});
}
}
});
});
}

View File

@ -0,0 +1,96 @@
const { expectEvent } = require('@openzeppelin/test-helpers');
const { expectRevertCustomError } = require('../../helpers/customError');
const Bytes32DequeMock = artifacts.require('Bytes32DequeMock');
/** Rebuild the content of the deque as a JS array. */
async function getContent (deque) {
const length = await deque.length().then(bn => bn.toNumber());
const values = await Promise.all(Array(length).fill().map((_, i) => deque.at(i)));
return values;
}
contract('DoubleEndedQueue', function (accounts) {
const bytesA = '0xdeadbeef'.padEnd(66, '0');
const bytesB = '0x0123456789'.padEnd(66, '0');
const bytesC = '0x42424242'.padEnd(66, '0');
const bytesD = '0x171717'.padEnd(66, '0');
beforeEach(async function () {
this.deque = await Bytes32DequeMock.new();
});
describe('when empty', function () {
it('getters', async function () {
expect(await this.deque.empty()).to.be.equal(true);
expect(await getContent(this.deque)).to.have.ordered.members([]);
});
it('reverts on accesses', async function () {
await expectRevertCustomError(this.deque.popBack(), 'Empty()');
await expectRevertCustomError(this.deque.popFront(), 'Empty()');
await expectRevertCustomError(this.deque.back(), 'Empty()');
await expectRevertCustomError(this.deque.front(), 'Empty()');
});
});
describe('when not empty', function () {
beforeEach(async function () {
await this.deque.pushBack(bytesB);
await this.deque.pushFront(bytesA);
await this.deque.pushBack(bytesC);
this.content = [ bytesA, bytesB, bytesC ];
});
it('getters', async function () {
expect(await this.deque.empty()).to.be.equal(false);
expect(await this.deque.length()).to.be.bignumber.equal(this.content.length.toString());
expect(await this.deque.front()).to.be.equal(this.content[0]);
expect(await this.deque.back()).to.be.equal(this.content[this.content.length - 1]);
expect(await getContent(this.deque)).to.have.ordered.members(this.content);
});
it('out of bounds access', async function () {
await expectRevertCustomError(this.deque.at(this.content.length), 'OutOfBounds()');
});
describe('push', function () {
it('front', async function () {
await this.deque.pushFront(bytesD);
this.content.unshift(bytesD); // add element at the beginning
expect(await getContent(this.deque)).to.have.ordered.members(this.content);
});
it('back', async function () {
await this.deque.pushBack(bytesD);
this.content.push(bytesD); // add element at the end
expect(await getContent(this.deque)).to.have.ordered.members(this.content);
});
});
describe('pop', function () {
it('front', async function () {
const value = this.content.shift(); // remove first element
expectEvent(await this.deque.popFront(), 'OperationResult', { value });
expect(await getContent(this.deque)).to.have.ordered.members(this.content);
});
it('back', async function () {
const value = this.content.pop(); // remove last element
expectEvent(await this.deque.popBack(), 'OperationResult', { value });
expect(await getContent(this.deque)).to.have.ordered.members(this.content);
});
});
it('clear', async function () {
await this.deque.clear();
expect(await this.deque.empty()).to.be.equal(true);
expect(await getContent(this.deque)).to.have.ordered.members([]);
});
});
});

View File

@ -0,0 +1,181 @@
const { expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const zip = require('lodash.zip');
function shouldBehaveLikeMap (keys, values, zeroValue) {
const [keyA, keyB, keyC] = keys;
const [valueA, valueB, valueC] = values;
async function expectMembersMatch (map, keys, values) {
expect(keys.length).to.equal(values.length);
await Promise.all(keys.map(async key =>
expect(await map.contains(key)).to.equal(true),
));
expect(await map.length()).to.bignumber.equal(keys.length.toString());
expect(
(await Promise.all(keys.map(key => map.get(key)))).map(k => k.toString()),
).to.have.same.members(
values.map(value => value.toString()),
);
// To compare key-value pairs, we zip keys and values, and convert BNs to
// strings to workaround Chai limitations when dealing with nested arrays
expect(await Promise.all([...Array(keys.length).keys()].map(async (index) => {
const entry = await map.at(index);
return [entry.key.toString(), entry.value.toString()];
}))).to.have.same.deep.members(
zip(keys.map(k => k.toString()), values.map(v => v.toString())),
);
}
it('starts empty', async function () {
expect(await this.map.contains(keyA)).to.equal(false);
await expectMembersMatch(this.map, [], []);
});
describe('set', function () {
it('adds a key', async function () {
const receipt = await this.map.set(keyA, valueA);
expectEvent(receipt, 'OperationResult', { result: true });
await expectMembersMatch(this.map, [keyA], [valueA]);
});
it('adds several keys', async function () {
await this.map.set(keyA, valueA);
await this.map.set(keyB, valueB);
await expectMembersMatch(this.map, [keyA, keyB], [valueA, valueB]);
expect(await this.map.contains(keyC)).to.equal(false);
});
it('returns false when adding keys already in the set', async function () {
await this.map.set(keyA, valueA);
const receipt = (await this.map.set(keyA, valueA));
expectEvent(receipt, 'OperationResult', { result: false });
await expectMembersMatch(this.map, [keyA], [valueA]);
});
it('updates values for keys already in the set', async function () {
await this.map.set(keyA, valueA);
await this.map.set(keyA, valueB);
await expectMembersMatch(this.map, [keyA], [valueB]);
});
});
describe('remove', function () {
it('removes added keys', async function () {
await this.map.set(keyA, valueA);
const receipt = await this.map.remove(keyA);
expectEvent(receipt, 'OperationResult', { result: true });
expect(await this.map.contains(keyA)).to.equal(false);
await expectMembersMatch(this.map, [], []);
});
it('returns false when removing keys not in the set', async function () {
const receipt = await this.map.remove(keyA);
expectEvent(receipt, 'OperationResult', { result: false });
expect(await this.map.contains(keyA)).to.equal(false);
});
it('adds and removes multiple keys', async function () {
// []
await this.map.set(keyA, valueA);
await this.map.set(keyC, valueC);
// [A, C]
await this.map.remove(keyA);
await this.map.remove(keyB);
// [C]
await this.map.set(keyB, valueB);
// [C, B]
await this.map.set(keyA, valueA);
await this.map.remove(keyC);
// [A, B]
await this.map.set(keyA, valueA);
await this.map.set(keyB, valueB);
// [A, B]
await this.map.set(keyC, valueC);
await this.map.remove(keyA);
// [B, C]
await this.map.set(keyA, valueA);
await this.map.remove(keyB);
// [A, C]
await expectMembersMatch(this.map, [keyA, keyC], [valueA, valueC]);
expect(await this.map.contains(keyB)).to.equal(false);
});
});
describe('read', function () {
beforeEach(async function () {
await this.map.set(keyA, valueA);
});
describe('get', function () {
it('existing value', async function () {
expect(
(await this.map.get(keyA)).toString(),
).to.be.equal(valueA.toString());
});
it('missing value', async function () {
await expectRevert(this.map.get(keyB), 'EnumerableMap: nonexistent key');
});
});
describe('get with message', function () {
it('existing value', async function () {
expect(
(await this.map.getWithMessage(keyA, 'custom error string'))
.toString(),
).to.be.equal(valueA.toString());
});
it('missing value', async function () {
await expectRevert(this.map.getWithMessage(keyB, 'custom error string'), 'custom error string');
});
});
describe('tryGet', function () {
it('existing value', async function () {
const result = await this.map.tryGet(keyA);
expect(result['0']).to.be.equal(true);
expect(result['1'].toString()).to.be.equal(valueA.toString());
});
it('missing value', async function () {
const result = await this.map.tryGet(keyB);
expect(result['0']).to.be.equal(false);
expect(result['1'].toString()).to.be.equal(zeroValue.toString());
});
});
});
}
module.exports = {
shouldBehaveLikeMap,
};

View File

@ -1,9 +1,10 @@
const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const { BN, constants } = require('@openzeppelin/test-helpers');
const zip = require('lodash.zip');
const AddressToUintMapMock = artifacts.require('AddressToUintMapMock');
const UintToAddressMapMock = artifacts.require('UintToAddressMapMock');
const Bytes32ToBytes32MapMock = artifacts.require('Bytes32ToBytes32MapMock');
const EnumerableMapMock = artifacts.require('EnumerableMapMock');
const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior');
contract('EnumerableMap', function (accounts) {
const [ accountA, accountB, accountC ] = accounts;
@ -12,170 +13,46 @@ contract('EnumerableMap', function (accounts) {
const keyB = new BN('451');
const keyC = new BN('9592328');
beforeEach(async function () {
this.map = await EnumerableMapMock.new();
});
const bytesA = '0xdeadbeef'.padEnd(66, '0');
const bytesB = '0x0123456789'.padEnd(66, '0');
const bytesC = '0x42424242'.padEnd(66, '0');
async function expectMembersMatch (map, keys, values) {
expect(keys.length).to.equal(values.length);
await Promise.all(keys.map(async key =>
expect(await map.contains(key)).to.equal(true),
));
expect(await map.length()).to.bignumber.equal(keys.length.toString());
expect(await Promise.all(keys.map(key =>
map.get(key),
))).to.have.same.members(values);
// To compare key-value pairs, we zip keys and values, and convert BNs to
// strings to workaround Chai limitations when dealing with nested arrays
expect(await Promise.all([...Array(keys.length).keys()].map(async (index) => {
const entry = await map.at(index);
return [entry.key.toString(), entry.value];
}))).to.have.same.deep.members(
zip(keys.map(k => k.toString()), values),
);
}
it('starts empty', async function () {
expect(await this.map.contains(keyA)).to.equal(false);
await expectMembersMatch(this.map, [], []);
});
describe('set', function () {
it('adds a key', async function () {
const receipt = await this.map.set(keyA, accountA);
expectEvent(receipt, 'OperationResult', { result: true });
await expectMembersMatch(this.map, [keyA], [accountA]);
});
it('adds several keys', async function () {
await this.map.set(keyA, accountA);
await this.map.set(keyB, accountB);
await expectMembersMatch(this.map, [keyA, keyB], [accountA, accountB]);
expect(await this.map.contains(keyC)).to.equal(false);
});
it('returns false when adding keys already in the set', async function () {
await this.map.set(keyA, accountA);
const receipt = (await this.map.set(keyA, accountA));
expectEvent(receipt, 'OperationResult', { result: false });
await expectMembersMatch(this.map, [keyA], [accountA]);
});
it('updates values for keys already in the set', async function () {
await this.map.set(keyA, accountA);
await this.map.set(keyA, accountB);
await expectMembersMatch(this.map, [keyA], [accountB]);
});
});
describe('remove', function () {
it('removes added keys', async function () {
await this.map.set(keyA, accountA);
const receipt = await this.map.remove(keyA);
expectEvent(receipt, 'OperationResult', { result: true });
expect(await this.map.contains(keyA)).to.equal(false);
await expectMembersMatch(this.map, [], []);
});
it('returns false when removing keys not in the set', async function () {
const receipt = await this.map.remove(keyA);
expectEvent(receipt, 'OperationResult', { result: false });
expect(await this.map.contains(keyA)).to.equal(false);
});
it('adds and removes multiple keys', async function () {
// []
await this.map.set(keyA, accountA);
await this.map.set(keyC, accountC);
// [A, C]
await this.map.remove(keyA);
await this.map.remove(keyB);
// [C]
await this.map.set(keyB, accountB);
// [C, B]
await this.map.set(keyA, accountA);
await this.map.remove(keyC);
// [A, B]
await this.map.set(keyA, accountA);
await this.map.set(keyB, accountB);
// [A, B]
await this.map.set(keyC, accountC);
await this.map.remove(keyA);
// [B, C]
await this.map.set(keyA, accountA);
await this.map.remove(keyB);
// [A, C]
await expectMembersMatch(this.map, [keyA, keyC], [accountA, accountC]);
expect(await this.map.contains(keyB)).to.equal(false);
});
});
describe('read', function () {
// AddressToUintMap
describe('AddressToUintMap', function () {
beforeEach(async function () {
await this.map.set(keyA, accountA);
this.map = await AddressToUintMapMock.new();
});
describe('get', function () {
it('existing value', async function () {
expect(await this.map.get(keyA)).to.be.equal(accountA);
});
it('missing value', async function () {
await expectRevert(this.map.get(keyB), 'EnumerableMap: nonexistent key');
});
shouldBehaveLikeMap(
[accountA, accountB, accountC],
[keyA, keyB, keyC],
new BN('0'),
);
});
// UintToAddressMap
describe('UintToAddressMap', function () {
beforeEach(async function () {
this.map = await UintToAddressMapMock.new();
});
describe('get with message', function () {
it('existing value', async function () {
expect(await this.map.getWithMessage(keyA, 'custom error string')).to.be.equal(accountA);
});
it('missing value', async function () {
await expectRevert(this.map.getWithMessage(keyB, 'custom error string'), 'custom error string');
});
shouldBehaveLikeMap(
[keyA, keyB, keyC],
[accountA, accountB, accountC],
constants.ZERO_ADDRESS,
);
});
// Bytes32ToBytes32Map
describe('Bytes32ToBytes32Map', function () {
beforeEach(async function () {
this.map = await Bytes32ToBytes32MapMock.new();
});
describe('tryGet', function () {
it('existing value', async function () {
expect(await this.map.tryGet(keyA)).to.be.deep.equal({
0: true,
1: accountA,
});
});
it('missing value', async function () {
expect(await this.map.tryGet(keyB)).to.be.deep.equal({
0: false,
1: constants.ZERO_ADDRESS,
});
});
});
shouldBehaveLikeMap(
[keyA, keyB, keyC].map(k => ('0x' + k.toString(16)).padEnd(66, '0')),
[bytesA, bytesB, bytesC],
constants.ZERO_BYTES32,
);
});
});