123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- const { time } = require('@openzeppelin/test-helpers');
- function zip (...args) {
- return Array(Math.max(...args.map(array => array.length)))
- .fill()
- .map((_, i) => args.map(array => array[i]));
- }
- function concatHex (...args) {
- return web3.utils.bytesToHex([].concat(...args.map(h => web3.utils.hexToBytes(h || '0x'))));
- }
- function concatOpts (args, opts = null) {
- return opts ? args.concat(opts) : args;
- }
- class GovernorHelper {
- constructor (governor) {
- this.governor = governor;
- }
- delegate (delegation = {}, opts = null) {
- return Promise.all([
- delegation.token.delegate(delegation.to, { from: delegation.to }),
- delegation.value &&
- delegation.token.transfer(...concatOpts([ delegation.to, delegation.value ]), opts),
- delegation.tokenId &&
- delegation.token.ownerOf(delegation.tokenId).then(owner =>
- delegation.token.transferFrom(...concatOpts([ owner, delegation.to, delegation.tokenId ], opts)),
- ),
- ]);
- }
- propose (opts = null) {
- const proposal = this.currentProposal;
- return this.governor.methods[
- proposal.useCompatibilityInterface
- ? 'propose(address[],uint256[],string[],bytes[],string)'
- : 'propose(address[],uint256[],bytes[],string)'
- ](...concatOpts(proposal.fullProposal, opts));
- }
- queue (opts = null) {
- const proposal = this.currentProposal;
- return proposal.useCompatibilityInterface
- ? this.governor.methods['queue(uint256)'](...concatOpts(
- [ proposal.id ],
- opts,
- ))
- : this.governor.methods['queue(address[],uint256[],bytes[],bytes32)'](...concatOpts(
- proposal.shortProposal,
- opts,
- ));
- }
- execute (opts = null) {
- const proposal = this.currentProposal;
- return proposal.useCompatibilityInterface
- ? this.governor.methods['execute(uint256)'](...concatOpts(
- [ proposal.id ],
- opts,
- ))
- : this.governor.methods['execute(address[],uint256[],bytes[],bytes32)'](...concatOpts(
- proposal.shortProposal,
- opts,
- ));
- }
- cancel (opts = null) {
- const proposal = this.currentProposal;
- return proposal.useCompatibilityInterface
- ? this.governor.methods['cancel(uint256)'](...concatOpts(
- [ proposal.id ],
- opts,
- ))
- : this.governor.methods['cancel(address[],uint256[],bytes[],bytes32)'](...concatOpts(
- proposal.shortProposal,
- opts,
- ));
- }
- vote (vote = {}, opts = null) {
- const proposal = this.currentProposal;
- return vote.signature
- // if signature, and either params or reason →
- ? vote.params || vote.reason
- ? vote.signature({
- proposalId: proposal.id,
- support: vote.support,
- reason: vote.reason || '',
- params: vote.params || '',
- }).then(({ v, r, s }) => this.governor.castVoteWithReasonAndParamsBySig(...concatOpts(
- [ proposal.id, vote.support, vote.reason || '', vote.params || '', v, r, s ],
- opts,
- )))
- : vote.signature({
- proposalId: proposal.id,
- support: vote.support,
- }).then(({ v, r, s }) => this.governor.castVoteBySig(...concatOpts(
- [ proposal.id, vote.support, v, r, s ],
- opts,
- )))
- : vote.params
- // otherwize if params
- ? this.governor.castVoteWithReasonAndParams(...concatOpts(
- [ proposal.id, vote.support, vote.reason || '', vote.params ],
- opts,
- ))
- : vote.reason
- // otherwize if reason
- ? this.governor.castVoteWithReason(...concatOpts(
- [ proposal.id, vote.support, vote.reason ],
- opts,
- ))
- : this.governor.castVote(...concatOpts(
- [ proposal.id, vote.support ],
- opts,
- ));
- }
- waitForSnapshot (offset = 0) {
- const proposal = this.currentProposal;
- return this.governor.proposalSnapshot(proposal.id)
- .then(blockNumber => time.advanceBlockTo(blockNumber.addn(offset)));
- }
- waitForDeadline (offset = 0) {
- const proposal = this.currentProposal;
- return this.governor.proposalDeadline(proposal.id)
- .then(blockNumber => time.advanceBlockTo(blockNumber.addn(offset)));
- }
- waitForEta (offset = 0) {
- const proposal = this.currentProposal;
- return this.governor.proposalEta(proposal.id)
- .then(timestamp => time.increaseTo(timestamp.addn(offset)));
- }
- /**
- * Specify a proposal either as
- * 1) an array of objects [{ target, value, data, signature? }]
- * 2) an object of arrays { targets: [], values: [], data: [], signatures?: [] }
- */
- setProposal (actions, description) {
- let targets, values, signatures, data, useCompatibilityInterface;
- if (Array.isArray(actions)) {
- useCompatibilityInterface = actions.some(a => 'signature' in a);
- targets = actions.map(a => a.target);
- values = actions.map(a => a.value || '0');
- signatures = actions.map(a => a.signature || '');
- data = actions.map(a => a.data || '0x');
- } else {
- useCompatibilityInterface = Array.isArray(actions.signatures);
- ({ targets, values, signatures = [], data } = actions);
- }
- const fulldata = zip(signatures.map(s => s && web3.eth.abi.encodeFunctionSignature(s)), data)
- .map(hexs => concatHex(...hexs));
- const descriptionHash = web3.utils.keccak256(description);
- // condensed version for queing end executing
- const shortProposal = [
- targets,
- values,
- fulldata,
- descriptionHash,
- ];
- // full version for proposing
- const fullProposal = [
- targets,
- values,
- ...(useCompatibilityInterface ? [ signatures ] : []),
- data,
- description,
- ];
- // proposal id
- const id = web3.utils.toBN(web3.utils.keccak256(web3.eth.abi.encodeParameters(
- [ 'address[]', 'uint256[]', 'bytes[]', 'bytes32' ],
- shortProposal,
- )));
- this.currentProposal = {
- id,
- targets,
- values,
- signatures,
- data,
- fulldata,
- description,
- descriptionHash,
- shortProposal,
- fullProposal,
- useCompatibilityInterface,
- };
- return this.currentProposal;
- }
- }
- module.exports = {
- GovernorHelper,
- };
|