Update docs

This commit is contained in:
github-actions
2023-05-09 19:56:30 +00:00
parent 47e38c7bda
commit 6ae39c4dc1
579 changed files with 30453 additions and 21485 deletions

View File

@ -0,0 +1,23 @@
const { clock } = require('../../helpers/time');
function shouldBehaveLikeEIP6372(mode = 'blocknumber') {
describe('should implement EIP6372', function () {
beforeEach(async function () {
this.mock = this.mock ?? this.token ?? this.votes;
});
it('clock is correct', async function () {
expect(await this.mock.clock()).to.be.bignumber.equal(await clock[mode]().then(web3.utils.toBN));
});
it('CLOCK_MODE is correct', async function () {
const params = new URLSearchParams(await this.mock.CLOCK_MODE());
expect(params.get('mode')).to.be.equal(mode);
expect(params.get('from')).to.be.equal(mode == 'blocknumber' ? 'default' : null);
});
});
}
module.exports = {
shouldBehaveLikeEIP6372,
};

View File

@ -6,7 +6,10 @@ const { fromRpcSig } = require('ethereumjs-util');
const ethSigUtil = require('eth-sig-util');
const Wallet = require('ethereumjs-wallet').default;
const { EIP712Domain, domainSeparator } = require('../../helpers/eip712');
const { shouldBehaveLikeEIP6372 } = require('./EIP6372.behavior');
const { getDomain, domainType, domainSeparator } = require('../../helpers/eip712');
const { clockFromReceipt } = require('../../helpers/time');
const Delegation = [
{ name: 'delegatee', type: 'address' },
@ -14,20 +17,16 @@ const Delegation = [
{ name: 'expiry', type: 'uint256' },
];
const version = '1';
function shouldBehaveLikeVotes(mode = 'blocknumber') {
shouldBehaveLikeEIP6372(mode);
function shouldBehaveLikeVotes () {
describe('run votes workflow', function () {
it('initial nonce is 0', async function () {
expect(await this.votes.nonces(this.account1)).to.be.bignumber.equal('0');
});
it('domain separator', async function () {
expect(
await this.votes.DOMAIN_SEPARATOR(),
).to.equal(
await domainSeparator(this.name, version, this.chainId, this.votes.address),
);
expect(await this.votes.DOMAIN_SEPARATOR()).to.equal(domainSeparator(await getDomain(this.votes)));
});
describe('delegation with signature', function () {
@ -35,32 +34,36 @@ function shouldBehaveLikeVotes () {
const delegatorAddress = web3.utils.toChecksumAddress(delegator.getAddressString());
const nonce = 0;
const buildData = (chainId, verifyingContract, name, message) => ({
data: {
const buildAndSignData = async (contract, message, pk) => {
const data = await getDomain(contract).then(domain => ({
primaryType: 'Delegation',
types: { EIP712Domain, Delegation },
domain: { name, version, chainId, verifyingContract },
types: { EIP712Domain: domainType(domain), Delegation },
domain,
message,
},
});
}));
return fromRpcSig(ethSigUtil.signTypedMessage(pk, { data }));
};
beforeEach(async function () {
await this.votes.mint(delegatorAddress, this.NFT0);
await this.votes.$_mint(delegatorAddress, this.NFT0);
});
it('accept signed delegation', async function () {
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
delegator.getPrivateKey(),
buildData(this.chainId, this.votes.address, this.name, {
const { v, r, s } = await buildAndSignData(
this.votes,
{
delegatee: delegatorAddress,
nonce,
expiry: MAX_UINT256,
}),
));
},
delegator.getPrivateKey(),
);
expect(await this.votes.delegates(delegatorAddress)).to.be.equal(ZERO_ADDRESS);
const { receipt } = await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s);
const timepoint = await clockFromReceipt[mode](receipt);
expectEvent(receipt, 'DelegateChanged', {
delegator: delegatorAddress,
fromDelegate: ZERO_ADDRESS,
@ -75,20 +78,21 @@ function shouldBehaveLikeVotes () {
expect(await this.votes.delegates(delegatorAddress)).to.be.equal(delegatorAddress);
expect(await this.votes.getVotes(delegatorAddress)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(delegatorAddress, timepoint - 1)).to.be.bignumber.equal('0');
await time.advanceBlock();
expect(await this.votes.getPastVotes(delegatorAddress, receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(delegatorAddress, timepoint)).to.be.bignumber.equal('1');
});
it('rejects reused signature', async function () {
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
delegator.getPrivateKey(),
buildData(this.chainId, this.votes.address, this.name, {
const { v, r, s } = await buildAndSignData(
this.votes,
{
delegatee: delegatorAddress,
nonce,
expiry: MAX_UINT256,
}),
));
},
delegator.getPrivateKey(),
);
await this.votes.delegateBySig(delegatorAddress, nonce, MAX_UINT256, v, r, s);
@ -99,14 +103,15 @@ function shouldBehaveLikeVotes () {
});
it('rejects bad delegatee', async function () {
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
delegator.getPrivateKey(),
buildData(this.chainId, this.votes.address, this.name, {
const { v, r, s } = await buildAndSignData(
this.votes,
{
delegatee: delegatorAddress,
nonce,
expiry: MAX_UINT256,
}),
));
},
delegator.getPrivateKey(),
);
const receipt = await this.votes.delegateBySig(this.account1Delegatee, nonce, MAX_UINT256, v, r, s);
const { args } = receipt.logs.find(({ event }) => event === 'DelegateChanged');
@ -116,14 +121,16 @@ function shouldBehaveLikeVotes () {
});
it('rejects bad nonce', async function () {
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
delegator.getPrivateKey(),
buildData(this.chainId, this.votes.address, this.name, {
const { v, r, s } = await buildAndSignData(
this.votes,
{
delegatee: delegatorAddress,
nonce,
expiry: MAX_UINT256,
}),
));
},
delegator.getPrivateKey(),
);
await expectRevert(
this.votes.delegateBySig(delegatorAddress, nonce + 1, MAX_UINT256, v, r, s),
'Votes: invalid nonce',
@ -132,14 +139,16 @@ function shouldBehaveLikeVotes () {
it('rejects expired permit', async function () {
const expiry = (await time.latest()) - time.duration.weeks(1);
const { v, r, s } = fromRpcSig(ethSigUtil.signTypedMessage(
delegator.getPrivateKey(),
buildData(this.chainId, this.votes.address, this.name, {
const { v, r, s } = await buildAndSignData(
this.votes,
{
delegatee: delegatorAddress,
nonce,
expiry,
}),
));
},
delegator.getPrivateKey(),
);
await expectRevert(
this.votes.delegateBySig(delegatorAddress, nonce, expiry, v, r, s),
@ -151,10 +160,12 @@ function shouldBehaveLikeVotes () {
describe('set delegation', function () {
describe('call', function () {
it('delegation with tokens', async function () {
await this.votes.mint(this.account1, this.NFT0);
await this.votes.$_mint(this.account1, this.NFT0);
expect(await this.votes.delegates(this.account1)).to.be.equal(ZERO_ADDRESS);
const { receipt } = await this.votes.delegate(this.account1, { from: this.account1 });
const timepoint = await clockFromReceipt[mode](receipt);
expectEvent(receipt, 'DelegateChanged', {
delegator: this.account1,
fromDelegate: ZERO_ADDRESS,
@ -169,9 +180,9 @@ function shouldBehaveLikeVotes () {
expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1);
expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(this.account1, timepoint - 1)).to.be.bignumber.equal('0');
await time.advanceBlock();
expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1, timepoint)).to.be.bignumber.equal('1');
});
it('delegation without tokens', async function () {
@ -192,7 +203,7 @@ function shouldBehaveLikeVotes () {
describe('change delegation', function () {
beforeEach(async function () {
await this.votes.mint(this.account1, this.NFT0);
await this.votes.$_mint(this.account1, this.NFT0);
await this.votes.delegate(this.account1, { from: this.account1 });
});
@ -200,6 +211,8 @@ function shouldBehaveLikeVotes () {
expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1);
const { receipt } = await this.votes.delegate(this.account1Delegatee, { from: this.account1 });
const timepoint = await clockFromReceipt[mode](receipt);
expectEvent(receipt, 'DelegateChanged', {
delegator: this.account1,
fromDelegate: this.account1,
@ -215,16 +228,16 @@ function shouldBehaveLikeVotes () {
previousBalance: '0',
newBalance: '1',
});
const prevBlock = receipt.blockNumber - 1;
expect(await this.votes.delegates(this.account1)).to.be.equal(this.account1Delegatee);
expect(await this.votes.getVotes(this.account1)).to.be.bignumber.equal('0');
expect(await this.votes.getVotes(this.account1Delegatee)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber - 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1Delegatee, prevBlock)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(this.account1, timepoint - 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1Delegatee, timepoint - 1)).to.be.bignumber.equal('0');
await time.advanceBlock();
expect(await this.votes.getPastVotes(this.account1, receipt.blockNumber)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(this.account1Delegatee, receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastVotes(this.account1, timepoint)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(this.account1Delegatee, timepoint)).to.be.bignumber.equal('1');
});
});
@ -234,10 +247,7 @@ function shouldBehaveLikeVotes () {
});
it('reverts if block number >= current block', async function () {
await expectRevert(
this.votes.getPastTotalSupply(5e10),
'block not yet mined',
);
await expectRevert(this.votes.getPastTotalSupply(5e10), 'future lookup');
});
it('returns 0 if there are no checkpoints', async function () {
@ -245,52 +255,60 @@ function shouldBehaveLikeVotes () {
});
it('returns the latest block if >= last checkpoint block', async function () {
const t1 = await this.votes.mint(this.account1, this.NFT0);
const { receipt } = await this.votes.$_mint(this.account1, this.NFT0);
const timepoint = await clockFromReceipt[mode](receipt);
await time.advanceBlock();
await time.advanceBlock();
expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(timepoint - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(timepoint + 1)).to.be.bignumber.equal('1');
});
it('returns zero if < first checkpoint block', async function () {
await time.advanceBlock();
const t2 = await this.votes.mint(this.account1, this.NFT1);
const { receipt } = await this.votes.$_mint(this.account1, this.NFT1);
const timepoint = await clockFromReceipt[mode](receipt);
await time.advanceBlock();
await time.advanceBlock();
expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(timepoint - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(timepoint + 1)).to.be.bignumber.equal('1');
});
it('generally returns the voting balance at the appropriate checkpoint', async function () {
const t1 = await this.votes.mint(this.account1, this.NFT1);
const t1 = await this.votes.$_mint(this.account1, this.NFT1);
await time.advanceBlock();
await time.advanceBlock();
const t2 = await this.votes.burn(this.NFT1);
const t2 = await this.votes.$_burn(this.NFT1);
await time.advanceBlock();
await time.advanceBlock();
const t3 = await this.votes.mint(this.account1, this.NFT2);
const t3 = await this.votes.$_mint(this.account1, this.NFT2);
await time.advanceBlock();
await time.advanceBlock();
const t4 = await this.votes.burn(this.NFT2);
const t4 = await this.votes.$_burn(this.NFT2);
await time.advanceBlock();
await time.advanceBlock();
const t5 = await this.votes.mint(this.account1, this.NFT3);
const t5 = await this.votes.$_mint(this.account1, this.NFT3);
await time.advanceBlock();
await time.advanceBlock();
expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t1.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t2.receipt.blockNumber + 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t3.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t4.receipt.blockNumber + 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t5.receipt.blockNumber + 1)).to.be.bignumber.equal('1');
t1.timepoint = await clockFromReceipt[mode](t1.receipt);
t2.timepoint = await clockFromReceipt[mode](t2.receipt);
t3.timepoint = await clockFromReceipt[mode](t3.receipt);
t4.timepoint = await clockFromReceipt[mode](t4.receipt);
t5.timepoint = await clockFromReceipt[mode](t5.receipt);
expect(await this.votes.getPastTotalSupply(t1.timepoint - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t1.timepoint)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t1.timepoint + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t2.timepoint)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t2.timepoint + 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t3.timepoint)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t3.timepoint + 1)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t4.timepoint)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t4.timepoint + 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastTotalSupply(t5.timepoint)).to.be.bignumber.equal('1');
expect(await this.votes.getPastTotalSupply(t5.timepoint + 1)).to.be.bignumber.equal('1');
});
});
@ -298,18 +316,15 @@ function shouldBehaveLikeVotes () {
// https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js.
describe('Compound test suite', function () {
beforeEach(async function () {
await this.votes.mint(this.account1, this.NFT0);
await this.votes.mint(this.account1, this.NFT1);
await this.votes.mint(this.account1, this.NFT2);
await this.votes.mint(this.account1, this.NFT3);
await this.votes.$_mint(this.account1, this.NFT0);
await this.votes.$_mint(this.account1, this.NFT1);
await this.votes.$_mint(this.account1, this.NFT2);
await this.votes.$_mint(this.account1, this.NFT3);
});
describe('getPastVotes', function () {
it('reverts if block number >= current block', async function () {
await expectRevert(
this.votes.getPastVotes(this.account2, 5e10),
'block not yet mined',
);
await expectRevert(this.votes.getPastVotes(this.account2, 5e10), 'future lookup');
});
it('returns 0 if there are no checkpoints', async function () {
@ -317,22 +332,24 @@ function shouldBehaveLikeVotes () {
});
it('returns the latest block if >= last checkpoint block', async function () {
const t1 = await this.votes.delegate(this.account2, { from: this.account1 });
const { receipt } = await this.votes.delegate(this.account2, { from: this.account1 });
const timepoint = await clockFromReceipt[mode](receipt);
await time.advanceBlock();
await time.advanceBlock();
const latest = await this.votes.getVotes(this.account2);
const nextBlock = t1.receipt.blockNumber + 1;
expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber)).to.be.bignumber.equal(latest);
expect(await this.votes.getPastVotes(this.account2, nextBlock)).to.be.bignumber.equal(latest);
expect(await this.votes.getPastVotes(this.account2, timepoint)).to.be.bignumber.equal(latest);
expect(await this.votes.getPastVotes(this.account2, timepoint + 1)).to.be.bignumber.equal(latest);
});
it('returns zero if < first checkpoint block', async function () {
await time.advanceBlock();
const t1 = await this.votes.delegate(this.account2, { from: this.account1 });
const { receipt } = await this.votes.delegate(this.account2, { from: this.account1 });
const timepoint = await clockFromReceipt[mode](receipt);
await time.advanceBlock();
await time.advanceBlock();
expect(await this.votes.getPastVotes(this.account2, t1.receipt.blockNumber - 1)).to.be.bignumber.equal('0');
expect(await this.votes.getPastVotes(this.account2, timepoint - 1)).to.be.bignumber.equal('0');
});
});
});

View File

@ -2,60 +2,70 @@ const { expectRevert, BN } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const {
shouldBehaveLikeVotes,
} = require('./Votes.behavior');
const { getChainId } = require('../../helpers/chainid');
const { clockFromReceipt } = require('../../helpers/time');
const Votes = artifacts.require('VotesMock');
const { shouldBehaveLikeVotes } = require('./Votes.behavior');
const MODES = {
blocknumber: artifacts.require('$VotesMock'),
timestamp: artifacts.require('$VotesTimestampMock'),
};
contract('Votes', function (accounts) {
const [ account1, account2, account3 ] = accounts;
beforeEach(async function () {
this.name = 'My Vote';
this.votes = await Votes.new(this.name);
});
const [account1, account2, account3] = accounts;
it('starts with zero votes', async function () {
expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('0');
});
for (const [mode, artifact] of Object.entries(MODES)) {
describe(`vote with ${mode}`, function () {
beforeEach(async function () {
this.name = 'My Vote';
this.votes = await artifact.new(this.name, '1');
});
describe('performs voting operations', function () {
beforeEach(async function () {
this.tx1 = await this.votes.mint(account1, 1);
this.tx2 = await this.votes.mint(account2, 1);
this.tx3 = await this.votes.mint(account3, 1);
it('starts with zero votes', async function () {
expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('0');
});
describe('performs voting operations', function () {
beforeEach(async function () {
this.tx1 = await this.votes.$_mint(account1, 1);
this.tx2 = await this.votes.$_mint(account2, 1);
this.tx3 = await this.votes.$_mint(account3, 1);
this.tx1.timepoint = await clockFromReceipt[mode](this.tx1.receipt);
this.tx2.timepoint = await clockFromReceipt[mode](this.tx2.receipt);
this.tx3.timepoint = await clockFromReceipt[mode](this.tx3.receipt);
});
it('reverts if block number >= current block', async function () {
await expectRevert(this.votes.getPastTotalSupply(this.tx3.timepoint + 1), 'Votes: future lookup');
});
it('delegates', async function () {
await this.votes.delegate(account3, account2);
expect(await this.votes.delegates(account3)).to.be.equal(account2);
});
it('returns total amount of votes', async function () {
expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('3');
});
});
describe('performs voting workflow', function () {
beforeEach(async function () {
this.chainId = await getChainId();
this.account1 = account1;
this.account2 = account2;
this.account1Delegatee = account2;
this.NFT0 = new BN('10000000000000000000000000');
this.NFT1 = new BN('10');
this.NFT2 = new BN('20');
this.NFT3 = new BN('30');
});
// includes EIP6372 behavior check
shouldBehaveLikeVotes(mode);
});
});
it('reverts if block number >= current block', async function () {
await expectRevert(
this.votes.getPastTotalSupply(this.tx3.receipt.blockNumber + 1),
'Votes: block not yet mined',
);
});
it('delegates', async function () {
await this.votes.delegate(account3, account2);
expect(await this.votes.delegates(account3)).to.be.equal(account2);
});
it('returns total amount of votes', async function () {
expect(await this.votes.getTotalSupply()).to.be.bignumber.equal('3');
});
});
describe('performs voting workflow', function () {
beforeEach(async function () {
this.chainId = await this.votes.getChainId();
this.account1 = account1;
this.account2 = account2;
this.account1Delegatee = account2;
this.NFT0 = new BN('10000000000000000000000000');
this.NFT1 = new BN('10');
this.NFT2 = new BN('20');
this.NFT3 = new BN('30');
});
shouldBehaveLikeVotes();
});
}
});