123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- 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
- ? // otherwise if params
- this.governor.castVoteWithReasonAndParams(
- ...concatOpts([proposal.id, vote.support, vote.reason || '', vote.params], opts),
- )
- : vote.reason
- ? // otherwise 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 queueing 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,
- };
|