Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
O
openzeppelin-contracts-upgradeable
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
俞永鹏
openzeppelin-contracts-upgradeable
Commits
f668ffb1
Commit
f668ffb1
authored
Mar 11, 2022
by
github-actions
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Transpile
d85136b4
parent
4b61a744
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
1804 additions
and
2750 deletions
+1804
-2750
Governor.test.js
test/governance/Governor.test.js
+443
-813
GovernorWorkflow.behavior.js
test/governance/GovernorWorkflow.behavior.js
+0
-186
GovernorCompatibilityBravo.test.js
...vernance/compatibility/GovernorCompatibilityBravo.test.js
+191
-362
GovernorComp.test.js
test/governance/extensions/GovernorComp.test.js
+45
-54
GovernorERC721.test.js
test/governance/extensions/GovernorERC721.test.js
+75
-89
GovernorPreventLateQuorum.test.js
test/governance/extensions/GovernorPreventLateQuorum.test.js
+112
-182
GovernorTimelockCompound.test.js
test/governance/extensions/GovernorTimelockCompound.test.js
+260
-380
GovernorTimelockControl.test.js
test/governance/extensions/GovernorTimelockControl.test.js
+261
-413
GovernorWeightQuorumFraction.test.js
...overnance/extensions/GovernorWeightQuorumFraction.test.js
+79
-71
GovernorWithParams.test.js
test/governance/extensions/GovernorWithParams.test.js
+109
-181
governance.js
test/helpers/governance.js
+211
-0
SupportsInterface.behavior.js
test/utils/introspection/SupportsInterface.behavior.js
+18
-19
No files found.
test/governance/Governor.test.js
View file @
f668ffb1
const
{
BN
,
constants
,
expectEvent
,
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
,
expectEvent
,
expectRevert
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
ethSigUtil
=
require
(
'eth-sig-util'
);
const
Wallet
=
require
(
'ethereumjs-wallet'
).
default
;
const
{
fromRpcSig
}
=
require
(
'ethereumjs-util'
);
const
Enums
=
require
(
'../helpers/enums'
);
const
{
EIP712Domain
}
=
require
(
'../helpers/eip712'
);
const
{
fromRpcSig
}
=
require
(
'ethereumjs-util'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'./GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../helpers/governance'
);
const
{
shouldSupportInterfaces
,
...
...
@@ -19,23 +17,40 @@ const CallReceiver = artifacts.require('CallReceiverMock');
contract
(
'Governor'
,
function
(
accounts
)
{
const
[
owner
,
proposer
,
voter1
,
voter2
,
voter3
,
voter4
]
=
accounts
;
const
empty
=
web3
.
utils
.
toChecksumAddress
(
web3
.
utils
.
randomHex
(
20
));
const
name
=
'OZ-Governor'
;
const
version
=
'1'
;
const
tokenName
=
'MockToken'
;
const
tokenSymbol
=
'MTKN'
;
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
this
.
chainId
=
await
web3
.
eth
.
getChainId
()
;
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
4
,
16
,
10
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
votingDelay
,
votingPeriod
,
10
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
value
,
},
],
'<proposal description>'
);
});
shouldSupportInterfaces
([
...
...
@@ -47,901 +62,516 @@ contract('Governor', function (accounts) {
it
(
'deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
COUNTING_MODE
()).
to
.
be
.
equal
(
'support=bravo&quorum=for,abstain'
);
});
describe
(
'scenario'
,
function
()
{
describe
(
'nominal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
value
=
web3
.
utils
.
toWei
(
'1'
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
:
this
.
value
});
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
this
.
value
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
this
.
value
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'7'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter4
,
weight
:
web3
.
utils
.
toWei
(
'2'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
this
.
votingDelay
=
await
this
.
mock
.
votingDelay
();
this
.
votingPeriod
=
await
this
.
mock
.
votingPeriod
();
});
it
(
'nominal workflow'
,
async
function
()
{
// Before
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
false
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
// Run proposal
const
txPropose
=
await
this
.
helper
.
propose
({
from
:
proposer
});
expectEvent
(
txPropose
,
'ProposalCreated'
,
{
proposalId
:
this
.
proposal
.
id
,
proposer
,
targets
:
this
.
proposal
.
targets
,
// values: this.proposal.values,
signatures
:
this
.
proposal
.
signatures
,
calldatas
:
this
.
proposal
.
data
,
startBlock
:
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
),
endBlock
:
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
),
description
:
this
.
proposal
.
description
,
},
);
await
this
.
helper
.
waitForSnapshot
();
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
from
:
voter1
}),
'VoteCast'
,
{
voter
:
voter1
,
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
}),
'VoteCast'
,
{
voter
:
voter2
,
support
:
Enums
.
VoteType
.
For
,
weight
:
web3
.
utils
.
toWei
(
'7'
),
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
}),
'VoteCast'
,
{
voter
:
voter3
,
support
:
Enums
.
VoteType
.
Against
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
}),
'VoteCast'
,
{
voter
:
voter4
,
support
:
Enums
.
VoteType
.
Abstain
,
weight
:
web3
.
utils
.
toWei
(
'2'
),
},
);
await
this
.
helper
.
waitForDeadline
();
const
txExecute
=
await
this
.
helper
.
execute
();
expectEvent
(
txExecute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
proposal
.
id
},
);
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
,
);
// After
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
(
result
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
});
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
,
proposer
,
targets
:
this
.
settings
.
proposal
[
0
],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures
:
this
.
settings
.
proposal
[
2
].
map
(()
=>
''
),
calldatas
:
this
.
settings
.
proposal
[
2
],
startBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
),
endBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
).
add
(
this
.
votingPeriod
),
description
:
this
.
settings
.
proposal
[
3
],
it
(
'vote with signature'
,
async
function
()
{
const
voterBySig
=
Wallet
.
generate
();
const
voterBySigAddress
=
web3
.
utils
.
toChecksumAddress
(
voterBySig
.
getAddressString
());
const
signature
=
async
(
message
)
=>
{
return
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
data
:
{
types
:
{
EIP712Domain
,
Ballot
:
[
{
name
:
'proposalId'
,
type
:
'uint256'
},
{
name
:
'support'
,
type
:
'uint8'
},
],
},
domain
:
{
name
,
version
,
chainId
:
this
.
chainId
,
verifyingContract
:
this
.
mock
.
address
},
primaryType
:
'Ballot'
,
message
,
},
);
},
));
};
await
this
.
token
.
delegate
(
voterBySigAddress
,
{
from
:
voter1
});
// Run proposal
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
signature
}),
'VoteCast'
,
{
voter
:
voterBySigAddress
,
support
:
Enums
.
VoteType
.
For
},
);
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
// After
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voterBySigAddress
)).
to
.
be
.
equal
(
true
);
});
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
(
vote
=>
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
});
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
it
(
'send ethers'
,
async
function
()
{
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
empty
,
value
,
},
],
'<proposal description>'
);
// Before
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
expect
(
await
web3
.
eth
.
getBalance
(
empty
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
// Run proposal
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
// After
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
empty
)).
to
.
be
.
bignumber
.
equal
(
value
);
});
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
this
.
value
);
describe
(
'should revert'
,
function
()
{
describe
(
'on propose'
,
function
()
{
it
(
'if proposal already exists'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
expectRevert
(
this
.
helper
.
propose
(),
'Governor: proposal already exists'
);
});
runGovernorWorkflow
();
});
describe
(
'vote with signature'
,
function
()
{
beforeEach
(
async
function
()
{
const
chainId
=
await
web3
.
eth
.
getChainId
();
// generate voter by signature wallet
const
voterBySig
=
Wallet
.
generate
();
this
.
voter
=
web3
.
utils
.
toChecksumAddress
(
voterBySig
.
getAddressString
());
// use delegateBySig to enable vote delegation for this wallet
const
{
v
,
r
,
s
}
=
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
data
:
{
types
:
{
EIP712Domain
,
Delegation
:
[
{
name
:
'delegatee'
,
type
:
'address'
},
{
name
:
'nonce'
,
type
:
'uint256'
},
{
name
:
'expiry'
,
type
:
'uint256'
},
],
},
domain
:
{
name
:
tokenName
,
version
:
'1'
,
chainId
,
verifyingContract
:
this
.
token
.
address
},
primaryType
:
'Delegation'
,
message
:
{
delegatee
:
this
.
voter
,
nonce
:
0
,
expiry
:
constants
.
MAX_UINT256
},
},
},
));
await
this
.
token
.
delegateBySig
(
this
.
voter
,
0
,
constants
.
MAX_UINT256
,
v
,
r
,
s
);
// prepare signature for vote by signature
const
signature
=
async
(
message
)
=>
{
return
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
data
:
{
types
:
{
EIP712Domain
,
Ballot
:
[
{
name
:
'proposalId'
,
type
:
'uint256'
},
{
name
:
'support'
,
type
:
'uint8'
},
],
},
domain
:
{
name
,
version
,
chainId
,
verifyingContract
:
this
.
mock
.
address
},
primaryType
:
'Ballot'
,
message
,
},
},
));
};
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
this
.
voter
,
signature
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter1
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter2
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
this
.
voter
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
(
result
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
});
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
describe
(
'on vote'
,
function
()
{
it
(
'if proposal does not exist'
,
async
function
()
{
await
expectRevert
(
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'Governor: unknown proposal id'
,
);
});
runGovernorWorkflow
();
});
describe
(
'send ethers'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
receiver
=
{
address
:
web3
.
utils
.
toChecksumAddress
(
web3
.
utils
.
randomHex
(
20
))
};
this
.
value
=
web3
.
utils
.
toWei
(
'1'
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
:
this
.
value
});
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
this
.
value
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
this
.
value
],
[
'0x'
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
it
(
'if voting has not started'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
expectRevert
(
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'Governor: vote not currently active'
,
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
this
.
value
);
});
runGovernorWorkflow
();
});
describe
(
'receiver revert without reason'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunctionRevertsNoReason
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'Governor: call reverted without message'
},
},
};
it
(
'if support value is invalid'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
expectRevert
(
this
.
helper
.
vote
({
support
:
new
BN
(
'255'
)
}),
'GovernorVotingSimple: invalid value for enum VoteType'
,
);
});
runGovernorWorkflow
();
});
describe
(
'receiver revert with reason'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunctionRevertsReason
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'CallReceiverMock: reverting'
},
},
};
it
(
'if vote was already casted'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
expectRevert
(
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'GovernorVotingSimple: vote already cast'
,
);
});
runGovernorWorkflow
();
});
describe
(
'missing proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
,
error
:
'Governor: unknown proposal id'
,
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Abstain
,
error
:
'Governor: unknown proposal id'
,
},
],
steps
:
{
propose
:
{
enable
:
false
},
wait
:
{
enable
:
false
},
execute
:
{
error
:
'Governor: unknown proposal id'
},
},
};
it
(
'if voting is over'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'Governor: vote not currently active'
,
);
});
runGovernorWorkflow
();
});
describe
(
'duplicate pending proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
describe
(
'on execute'
,
function
()
{
it
(
'if proposal does not exist'
,
async
function
()
{
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: unknown proposal id'
);
});
afterEach
(
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(...
this
.
settings
.
proposal
),
'Governor: proposal already exists'
);
});
runGovernorWorkflow
();
});
describe
(
'duplicate executed proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
it
(
'if quorum is not reached'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter3
});
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
afterEach
(
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(...
this
.
settings
.
proposal
),
'Governor: proposal already exists'
);
});
runGovernorWorkflow
();
});
describe
(
'Invalid vote type'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
new
BN
(
'255'
),
error
:
'GovernorVotingSimple: invalid value for enum VoteType'
,
},
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
it
(
'if score not reached'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter1
});
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
describe
(
'double cast'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
,
},
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
,
error
:
'GovernorVotingSimple: vote already cast'
,
},
],
};
it
(
'if voting is not over'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
describe
(
'quorum not reached'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'4'
),
support
:
Enums
.
VoteType
.
Abstain
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
Against
},
],
steps
:
{
execute
:
{
error
:
'Governor: proposal not successful'
},
it
(
'if receiver revert without reason'
,
async
function
()
{
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunctionRevertsNoReason
().
encodeABI
(),
},
};
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: call reverted without message'
);
});
runGovernorWorkflow
();
});
describe
(
'score not reached'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
Against
},
],
steps
:
{
execute
:
{
error
:
'Governor: proposal not successful'
},
it
(
'if receiver revert with reason'
,
async
function
()
{
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunctionRevertsReason
().
encodeABI
(),
},
};
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'CallReceiverMock: reverting'
);
});
runGovernorWorkflow
();
});
describe
(
'vote not over'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
error
:
'Governor: proposal not successful'
},
},
};
it
(
'if proposal was already executed'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
});
describe
(
'state'
,
function
()
{
describe
(
'Unset'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
propose
:
{
enable
:
false
},
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
await
expectRevert
(
this
.
mock
.
state
(
this
.
id
),
'Governor: unknown proposal id'
);
});
runGovernorWorkflow
();
it
(
'Unset'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
state
(
this
.
proposal
.
id
),
'Governor: unknown proposal id'
);
});
describe
(
'Pending & Active'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
propose
:
{
noadvance
:
true
},
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Pending
);
await
time
.
advanceBlockTo
(
this
.
snapshot
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Pending
);
await
time
.
advanceBlock
();
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
});
runGovernorWorkflow
();
it
(
'Pending & Active'
,
async
function
()
{
await
this
.
helper
.
propose
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Pending
);
await
this
.
helper
.
waitForSnapshot
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Pending
);
await
this
.
helper
.
waitForSnapshot
(
+
1
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
});
describe
(
'Defeated'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Defeated
);
});
runGovernorWorkflow
();
it
(
'Defeated'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForDeadline
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
await
this
.
helper
.
waitForDeadline
(
+
1
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Defeated
);
});
describe
(
'Succeeded'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
});
runGovernorWorkflow
();
it
(
'Succeeded'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
await
this
.
helper
.
waitForDeadline
(
+
1
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
});
describe
(
'Executed'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Executed
);
});
runGovernorWorkflow
();
it
(
'Executed'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Executed
);
});
});
describe
(
'Cancel'
,
function
()
{
describe
(
'Before proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
propose
:
{
enable
:
false
},
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
await
expectRevert
(
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: unknown proposal id'
,
);
});
runGovernorWorkflow
();
describe
(
'cancel'
,
function
()
{
it
(
'before proposal'
,
async
function
()
{
await
expectRevert
(
this
.
helper
.
cancel
(),
'Governor: unknown proposal id'
);
});
describe
(
'After proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
it
(
'after proposal'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
expectRevert
(
this
.
mock
.
castVote
(
this
.
id
,
new
BN
(
'100'
),
{
from
:
voter1
}),
'Governor: vote not currently active'
,
);
});
runGovernorWorkflow
();
await
this
.
helper
.
cancel
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
this
.
helper
.
waitForSnapshot
();
await
expectRevert
(
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'Governor: vote not currently active'
,
);
});
describe
(
'After vote'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
it
(
'after vote'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
});
runGovernorWorkflow
();
await
this
.
helper
.
cancel
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
describe
(
'After deadline'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
it
(
'after deadline'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
});
runGovernorWorkflow
();
await
this
.
helper
.
cancel
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
describe
(
'After execution'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
await
expectRevert
(
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not active'
,
);
});
runGovernorWorkflow
();
it
(
'after execution'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
await
expectRevert
(
this
.
helper
.
cancel
(),
'Governor: proposal not active'
);
});
});
describe
(
'
P
roposal length'
,
function
()
{
describe
(
'
p
roposal length'
,
function
()
{
it
(
'empty'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(
[],
[],
[],
'<proposal description>'
,
),
'Governor: empty proposal'
,
);
this
.
helper
.
setProposal
([
],
'<proposal description>'
);
await
expectRevert
(
this
.
helper
.
propose
(),
'Governor: empty proposal'
);
});
it
(
'missmatch #1'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(
[
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
),
'Governor: invalid proposal length'
,
);
this
.
helper
.
setProposal
({
targets
:
[
],
values
:
[
web3
.
utils
.
toWei
(
'0'
)
],
data
:
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
},
'<proposal description>'
);
await
expectRevert
(
this
.
helper
.
propose
(),
'Governor: invalid proposal length'
);
});
it
(
'missmatch #2'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(
[
this
.
receiver
.
address
],
[
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
),
'Governor: invalid proposal length'
,
);
this
.
helper
.
setProposal
({
targets
:
[
this
.
receiver
.
address
],
values
:
[
],
data
:
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
},
'<proposal description>'
);
await
expectRevert
(
this
.
helper
.
propose
(),
'Governor: invalid proposal length'
);
});
it
(
'missmatch #3'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
propose
(
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
],
'<proposal description>'
,
),
'Governor: invalid proposal length'
,
);
this
.
helper
.
setProposal
({
targets
:
[
this
.
receiver
.
address
],
values
:
[
web3
.
utils
.
toWei
(
'0'
)
],
data
:
[
],
},
'<proposal description>'
);
await
expectRevert
(
this
.
helper
.
propose
(),
'Governor: invalid proposal length'
);
});
});
describe
(
'Settings update'
,
function
()
{
describe
(
'setVotingDelay'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
setVotingDelay
(
'0'
).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'0'
);
describe
(
'onlyGovernance updates'
,
function
()
{
it
(
'setVotingDelay is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setVotingDelay
(
'0'
),
'Governor: onlyGovernance'
);
});
expectEvent
(
this
.
receipts
.
execute
,
'VotingDelaySet'
,
{
oldVotingDelay
:
'4'
,
newVotingDelay
:
'0'
},
);
});
runGovernorWorkflow
();
it
(
'setVotingPeriod is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setVotingPeriod
(
'32'
),
'Governor: onlyGovernance'
);
});
describe
(
'setVotingPeriod'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
setVotingPeriod
(
'32'
).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'32'
);
it
(
'setProposalThreshold is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setProposalThreshold
(
'1000000000000000000'
),
'Governor: onlyGovernance'
);
});
expectEvent
(
this
.
receipts
.
execute
,
'VotingPeriodSet'
,
{
oldVotingPeriod
:
'16'
,
newVotingPeriod
:
'32'
},
);
});
runGovernorWorkflow
();
it
(
'can setVotingDelay through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
setVotingDelay
(
'0'
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
helper
.
execute
(),
'VotingDelaySet'
,
{
oldVotingDelay
:
'4'
,
newVotingDelay
:
'0'
},
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'0'
);
});
describe
(
'setVotingPeriod to 0'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
setVotingPeriod
(
'0'
).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'GovernorSettings: voting period too low'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
});
runGovernorWorkflow
();
it
(
'can setVotingPeriod through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
setVotingPeriod
(
'32'
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
helper
.
execute
(),
'VotingPeriodSet'
,
{
oldVotingPeriod
:
'16'
,
newVotingPeriod
:
'32'
},
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'32'
);
});
describe
(
'setProposalThreshold'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
setProposalThreshold
(
'1000000000000000000'
).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
proposalThreshold
()).
to
.
be
.
bignumber
.
equal
(
'1000000000000000000'
);
it
(
'cannot setVotingPeriod to 0 through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
setVotingPeriod
(
'0'
).
encodeABI
(),
},
],
'<proposal description>'
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalThresholdSet'
,
{
oldProposalThreshold
:
'0'
,
newProposalThreshold
:
'1000000000000000000'
},
);
});
runGovernorWorkflow
();
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'GovernorSettings: voting period too low'
);
});
describe
(
'update protected'
,
function
()
{
it
(
'setVotingDelay'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setVotingDelay
(
'0'
),
'Governor: onlyGovernance'
);
});
it
(
'can setProposalThreshold to 0 through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
setProposalThreshold
(
'1000000000000000000'
).
encodeABI
(),
},
],
'<proposal description>'
);
it
(
'setVotingPeriod'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setVotingPeriod
(
'32'
),
'Governor: onlyGovernance'
);
});
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
it
(
'setProposalThreshold'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setProposalThreshold
(
'1000000000000000000'
),
'Governor: onlyGovernance'
);
});
expectEvent
(
await
this
.
helper
.
execute
(),
'ProposalThresholdSet'
,
{
oldProposalThreshold
:
'0'
,
newProposalThreshold
:
'1000000000000000000'
},
);
expect
(
await
this
.
mock
.
proposalThreshold
()).
to
.
be
.
bignumber
.
equal
(
'1000000000000000000'
);
});
});
});
test/governance/GovernorWorkflow.behavior.js
deleted
100644 → 0
View file @
4b61a744
const
{
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
async
function
getReceiptOrRevert
(
promise
,
error
=
undefined
)
{
if
(
error
)
{
await
expectRevert
(
promise
,
error
);
return
undefined
;
}
else
{
const
{
receipt
}
=
await
promise
;
return
receipt
;
}
}
function
tryGet
(
obj
,
path
=
''
)
{
try
{
return
path
.
split
(
'.'
).
reduce
((
o
,
k
)
=>
o
[
k
],
obj
);
}
catch
(
_
)
{
return
undefined
;
}
}
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
runGovernorWorkflow
()
{
beforeEach
(
async
function
()
{
this
.
receipts
=
{};
// distinguish depending on the proposal length
// - length 4: propose(address[], uint256[], bytes[], string) → GovernorCore
// - length 5: propose(address[], uint256[], string[], bytes[], string) → GovernorCompatibilityBravo
this
.
useCompatibilityInterface
=
this
.
settings
.
proposal
.
length
===
5
;
// compute description hash
this
.
descriptionHash
=
web3
.
utils
.
keccak256
(
this
.
settings
.
proposal
.
slice
(
-
1
).
find
(
Boolean
));
// condensed proposal, used for queue and execute operation
this
.
settings
.
shortProposal
=
[
// targets
this
.
settings
.
proposal
[
0
],
// values
this
.
settings
.
proposal
[
1
],
// calldata (prefix selector if necessary)
this
.
useCompatibilityInterface
?
zip
(
this
.
settings
.
proposal
[
2
].
map
(
selector
=>
selector
&&
web3
.
eth
.
abi
.
encodeFunctionSignature
(
selector
)),
this
.
settings
.
proposal
[
3
],
).
map
(
hexs
=>
concatHex
(...
hexs
))
:
this
.
settings
.
proposal
[
2
],
// descriptionHash
this
.
descriptionHash
,
];
// proposal id
this
.
id
=
await
this
.
mock
.
hashProposal
(...
this
.
settings
.
shortProposal
);
});
it
(
'run'
,
async
function
()
{
// transfer tokens
if
(
tryGet
(
this
.
settings
,
'voters'
))
{
for
(
const
voter
of
this
.
settings
.
voters
)
{
if
(
voter
.
weight
)
{
await
this
.
token
.
transfer
(
voter
.
voter
,
voter
.
weight
,
{
from
:
this
.
settings
.
tokenHolder
});
}
else
if
(
voter
.
nfts
)
{
for
(
const
nft
of
voter
.
nfts
)
{
await
this
.
token
.
transferFrom
(
this
.
settings
.
tokenHolder
,
voter
.
voter
,
nft
,
{
from
:
this
.
settings
.
tokenHolder
});
}
}
}
}
// propose
if
(
this
.
mock
.
propose
&&
tryGet
(
this
.
settings
,
'steps.propose.enable'
)
!==
false
)
{
this
.
receipts
.
propose
=
await
getReceiptOrRevert
(
this
.
mock
.
methods
[
this
.
useCompatibilityInterface
?
'propose(address[],uint256[],string[],bytes[],string)'
:
'propose(address[],uint256[],bytes[],string)'
](
...
this
.
settings
.
proposal
,
{
from
:
this
.
settings
.
proposer
},
),
tryGet
(
this
.
settings
,
'steps.propose.error'
),
);
if
(
tryGet
(
this
.
settings
,
'steps.propose.error'
)
===
undefined
)
{
this
.
deadline
=
await
this
.
mock
.
proposalDeadline
(
this
.
id
);
this
.
snapshot
=
await
this
.
mock
.
proposalSnapshot
(
this
.
id
);
}
if
(
tryGet
(
this
.
settings
,
'steps.propose.delay'
))
{
await
time
.
increase
(
tryGet
(
this
.
settings
,
'steps.propose.delay'
));
}
if
(
tryGet
(
this
.
settings
,
'steps.propose.error'
)
===
undefined
&&
tryGet
(
this
.
settings
,
'steps.propose.noadvance'
)
!==
true
)
{
await
time
.
advanceBlockTo
(
this
.
snapshot
.
addn
(
1
));
}
}
// vote
if
(
tryGet
(
this
.
settings
,
'voters'
))
{
this
.
receipts
.
castVote
=
[];
for
(
const
voter
of
this
.
settings
.
voters
.
filter
(({
support
})
=>
!!
support
))
{
if
(
!
voter
.
signature
)
{
this
.
receipts
.
castVote
.
push
(
await
getReceiptOrRevert
(
voter
.
reason
?
this
.
mock
.
castVoteWithReason
(
this
.
id
,
voter
.
support
,
voter
.
reason
,
{
from
:
voter
.
voter
})
:
this
.
mock
.
castVote
(
this
.
id
,
voter
.
support
,
{
from
:
voter
.
voter
}),
voter
.
error
,
),
);
}
else
{
const
{
v
,
r
,
s
}
=
await
voter
.
signature
({
proposalId
:
this
.
id
,
support
:
voter
.
support
});
this
.
receipts
.
castVote
.
push
(
await
getReceiptOrRevert
(
this
.
mock
.
castVoteBySig
(
this
.
id
,
voter
.
support
,
v
,
r
,
s
),
voter
.
error
,
),
);
}
if
(
tryGet
(
voter
,
'delay'
))
{
await
time
.
increase
(
tryGet
(
voter
,
'delay'
));
}
}
}
// fast forward
if
(
tryGet
(
this
.
settings
,
'steps.wait.enable'
)
!==
false
)
{
await
time
.
advanceBlockTo
(
this
.
deadline
.
addn
(
1
));
}
// queue
if
(
this
.
mock
.
queue
&&
tryGet
(
this
.
settings
,
'steps.queue.enable'
)
!==
false
)
{
this
.
receipts
.
queue
=
await
getReceiptOrRevert
(
this
.
useCompatibilityInterface
?
this
.
mock
.
methods
[
'queue(uint256)'
](
this
.
id
,
{
from
:
this
.
settings
.
queuer
},
)
:
this
.
mock
.
methods
[
'queue(address[],uint256[],bytes[],bytes32)'
](
...
this
.
settings
.
shortProposal
,
{
from
:
this
.
settings
.
queuer
},
),
tryGet
(
this
.
settings
,
'steps.queue.error'
),
);
this
.
eta
=
await
this
.
mock
.
proposalEta
(
this
.
id
);
if
(
tryGet
(
this
.
settings
,
'steps.queue.delay'
))
{
await
time
.
increase
(
tryGet
(
this
.
settings
,
'steps.queue.delay'
));
}
}
// execute
if
(
this
.
mock
.
execute
&&
tryGet
(
this
.
settings
,
'steps.execute.enable'
)
!==
false
)
{
this
.
receipts
.
execute
=
await
getReceiptOrRevert
(
this
.
useCompatibilityInterface
?
this
.
mock
.
methods
[
'execute(uint256)'
](
this
.
id
,
{
from
:
this
.
settings
.
executer
},
)
:
this
.
mock
.
methods
[
'execute(address[],uint256[],bytes[],bytes32)'
](
...
this
.
settings
.
shortProposal
,
{
from
:
this
.
settings
.
executer
},
),
tryGet
(
this
.
settings
,
'steps.execute.error'
),
);
if
(
tryGet
(
this
.
settings
,
'steps.execute.delay'
))
{
await
time
.
increase
(
tryGet
(
this
.
settings
,
'steps.execute.delay'
));
}
}
});
}
module
.
exports
=
{
runGovernorWorkflow
,
};
test/governance/compatibility/GovernorCompatibilityBravo.test.js
View file @
f668ffb1
const
{
BN
,
expectEvent
,
expectRevert
}
=
require
(
'@openzeppelin/test-helpers'
);
const
Enums
=
require
(
'../../helpers/enums
'
);
const
{
expect
}
=
require
(
'chai
'
);
const
RLP
=
require
(
'rlp'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'../GovernorWorkflow.behavior'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
Token
=
artifacts
.
require
(
'ERC20VotesCompMock'
);
const
Timelock
=
artifacts
.
require
(
'CompTimelock'
);
...
...
@@ -23,7 +21,10 @@ contract('GovernorCompatibilityBravo', function (accounts) {
const
tokenName
=
'MockToken'
;
const
tokenSymbol
=
'MTKN'
;
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
proposalThreshold
=
web3
.
utils
.
toWei
(
'10'
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
const
[
deployer
]
=
await
web3
.
eth
.
getAccounts
();
...
...
@@ -35,392 +36,220 @@ contract('GovernorCompatibilityBravo', function (accounts) {
const
predictGovernor
=
makeContractAddress
(
deployer
,
nonce
+
1
);
this
.
timelock
=
await
Timelock
.
new
(
predictGovernor
,
2
*
86400
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
4
,
16
,
proposalThreshold
,
this
.
timelock
.
address
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
votingDelay
,
votingPeriod
,
proposalThreshold
,
this
.
timelock
.
address
,
);
this
.
receiver
=
await
CallReceiver
.
new
();
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
token
.
transfer
(
proposer
,
proposalThreshold
,
{
from
:
owner
});
await
this
.
token
.
delegate
(
proposer
,
{
from
:
proposer
});
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
timelock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
proposer
,
value
:
proposalThreshold
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
signature
:
'mockFunction()'
,
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
quorumVotes
()).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
COUNTING_MODE
()).
to
.
be
.
equal
(
'support=bravo&quorum=bravo'
);
});
describe
(
'nominal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
// targets
[
web3
.
utils
.
toWei
(
'0'
)
],
// values
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
// calldatas
'<proposal description>'
,
// description
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
Abstain
,
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
,
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
,
},
{
voter
:
voter4
,
support
:
'100'
,
error
:
'GovernorCompatibilityBravo: invalid vote type'
,
},
{
voter
:
voter1
,
support
:
Enums
.
VoteType
.
For
,
error
:
'GovernorCompatibilityBravo: vote already cast'
,
skip
:
true
,
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
this
.
votingDelay
=
await
this
.
mock
.
votingDelay
();
this
.
votingPeriod
=
await
this
.
mock
.
votingPeriod
();
this
.
receipts
=
{};
});
afterEach
(
async
function
()
{
const
proposal
=
await
this
.
mock
.
proposals
(
this
.
id
);
expect
(
proposal
.
id
).
to
.
be
.
bignumber
.
equal
(
this
.
id
);
expect
(
proposal
.
proposer
).
to
.
be
.
equal
(
proposer
);
expect
(
proposal
.
eta
).
to
.
be
.
bignumber
.
equal
(
this
.
eta
);
expect
(
proposal
.
startBlock
).
to
.
be
.
bignumber
.
equal
(
this
.
snapshot
);
expect
(
proposal
.
endBlock
).
to
.
be
.
bignumber
.
equal
(
this
.
deadline
);
expect
(
proposal
.
canceled
).
to
.
be
.
equal
(
false
);
expect
(
proposal
.
executed
).
to
.
be
.
equal
(
true
);
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
proposal
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
const
action
=
await
this
.
mock
.
getActions
(
this
.
id
);
expect
(
action
.
targets
).
to
.
be
.
deep
.
equal
(
this
.
settings
.
proposal
[
0
]);
// expect(action.values).to.be.deep.equal(this.settings.proposal[1]);
expect
(
action
.
signatures
).
to
.
be
.
deep
.
equal
(
Array
(
this
.
settings
.
proposal
[
2
].
length
).
fill
(
''
));
expect
(
action
.
calldatas
).
to
.
be
.
deep
.
equal
(
this
.
settings
.
proposal
[
2
]);
for
(
const
voter
of
this
.
settings
.
voters
.
filter
(({
skip
})
=>
!
skip
))
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter
.
voter
)).
to
.
be
.
equal
(
voter
.
error
===
undefined
);
const
receipt
=
await
this
.
mock
.
getReceipt
(
this
.
id
,
voter
.
voter
);
expect
(
receipt
.
hasVoted
).
to
.
be
.
equal
(
voter
.
error
===
undefined
);
expect
(
receipt
.
support
).
to
.
be
.
bignumber
.
equal
(
voter
.
error
===
undefined
?
voter
.
support
:
'0'
);
expect
(
receipt
.
votes
).
to
.
be
.
bignumber
.
equal
(
voter
.
error
===
undefined
?
voter
.
weight
:
'0'
);
}
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
,
proposer
,
targets
:
this
.
settings
.
proposal
[
0
],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures
:
this
.
settings
.
proposal
[
2
].
map
(()
=>
''
),
calldatas
:
this
.
settings
.
proposal
[
2
],
startBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
),
endBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
).
add
(
this
.
votingPeriod
),
description
:
this
.
settings
.
proposal
[
3
],
},
);
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
(
vote
=>
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
});
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
});
runGovernorWorkflow
();
});
describe
(
'with function selector and arguments'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
Array
(
4
).
fill
(
this
.
receiver
.
address
),
Array
(
4
).
fill
(
web3
.
utils
.
toWei
(
'0'
)),
[
''
,
''
,
'mockFunctionNonPayable()'
,
'mockFunctionWithArgs(uint256,uint256)'
,
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
this
.
receiver
.
contract
.
methods
.
mockFunctionWithArgs
(
17
,
42
).
encodeABI
(),
'0x'
,
web3
.
eth
.
abi
.
encodeParameters
([
'uint256'
,
'uint256'
],
[
18
,
43
]),
],
'<proposal description>'
,
// description
],
it
(
'nominal workflow'
,
async
function
()
{
// Before
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
false
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
timelock
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
// Run proposal
const
txPropose
=
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
// After
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
timelock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
const
proposal
=
await
this
.
mock
.
proposals
(
this
.
proposal
.
id
);
expect
(
proposal
.
id
).
to
.
be
.
bignumber
.
equal
(
this
.
proposal
.
id
);
expect
(
proposal
.
proposer
).
to
.
be
.
equal
(
proposer
);
expect
(
proposal
.
eta
).
to
.
be
.
bignumber
.
equal
(
await
this
.
mock
.
proposalEta
(
this
.
proposal
.
id
));
expect
(
proposal
.
startBlock
).
to
.
be
.
bignumber
.
equal
(
await
this
.
mock
.
proposalSnapshot
(
this
.
proposal
.
id
));
expect
(
proposal
.
endBlock
).
to
.
be
.
bignumber
.
equal
(
await
this
.
mock
.
proposalDeadline
(
this
.
proposal
.
id
));
expect
(
proposal
.
canceled
).
to
.
be
.
equal
(
false
);
expect
(
proposal
.
executed
).
to
.
be
.
equal
(
true
);
const
action
=
await
this
.
mock
.
getActions
(
this
.
proposal
.
id
);
expect
(
action
.
targets
).
to
.
be
.
deep
.
equal
(
this
.
proposal
.
targets
);
// expect(action.values).to.be.deep.equal(this.proposal.values);
expect
(
action
.
signatures
).
to
.
be
.
deep
.
equal
(
this
.
proposal
.
signatures
);
expect
(
action
.
calldatas
).
to
.
be
.
deep
.
equal
(
this
.
proposal
.
data
);
const
voteReceipt1
=
await
this
.
mock
.
getReceipt
(
this
.
proposal
.
id
,
voter1
);
expect
(
voteReceipt1
.
hasVoted
).
to
.
be
.
equal
(
true
);
expect
(
voteReceipt1
.
support
).
to
.
be
.
bignumber
.
equal
(
Enums
.
VoteType
.
For
);
expect
(
voteReceipt1
.
votes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'10'
));
const
voteReceipt2
=
await
this
.
mock
.
getReceipt
(
this
.
proposal
.
id
,
voter2
);
expect
(
voteReceipt2
.
hasVoted
).
to
.
be
.
equal
(
true
);
expect
(
voteReceipt2
.
support
).
to
.
be
.
bignumber
.
equal
(
Enums
.
VoteType
.
For
);
expect
(
voteReceipt2
.
votes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'7'
));
const
voteReceipt3
=
await
this
.
mock
.
getReceipt
(
this
.
proposal
.
id
,
voter3
);
expect
(
voteReceipt3
.
hasVoted
).
to
.
be
.
equal
(
true
);
expect
(
voteReceipt3
.
support
).
to
.
be
.
bignumber
.
equal
(
Enums
.
VoteType
.
Against
);
expect
(
voteReceipt3
.
votes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'5'
));
const
voteReceipt4
=
await
this
.
mock
.
getReceipt
(
this
.
proposal
.
id
,
voter4
);
expect
(
voteReceipt4
.
hasVoted
).
to
.
be
.
equal
(
true
);
expect
(
voteReceipt4
.
support
).
to
.
be
.
bignumber
.
equal
(
Enums
.
VoteType
.
Abstain
);
expect
(
voteReceipt4
.
votes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'2'
));
expectEvent
(
txPropose
,
'ProposalCreated'
,
{
proposalId
:
this
.
proposal
.
id
,
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
,
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
});
runGovernorWorkflow
();
afterEach
(
async
function
()
{
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalledWithArgs'
,
{
a
:
'17'
,
b
:
'42'
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalledWithArgs'
,
{
a
:
'18'
,
b
:
'43'
},
);
});
targets
:
this
.
proposal
.
targets
,
// values: this.proposal.values,
signatures
:
this
.
proposal
.
signatures
.
map
(()
=>
''
),
// this event doesn't contain the proposal detail
calldatas
:
this
.
proposal
.
fulldata
,
startBlock
:
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
),
endBlock
:
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
),
description
:
this
.
proposal
.
description
,
},
);
expectEvent
(
txExecute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
proposal
.
id
},
);
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
,
);
});
describe
(
'proposalThreshold not reached'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
// targets
[
web3
.
utils
.
toWei
(
'0'
)
],
// values
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
// calldatas
'<proposal description>'
,
// description
],
proposer
:
other
,
steps
:
{
propose
:
{
error
:
'GovernorCompatibilityBravo: proposer votes below proposal threshold'
},
wait
:
{
enable
:
false
},
queue
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
runGovernorWorkflow
();
it
(
'with function selector and arguments'
,
async
function
()
{
const
target
=
this
.
receiver
.
address
;
this
.
helper
.
setProposal
([
{
target
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
},
{
target
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunctionWithArgs
(
17
,
42
).
encodeABI
()
},
{
target
,
signature
:
'mockFunctionNonPayable()'
},
{
target
,
signature
:
'mockFunctionWithArgs(uint256,uint256)'
,
data
:
web3
.
eth
.
abi
.
encodeParameters
([
'uint256'
,
'uint256'
],
[
18
,
43
]),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
,
);
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
,
);
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalledWithArgs'
,
{
a
:
'17'
,
b
:
'42'
},
);
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalledWithArgs'
,
{
a
:
'18'
,
b
:
'43'
},
);
});
describe
(
'cancel'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
// targets
[
web3
.
utils
.
toWei
(
'0'
)
],
// values
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
// calldatas
'<proposal description>'
,
// description
],
proposer
,
tokenHolder
:
owner
,
steps
:
{
wait
:
{
enable
:
false
},
queue
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
describe
(
'by proposer'
,
function
()
{
afterEach
(
async
function
()
{
await
this
.
mock
.
cancel
(
this
.
id
,
{
from
:
proposer
});
});
runGovernorWorkflow
();
});
describe
(
'if proposer below threshold'
,
function
()
{
afterEach
(
async
function
()
{
await
this
.
token
.
transfer
(
voter1
,
web3
.
utils
.
toWei
(
'1'
),
{
from
:
proposer
});
await
this
.
mock
.
cancel
(
this
.
id
);
describe
(
'should revert'
,
function
()
{
describe
(
'on propose'
,
function
()
{
it
(
'if proposal doesnt meet proposalThreshold'
,
async
function
()
{
await
expectRevert
(
this
.
helper
.
propose
({
from
:
other
}),
'GovernorCompatibilityBravo: proposer votes below proposal threshold'
,
);
});
runGovernorWorkflow
();
});
describe
(
'not if proposer above threshold'
,
function
()
{
afterEach
(
async
function
()
{
describe
(
'on vote'
,
function
()
{
it
(
'if vote type is invalide'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
await
expectRevert
(
this
.
mock
.
cancel
(
this
.
id
),
'Governor
Bravo: proposer above threshold
'
,
this
.
helper
.
vote
({
support
:
5
},
{
from
:
voter1
}
),
'Governor
CompatibilityBravo: invalid vote type
'
,
);
});
runGovernorWorkflow
();
});
});
describe
(
'with compatibility interface'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
// targets
[
web3
.
utils
.
toWei
(
'0'
)
],
// values
[
'mockFunction()'
],
// signatures
[
'0x'
],
// calldatas
'<proposal description>'
,
// description
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
Abstain
,
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
,
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
,
},
{
voter
:
voter4
,
support
:
'100'
,
error
:
'GovernorCompatibilityBravo: invalid vote type'
,
},
{
voter
:
voter1
,
support
:
Enums
.
VoteType
.
For
,
error
:
'GovernorCompatibilityBravo: vote already cast'
,
skip
:
true
,
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
this
.
votingDelay
=
await
this
.
mock
.
votingDelay
();
this
.
votingPeriod
=
await
this
.
mock
.
votingPeriod
();
this
.
receipts
=
{};
describe
(
'cancel'
,
function
()
{
it
(
'proposer can cancel'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
cancel
({
from
:
proposer
});
});
afterEach
(
async
function
()
{
const
proposal
=
await
this
.
mock
.
proposals
(
this
.
id
);
expect
(
proposal
.
id
).
to
.
be
.
bignumber
.
equal
(
this
.
id
);
expect
(
proposal
.
proposer
).
to
.
be
.
equal
(
proposer
);
expect
(
proposal
.
eta
).
to
.
be
.
bignumber
.
equal
(
this
.
eta
);
expect
(
proposal
.
startBlock
).
to
.
be
.
bignumber
.
equal
(
this
.
snapshot
);
expect
(
proposal
.
endBlock
).
to
.
be
.
bignumber
.
equal
(
this
.
deadline
);
expect
(
proposal
.
canceled
).
to
.
be
.
equal
(
false
);
expect
(
proposal
.
executed
).
to
.
be
.
equal
(
true
);
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
proposal
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
const
action
=
await
this
.
mock
.
getActions
(
this
.
id
);
expect
(
action
.
targets
).
to
.
be
.
deep
.
equal
(
this
.
settings
.
proposal
[
0
]);
// expect(action.values).to.be.deep.equal(this.settings.proposal[1]);
expect
(
action
.
signatures
).
to
.
be
.
deep
.
equal
(
this
.
settings
.
proposal
[
2
]);
expect
(
action
.
calldatas
).
to
.
be
.
deep
.
equal
(
this
.
settings
.
proposal
[
3
]);
for
(
const
voter
of
this
.
settings
.
voters
.
filter
(({
skip
})
=>
!
skip
))
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter
.
voter
)).
to
.
be
.
equal
(
voter
.
error
===
undefined
);
const
receipt
=
await
this
.
mock
.
getReceipt
(
this
.
id
,
voter
.
voter
);
expect
(
receipt
.
hasVoted
).
to
.
be
.
equal
(
voter
.
error
===
undefined
);
expect
(
receipt
.
support
).
to
.
be
.
bignumber
.
equal
(
voter
.
error
===
undefined
?
voter
.
support
:
'0'
);
expect
(
receipt
.
votes
).
to
.
be
.
bignumber
.
equal
(
voter
.
error
===
undefined
?
voter
.
weight
:
'0'
);
}
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
,
proposer
,
targets
:
this
.
settings
.
proposal
[
0
],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures
:
this
.
settings
.
proposal
[
2
].
map
(
_
=>
''
),
calldatas
:
this
.
settings
.
shortProposal
[
2
],
startBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
),
endBlock
:
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
this
.
votingDelay
).
add
(
this
.
votingPeriod
),
description
:
this
.
settings
.
proposal
[
4
],
},
);
it
(
'anyone can cancel if proposer drop below threshold'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
token
.
transfer
(
voter1
,
web3
.
utils
.
toWei
(
'1'
),
{
from
:
proposer
});
await
this
.
helper
.
cancel
();
});
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
(
vote
=>
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
});
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
it
(
'cannot cancel is proposer is still above threshold'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
expectRevert
(
this
.
helper
.
cancel
(),
'GovernorBravo: proposer above threshold'
);
});
runGovernorWorkflow
();
});
});
test/governance/extensions/GovernorComp.test.js
View file @
f668ffb1
const
{
BN
,
expectEvent
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'./../GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
Token
=
artifacts
.
require
(
'ERC20VotesCompMock'
);
const
Governor
=
artifacts
.
require
(
'GovernorCompMock'
);
...
...
@@ -17,71 +15,64 @@ contract('GovernorComp', function (accounts) {
const
tokenName
=
'MockToken'
;
const
tokenSymbol
=
'MTKN'
;
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
});
describe
(
'voting with comp token'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'10'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter4
,
weight
:
web3
.
utils
.
toWei
(
'2'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter3
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter4
)).
to
.
be
.
equal
(
true
);
it
(
'voting with comp token'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter3
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter4
)).
to
.
be
.
equal
(
true
);
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
(
vote
=>
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
});
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
(
result
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
});
await
this
.
mock
.
proposalVotes
(
this
.
proposal
.
id
).
then
(
results
=>
{
expect
(
results
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'17'
));
expect
(
results
.
againstVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'5'
));
expect
(
results
.
abstainVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'2'
));
});
runGovernorWorkflow
();
});
});
test/governance/extensions/GovernorERC721.test.js
View file @
f668ffb1
const
{
expectEvent
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
}
=
require
(
'bn.js
'
);
const
{
BN
,
expectEvent
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai
'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'./../GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
Token
=
artifacts
.
require
(
'ERC721VotesMock'
);
const
Governor
=
artifacts
.
require
(
'GovernorVoteMocks'
);
...
...
@@ -14,105 +11,94 @@ contract('GovernorERC721Mock', function (accounts) {
const
[
owner
,
voter1
,
voter2
,
voter3
,
voter4
]
=
accounts
;
const
name
=
'OZ-Governor'
;
// const version = '1';
const
tokenName
=
'MockNFToken'
;
const
tokenSymbol
=
'MTKN'
;
const
NFT0
=
web3
.
utils
.
toWei
(
'100'
);
const
NFT1
=
web3
.
utils
.
toWei
(
'10'
);
const
NFT2
=
web3
.
utils
.
toWei
(
'20'
);
const
NFT3
=
web3
.
utils
.
toWei
(
'30'
);
const
NFT4
=
web3
.
utils
.
toWei
(
'40'
);
// Must be the same as in contract
const
ProposalState
=
{
Pending
:
new
BN
(
'0'
),
Active
:
new
BN
(
'1'
),
Canceled
:
new
BN
(
'2'
),
Defeated
:
new
BN
(
'3'
),
Succeeded
:
new
BN
(
'4'
),
Queued
:
new
BN
(
'5'
),
Expired
:
new
BN
(
'6'
),
Executed
:
new
BN
(
'7'
),
};
const
NFT0
=
new
BN
(
0
);
const
NFT1
=
new
BN
(
1
);
const
NFT2
=
new
BN
(
2
);
const
NFT3
=
new
BN
(
3
);
const
NFT4
=
new
BN
(
4
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
);
this
.
receiver
=
await
CallReceiver
.
new
();
await
this
.
token
.
mint
(
owner
,
NFT0
);
await
this
.
token
.
mint
(
owner
,
NFT1
);
await
this
.
token
.
mint
(
owner
,
NFT2
);
await
this
.
token
.
mint
(
owner
,
NFT3
);
await
this
.
token
.
mint
(
owner
,
NFT4
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
Promise
.
all
([
NFT0
,
NFT1
,
NFT2
,
NFT3
,
NFT4
].
map
(
tokenId
=>
this
.
token
.
mint
(
owner
,
tokenId
)));
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
tokenId
:
NFT0
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
tokenId
:
NFT1
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
tokenId
:
NFT2
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
tokenId
:
NFT3
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
tokenId
:
NFT4
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
});
describe
(
'voting with ERC721 token'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
nfts
:
[
NFT0
],
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter2
,
nfts
:
[
NFT1
,
NFT2
],
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter3
,
nfts
:
[
NFT3
],
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter4
,
nfts
:
[
NFT4
],
support
:
Enums
.
VoteType
.
Abstain
},
],
};
it
(
'voting with ERC721 token'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
}),
'VoteCast'
,
{
voter
:
voter1
,
support
:
Enums
.
VoteType
.
For
,
weight
:
'1'
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
}),
'VoteCast'
,
{
voter
:
voter2
,
support
:
Enums
.
VoteType
.
For
,
weight
:
'2'
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
}),
'VoteCast'
,
{
voter
:
voter3
,
support
:
Enums
.
VoteType
.
Against
,
weight
:
'1'
},
);
expectEvent
(
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
}),
'VoteCast'
,
{
voter
:
voter4
,
support
:
Enums
.
VoteType
.
Abstain
,
weight
:
'1'
},
);
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter3
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter4
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
proposal
.
id
).
then
(
results
=>
{
expect
(
results
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
'3'
);
expect
(
results
.
againstVotes
).
to
.
be
.
bignumber
.
equal
(
'1'
);
expect
(
results
.
abstainVotes
).
to
.
be
.
bignumber
.
equal
(
'1'
);
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
for
(
const
vote
of
this
.
receipts
.
castVote
.
filter
(
Boolean
))
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter
)).
to
.
be
.
equal
(
true
);
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
if
(
voter
===
voter2
)
{
expect
(
await
this
.
token
.
getVotes
(
voter
,
vote
.
blockNumber
)).
to
.
be
.
bignumber
.
equal
(
'2'
);
}
else
{
expect
(
await
this
.
token
.
getVotes
(
voter
,
vote
.
blockNumber
)).
to
.
be
.
bignumber
.
equal
(
'1'
);
}
}
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
(
result
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
nfts
})
=>
acc
.
add
(
new
BN
(
nfts
.
length
)),
new
BN
(
'0'
),
),
);
}
});
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
ProposalState
.
Executed
);
});
runGovernorWorkflow
();
});
});
test/governance/extensions/GovernorPreventLateQuorum.test.js
View file @
f668ffb1
const
{
BN
,
expectEvent
,
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
,
expectEvent
,
expectRevert
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'../GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
Token
=
artifacts
.
require
(
'ERC20VotesCompMock'
);
const
Governor
=
artifacts
.
require
(
'GovernorPreventLateQuorumMock'
);
...
...
@@ -21,6 +19,7 @@ contract('GovernorPreventLateQuorum', function (accounts) {
const
votingPeriod
=
new
BN
(
16
);
const
lateQuorumVoteExtension
=
new
BN
(
8
);
const
quorum
=
web3
.
utils
.
toWei
(
'1'
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
...
...
@@ -34,11 +33,25 @@ contract('GovernorPreventLateQuorum', function (accounts) {
lateQuorumVoteExtension
,
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
...
...
@@ -50,198 +63,115 @@ contract('GovernorPreventLateQuorum', function (accounts) {
expect
(
await
this
.
mock
.
lateQuorumVoteExtension
()).
to
.
be
.
bignumber
.
equal
(
lateQuorumVoteExtension
);
});
describe
(
'nominal is unaffected'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'7'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter4
,
weight
:
web3
.
utils
.
toWei
(
'2'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
it
(
'nominal workflow unaffected'
,
async
function
()
{
const
txPropose
=
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter3
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter4
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
proposal
.
id
).
then
(
results
=>
{
expect
(
results
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'17'
));
expect
(
results
.
againstVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'5'
));
expect
(
results
.
abstainVotes
).
to
.
be
.
bignumber
.
equal
(
web3
.
utils
.
toWei
(
'2'
));
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
(
result
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
).
filter
(({
support
})
=>
support
===
value
).
reduce
(
(
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
),
),
);
}
});
const
startBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
);
const
endBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
endBlock
);
const
startBlock
=
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
);
const
endBlock
=
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
endBlock
);
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
,
proposer
,
targets
:
this
.
settings
.
proposal
[
0
],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures
:
this
.
settings
.
proposal
[
2
].
map
(()
=>
''
),
calldatas
:
this
.
settings
.
proposal
[
2
],
startBlock
,
endBlock
,
description
:
this
.
settings
.
proposal
[
3
],
},
);
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
(
vote
=>
{
const
{
voter
}
=
vote
.
logs
.
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
expectEvent
.
notEmitted
(
vote
,
'ProposalExtended'
,
);
});
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
});
runGovernorWorkflow
();
});
describe
(
'Delay is extended to prevent last minute take-over'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
expectEvent
(
txPropose
,
'ProposalCreated'
,
{
proposalId
:
this
.
proposal
.
id
,
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'0.2'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'1.0'
)
},
// do not actually vote, only getting tokens
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'0.9'
)
},
// do not actually vote, only getting tokens
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
targets
:
this
.
proposal
.
targets
,
// values: this.proposal.values.map(value => new BN(value)),
signatures
:
this
.
proposal
.
signatures
,
calldatas
:
this
.
proposal
.
data
,
startBlock
,
endBlock
,
description
:
this
.
proposal
.
description
,
},
);
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
it
(
'Delay is extended to prevent last minute take-over'
,
async
function
()
{
const
txPropose
=
await
this
.
helper
.
propose
({
from
:
proposer
}
);
const
startBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
);
const
endBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
endBlock
);
// compute original schedule
const
startBlock
=
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
);
const
endBlock
=
new
BN
(
txPropose
.
receipt
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
endBlock
);
// wait until the vote is almost over
await
time
.
advanceBlockTo
(
endBlock
.
subn
(
1
)
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
// wait for the last minute to vote
await
this
.
helper
.
waitForDeadline
(
-
1
);
const
txVote
=
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
}
);
// try to overtake the vote at the last minute
const
tx
=
await
this
.
mock
.
castVote
(
this
.
id
,
Enums
.
VoteType
.
For
,
{
from
:
voter2
}
);
// cannot execute yet
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
// vote duration is extended
const
extendedBlock
=
new
BN
(
tx
.
receipt
.
blockNumber
).
add
(
lateQuorumVoteExtension
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
extendedBlock
);
// compute new extended schedule
const
extendedDeadline
=
new
BN
(
txVote
.
receipt
.
blockNumber
).
add
(
lateQuorumVoteExtension
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
extendedDeadline
);
expectEvent
(
tx
,
'ProposalExtended'
,
{
proposalId
:
this
.
id
,
extendedDeadline
:
extendedBlock
},
);
// still possible to vote
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter1
});
// vote is still active after expected end
await
time
.
advanceBlockTo
(
endBlock
.
addn
(
1
));
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
await
this
.
helper
.
waitForDeadline
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
await
this
.
helper
.
waitForDeadline
(
+
1
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Defeated
);
// Still possible to vote
await
this
.
mock
.
castVote
(
this
.
id
,
Enums
.
VoteType
.
Against
,
{
from
:
voter3
});
// proposal fails
await
time
.
advanceBlockTo
(
extendedBlock
.
addn
(
1
));
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Defeated
);
});
runGovernorWorkflow
();
// check extension event
expectEvent
(
txVote
,
'ProposalExtended'
,
{
proposalId
:
this
.
proposal
.
id
,
extendedDeadline
},
);
});
describe
(
'setLateQuorumVoteExtension'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
newVoteExtension
=
new
BN
(
0
);
// disable voting delay extension
});
it
(
'protected'
,
async
function
()
{
describe
(
'onlyGovernance updates'
,
function
()
{
it
(
'setLateQuorumVoteExtension is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
setLateQuorumVoteExtension
(
this
.
newVoteExtension
),
this
.
mock
.
setLateQuorumVoteExtension
(
0
),
'Governor: onlyGovernance'
,
);
});
describe
(
'using workflow'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
setLateQuorumVoteExtension
(
this
.
newVoteExtension
).
encodeABI
()
],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1.0'
),
support
:
Enums
.
VoteType
.
For
},
],
};
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'LateQuorumVoteExtensionSet'
,
{
oldVoteExtension
:
lateQuorumVoteExtension
,
newVoteExtension
:
this
.
newVoteExtension
},
);
expect
(
await
this
.
mock
.
lateQuorumVoteExtension
()).
to
.
be
.
bignumber
.
equal
(
this
.
newVoteExtension
);
});
runGovernorWorkflow
();
it
(
'can setLateQuorumVoteExtension through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
setLateQuorumVoteExtension
(
'0'
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
helper
.
execute
(),
'LateQuorumVoteExtensionSet'
,
{
oldVoteExtension
:
lateQuorumVoteExtension
,
newVoteExtension
:
'0'
},
);
expect
(
await
this
.
mock
.
lateQuorumVoteExtension
()).
to
.
be
.
bignumber
.
equal
(
'0'
);
});
});
});
test/governance/extensions/GovernorTimelockCompound.test.js
View file @
f668ffb1
const
{
constants
,
expectEvent
,
expectRevert
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
,
constants
,
expectEvent
,
expectRevert
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
RLP
=
require
(
'rlp'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'../GovernorWorkflow.behavior'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
{
shouldSupportInterfaces
,
...
...
@@ -21,13 +18,16 @@ function makeContractAddress (creator, nonce) {
}
contract
(
'GovernorTimelockCompound'
,
function
(
accounts
)
{
const
[
admin
,
voter
,
other
]
=
accounts
;
const
[
owner
,
voter1
,
voter2
,
voter3
,
voter4
,
other
]
=
accounts
;
const
name
=
'OZ-Governor'
;
// const version = '1';
const
tokenName
=
'MockToken'
;
const
tokenSymbol
=
'MTKN'
;
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
const
[
deployer
]
=
await
web3
.
eth
.
getAccounts
();
...
...
@@ -39,10 +39,34 @@ contract('GovernorTimelockCompound', function (accounts) {
const
predictGovernor
=
makeContractAddress
(
deployer
,
nonce
+
1
);
this
.
timelock
=
await
Timelock
.
new
(
predictGovernor
,
2
*
86400
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
4
,
16
,
this
.
timelock
.
address
,
0
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
votingDelay
,
votingPeriod
,
this
.
timelock
.
address
,
0
,
);
this
.
receiver
=
await
CallReceiver
.
new
();
await
this
.
token
.
mint
(
voter
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter
,
{
from
:
voter
});
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
timelock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
shouldSupportInterfaces
([
...
...
@@ -53,436 +77,292 @@ contract('GovernorTimelockCompound', function (accounts) {
]);
it
(
'doesn
\'
t accept ether transfers'
,
async
function
()
{
await
expectRevert
.
unspecified
(
web3
.
eth
.
sendTransaction
({
from
:
vot
er
,
to
:
this
.
mock
.
address
,
value
:
1
}));
await
expectRevert
.
unspecified
(
web3
.
eth
.
sendTransaction
({
from
:
own
er
,
to
:
this
.
mock
.
address
,
value
:
1
}));
});
it
(
'post deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
timelock
()).
to
.
be
.
equal
(
this
.
timelock
.
address
);
expect
(
await
this
.
timelock
.
admin
()).
to
.
be
.
equal
(
this
.
mock
.
address
);
});
describe
(
'nominal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
queue
,
'ProposalQueued'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
queue
.
transactionHash
,
this
.
timelock
,
'QueueTransaction'
,
{
eta
:
this
.
eta
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
timelock
,
'ExecuteTransaction'
,
{
eta
:
this
.
eta
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
});
runGovernorWorkflow
();
it
(
'nominal'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
const
txQueue
=
await
this
.
helper
.
queue
();
const
eta
=
await
this
.
mock
.
proposalEta
(
this
.
proposal
.
id
);
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
expectEvent
(
txQueue
,
'ProposalQueued'
,
{
proposalId
:
this
.
proposal
.
id
});
await
expectEvent
.
inTransaction
(
txQueue
.
tx
,
this
.
timelock
,
'QueueTransaction'
,
{
eta
});
expectEvent
(
txExecute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
proposal
.
id
});
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
timelock
,
'ExecuteTransaction'
,
{
eta
});
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
);
});
describe
(
'not queued'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
enable
:
false
},
execute
:
{
error
:
'GovernorTimelockCompound: proposal not yet queued'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
});
runGovernorWorkflow
();
});
describe
(
'should revert'
,
function
()
{
describe
(
'on queue'
,
function
()
{
it
(
'if already queued'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
expectRevert
(
this
.
helper
.
queue
(),
'Governor: proposal not successful'
);
});
describe
(
'to early'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'Timelock::executeTransaction: Transaction hasn
\'
t surpassed time lock'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
it
(
'if proposal contains duplicate calls'
,
async
function
()
{
const
action
=
{
target
:
this
.
token
.
address
,
data
:
this
.
token
.
contract
.
methods
.
approve
(
this
.
receiver
.
address
,
constants
.
MAX_UINT256
).
encodeABI
(),
};
this
.
helper
.
setProposal
([
action
,
action
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
queue
(),
'GovernorTimelockCompound: identical proposal action already queued'
,
);
await
expectRevert
(
this
.
helper
.
execute
(),
'GovernorTimelockCompound: proposal not yet queued'
,
);
});
});
runGovernorWorkflow
();
});
describe
(
'to late'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
30
*
86400
},
execute
:
{
error
:
'Governor: proposal not successful'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Expired
);
});
runGovernorWorkflow
();
});
describe
(
'on execute'
,
function
()
{
it
(
'if not queued'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
(
+
1
);
describe
(
'deplicated underlying call'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
Array
(
2
).
fill
(
this
.
token
.
address
),
Array
(
2
).
fill
(
web3
.
utils
.
toWei
(
'0'
)),
Array
(
2
).
fill
(
this
.
token
.
contract
.
methods
.
approve
(
this
.
receiver
.
address
,
constants
.
MAX_UINT256
).
encodeABI
()),
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
error
:
'GovernorTimelockCompound: identical proposal action already queued'
,
},
execute
:
{
error
:
'GovernorTimelockCompound: proposal not yet queued'
,
},
},
};
});
runGovernorWorkflow
();
});
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
describe
(
're-queue / re-execute'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Executed
);
await
expectRevert
(
this
.
helper
.
execute
(),
'GovernorTimelockCompound: proposal not yet queued'
,
);
});
await
expectRevert
(
this
.
mock
.
queue
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
it
(
'if too early'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Timelock::executeTransaction: Transaction hasn
\'
t surpassed time lock'
,
);
});
it
(
'if too late'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
(
+
30
*
86400
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Expired
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
,
);
});
it
(
'if already executed'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
await
this
.
helper
.
execute
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
,
);
});
});
runGovernorWorkflow
();
});
describe
(
'cancel before queue prevents scheduling'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
describe
(
'cancel'
,
function
()
{
it
(
'cancel before queue prevents scheduling'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
await
this
.
helper
.
cancel
(
),
'ProposalCanceled'
,
{
proposalId
:
this
.
id
},
{
proposalId
:
this
.
proposal
.
id
},
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
mock
.
queue
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
helper
.
queue
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
describe
(
'cancel after queue prevents executing'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
it
(
'cancel after queue prevents executing'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
const
receipt
=
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
);
expectEvent
(
receipt
,
await
this
.
helper
.
cancel
()
,
'ProposalCanceled'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
receipt
.
receipt
.
transactionHash
,
this
.
timelock
,
'CancelTransaction'
,
{
proposalId
:
this
.
proposal
.
id
},
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
describe
(
'relay'
,
function
()
{
beforeEach
(
async
function
()
{
await
this
.
token
.
mint
(
this
.
mock
.
address
,
1
);
this
.
call
=
[
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
];
});
describe
(
'onlyGovernance'
,
function
()
{
describe
(
'relay'
,
function
()
{
beforeEach
(
async
function
()
{
await
this
.
token
.
mint
(
this
.
mock
.
address
,
1
);
});
it
(
'protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
relay
(...
this
.
call
),
'Governor: onlyGovernance'
,
);
});
it
(
'is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
relay
(
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
),
'Governor: onlyGovernance'
,
);
});
describe
(
'using workflow'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
,
],
[
web3
.
utils
.
toWei
(
'0'
),
],
[
this
.
mock
.
contract
.
methods
.
relay
(...
this
.
call
).
encodeABI
(),
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
it
(
'can be executed through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
relay
(
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
).
encodeABI
(),
},
}
;
],
'<proposal description>'
)
;
expect
(
await
this
.
token
.
balanceOf
(
this
.
mock
.
address
),
1
);
expect
(
await
this
.
token
.
balanceOf
(
other
),
0
);
});
afterEach
(
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
expect
(
await
this
.
token
.
balanceOf
(
this
.
mock
.
address
),
0
);
expect
(
await
this
.
token
.
balanceOf
(
other
),
1
);
});
runGovernorWorkflow
();
});
});
describe
(
'updateTimelock'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
newTimelock
=
await
Timelock
.
new
(
this
.
mock
.
address
,
7
*
86400
);
});
it
(
'protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
updateTimelock
(
this
.
newTimelock
.
address
),
'Governor: onlyGovernance'
,
);
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
token
,
'Transfer'
,
{
from
:
this
.
mock
.
address
,
to
:
other
,
value
:
'1'
},
);
});
});
describe
(
'u
sing workflow
'
,
function
()
{
describe
(
'u
pdateTimelock
'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
timelock
.
address
,
this
.
mock
.
address
,
],
[
web3
.
utils
.
toWei
(
'0'
),
web3
.
utils
.
toWei
(
'0'
),
],
[
this
.
timelock
.
contract
.
methods
.
setPendingAdmin
(
admin
).
encodeABI
(),
this
.
mock
.
contract
.
methods
.
updateTimelock
(
this
.
newTimelock
.
address
).
encodeABI
(),
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
this
.
newTimelock
=
await
Timelock
.
new
(
this
.
mock
.
address
,
7
*
86400
);
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
it
(
'is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
updateTimelock
(
this
.
newTimelock
.
address
),
'Governor: onlyGovernance'
,
);
});
it
(
'can be executed through governance to'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
timelock
.
address
,
data
:
this
.
timelock
.
contract
.
methods
.
setPendingAdmin
(
owner
).
encodeABI
(),
},
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
updateTimelock
(
this
.
newTimelock
.
address
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
expectEvent
(
t
his
.
receipts
.
e
xecute
,
t
xE
xecute
,
'TimelockChange'
,
{
oldTimelock
:
this
.
timelock
.
address
,
newTimelock
:
this
.
newTimelock
.
address
},
);
expect
(
await
this
.
mock
.
timelock
()).
to
.
be
.
bignumber
.
equal
(
this
.
newTimelock
.
address
);
});
runGovernorWorkflow
();
});
});
describe
(
'transfer timelock to new governor'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
newGovernor
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
8
,
32
,
this
.
timelock
.
address
,
0
);
});
it
(
'can transfer timelock to new governor'
,
async
function
()
{
const
newGovernor
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
8
,
32
,
this
.
timelock
.
address
,
0
);
describe
(
'using workflow'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
timelock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
timelock
.
contract
.
methods
.
setPendingAdmin
(
this
.
newGovernor
.
address
).
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
},
};
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
timelock
,
'NewPendingAdmin'
,
{
newPendingAdmin
:
this
.
newGovernor
.
address
},
);
await
this
.
newGovernor
.
__acceptAdmin
();
expect
(
await
this
.
timelock
.
admin
()).
to
.
be
.
bignumber
.
equal
(
this
.
newGovernor
.
address
);
});
runGovernorWorkflow
();
this
.
helper
.
setProposal
([
{
target
:
this
.
timelock
.
address
,
data
:
this
.
timelock
.
contract
.
methods
.
setPendingAdmin
(
newGovernor
.
address
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
timelock
,
'NewPendingAdmin'
,
{
newPendingAdmin
:
newGovernor
.
address
},
);
await
newGovernor
.
__acceptAdmin
();
expect
(
await
this
.
timelock
.
admin
()).
to
.
be
.
bignumber
.
equal
(
newGovernor
.
address
);
});
});
});
test/governance/extensions/GovernorTimelockControl.test.js
View file @
f668ffb1
const
{
constants
,
expectEvent
,
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
,
constants
,
expectEvent
,
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'../GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
{
shouldSupportInterfaces
,
...
...
@@ -16,7 +13,7 @@ const Governor = artifacts.require('GovernorTimelockControlMock');
const
CallReceiver
=
artifacts
.
require
(
'CallReceiverMock'
);
contract
(
'GovernorTimelockControl'
,
function
(
accounts
)
{
const
[
admin
,
voter
,
other
]
=
accounts
;
const
[
owner
,
voter1
,
voter2
,
voter3
,
voter4
,
other
]
=
accounts
;
const
TIMELOCK_ADMIN_ROLE
=
web3
.
utils
.
soliditySha3
(
'TIMELOCK_ADMIN_ROLE'
);
const
PROPOSER_ROLE
=
web3
.
utils
.
soliditySha3
(
'PROPOSER_ROLE'
);
...
...
@@ -28,30 +25,61 @@ contract('GovernorTimelockControl', function (accounts) {
const
tokenName
=
'MockToken'
;
const
tokenSymbol
=
'MTKN'
;
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
const
[
deployer
]
=
await
web3
.
eth
.
getAccounts
();
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
timelock
=
await
Timelock
.
new
(
3600
,
[],
[]);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
4
,
16
,
this
.
timelock
.
address
,
0
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
votingDelay
,
votingPeriod
,
this
.
timelock
.
address
,
0
,
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
this
.
TIMELOCK_ADMIN_ROLE
=
await
this
.
timelock
.
TIMELOCK_ADMIN_ROLE
();
this
.
PROPOSER_ROLE
=
await
this
.
timelock
.
PROPOSER_ROLE
();
this
.
EXECUTOR_ROLE
=
await
this
.
timelock
.
EXECUTOR_ROLE
();
this
.
CANCELLER_ROLE
=
await
this
.
timelock
.
CANCELLER_ROLE
();
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
timelock
.
address
,
value
});
// normal setup: governor is proposer, everyone is executor, timelock is its own admin
await
this
.
timelock
.
grantRole
(
PROPOSER_ROLE
,
this
.
mock
.
address
);
await
this
.
timelock
.
grantRole
(
PROPOSER_ROLE
,
admin
);
await
this
.
timelock
.
grantRole
(
PROPOSER_ROLE
,
owner
);
await
this
.
timelock
.
grantRole
(
CANCELLER_ROLE
,
this
.
mock
.
address
);
await
this
.
timelock
.
grantRole
(
CANCELLER_ROLE
,
admin
);
await
this
.
timelock
.
grantRole
(
CANCELLER_ROLE
,
owner
);
await
this
.
timelock
.
grantRole
(
EXECUTOR_ROLE
,
constants
.
ZERO_ADDRESS
);
await
this
.
timelock
.
revokeRole
(
TIMELOCK_ADMIN_ROLE
,
deployer
);
await
this
.
token
.
mint
(
voter
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter
,
{
from
:
voter
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
this
.
proposal
.
timelockid
=
await
this
.
timelock
.
hashOperationBatch
(
...
this
.
proposal
.
shortProposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
proposal
.
shortProposal
[
3
],
);
});
shouldSupportInterfaces
([
...
...
@@ -62,473 +90,293 @@ contract('GovernorTimelockControl', function (accounts) {
]);
it
(
'doesn
\'
t accept ether transfers'
,
async
function
()
{
await
expectRevert
.
unspecified
(
web3
.
eth
.
sendTransaction
({
from
:
vot
er
,
to
:
this
.
mock
.
address
,
value
:
1
}));
await
expectRevert
.
unspecified
(
web3
.
eth
.
sendTransaction
({
from
:
own
er
,
to
:
this
.
mock
.
address
,
value
:
1
}));
});
it
(
'post deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
timelock
()).
to
.
be
.
equal
(
this
.
timelock
.
address
);
});
describe
(
'nominal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
},
};
});
afterEach
(
async
function
()
{
const
timelockid
=
await
this
.
timelock
.
hashOperationBatch
(
...
this
.
settings
.
proposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
descriptionHash
,
);
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
queue
,
'ProposalQueued'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
queue
.
transactionHash
,
this
.
timelock
,
'CallScheduled'
,
{
id
:
timelockid
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
timelock
,
'CallExecuted'
,
{
id
:
timelockid
},
);
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
,
);
});
runGovernorWorkflow
();
it
(
'nominal'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
const
txQueue
=
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
expectEvent
(
txQueue
,
'ProposalQueued'
,
{
proposalId
:
this
.
proposal
.
id
});
await
expectEvent
.
inTransaction
(
txQueue
.
tx
,
this
.
timelock
,
'CallScheduled'
,
{
id
:
this
.
proposal
.
timelockid
});
expectEvent
(
txExecute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
proposal
.
id
});
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
timelock
,
'CallExecuted'
,
{
id
:
this
.
proposal
.
timelockid
});
await
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
receiver
,
'MockFunctionCalled'
);
});
describe
(
'executed by other proposer'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
execute
:
{
enable
:
false
},
},
};
describe
(
'should revert'
,
function
()
{
describe
(
'on queue'
,
function
()
{
it
(
'if already queued'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
expectRevert
(
this
.
helper
.
queue
(),
'Governor: proposal not successful'
);
});
});
afterEach
(
async
function
()
{
await
this
.
timelock
.
executeBatch
(
...
this
.
settings
.
proposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
descriptionHash
,
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Executed
);
describe
(
'on execute'
,
function
()
{
it
(
'if not queued'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
(
+
1
);
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
});
runGovernorWorkflow
();
});
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
describe
(
'not queued'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
enable
:
false
},
execute
:
{
error
:
'TimelockController: operation is not ready'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
});
runGovernorWorkflow
();
});
await
expectRevert
(
this
.
helper
.
execute
(),
'TimelockController: operation is not ready'
);
});
describe
(
'to early'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'TimelockController: operation is not ready'
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
});
runGovernorWorkflow
();
});
it
(
'if too early'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
describe
(
're-queue / re-execute'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Executed
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
await
expectRevert
(
this
.
mock
.
queue
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
await
expectRevert
(
this
.
helper
.
execute
(),
'TimelockController: operation is not ready'
);
});
it
(
'if already executed'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
await
this
.
helper
.
execute
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
it
(
'if already executed by another proposer'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
await
this
.
timelock
.
executeBatch
(
...
this
.
proposal
.
shortProposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
proposal
.
shortProposal
[
3
],
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
});
runGovernorWorkflow
();
});
describe
(
'cancel before queue prevents scheduling'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Succeeded
);
describe
(
'cancel'
,
function
()
{
it
(
'cancel before queue prevents scheduling'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
await
this
.
helper
.
cancel
(
),
'ProposalCanceled'
,
{
proposalId
:
this
.
id
},
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
mock
.
queue
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
{
proposalId
:
this
.
proposal
.
id
},
);
});
runGovernorWorkflow
();
});
describe
(
'cancel after queue prevents execution'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
execute
:
{
enable
:
false
},
},
};
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
helper
.
queue
(),
'Governor: proposal not successful'
);
});
afterEach
(
async
function
()
{
const
timelockid
=
await
this
.
timelock
.
hashOperationBatch
(
...
this
.
settings
.
proposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
descriptionHash
,
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
it
(
'cancel after queue prevents executing'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
const
receipt
=
await
this
.
mock
.
cancel
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
);
expectEvent
(
receipt
,
await
this
.
helper
.
cancel
()
,
'ProposalCanceled'
,
{
proposalId
:
this
.
id
},
{
proposalId
:
this
.
proposal
.
id
},
);
await
expectEvent
.
inTransaction
(
receipt
.
receipt
.
transactionHash
,
this
.
timelock
,
'Cancelled'
,
{
id
:
timelockid
},
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
mock
.
execute
(...
this
.
settings
.
proposal
.
slice
(
0
,
-
1
),
this
.
descriptionHash
),
'Governor: proposal not successful'
,
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
runGovernorWorkflow
();
});
describe
(
'relay'
,
function
()
{
beforeEach
(
async
function
()
{
await
this
.
token
.
mint
(
this
.
mock
.
address
,
1
);
this
.
call
=
[
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
];
});
it
(
'cancel on timelock is reflected on governor'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
it
(
'protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
relay
(...
this
.
call
),
'Governor: onlyGovernance'
,
);
});
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
it
(
'protected against other proposers'
,
async
function
()
{
await
this
.
timelock
.
schedule
(
this
.
mock
.
address
,
web3
.
utils
.
toWei
(
'0'
),
this
.
mock
.
contract
.
methods
.
relay
(...
this
.
call
).
encodeABI
(),
constants
.
ZERO_BYTES32
,
constants
.
ZERO_BYTES32
,
3600
,
{
from
:
admin
},
expectEvent
(
await
this
.
timelock
.
cancel
(
this
.
proposal
.
timelockid
,
{
from
:
owner
}),
'Cancelled'
,
{
id
:
this
.
proposal
.
timelockid
},
);
await
time
.
increase
(
3600
);
await
expectRevert
(
this
.
timelock
.
execute
(
this
.
mock
.
address
,
web3
.
utils
.
toWei
(
'0'
),
this
.
mock
.
contract
.
methods
.
relay
(...
this
.
call
).
encodeABI
(),
constants
.
ZERO_BYTES32
,
constants
.
ZERO_BYTES32
,
{
from
:
admin
},
),
'TimelockController: underlying transaction reverted'
,
);
expect
(
await
this
.
mock
.
state
(
this
.
proposal
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
});
});
describe
(
'using workflow'
,
function
()
{
describe
(
'onlyGovernance'
,
function
()
{
describe
(
'relay'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
,
],
[
web3
.
utils
.
toWei
(
'0'
),
],
[
this
.
mock
.
contract
.
methods
.
relay
(...
this
.
call
).
encodeABI
(),
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
7
*
86400
},
await
this
.
token
.
mint
(
this
.
mock
.
address
,
1
);
});
it
(
'is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
relay
(
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
),
'Governor: onlyGovernance'
,
);
});
it
(
'can be executed through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
relay
(
this
.
token
.
address
,
0
,
this
.
token
.
contract
.
methods
.
transfer
(
other
,
1
).
encodeABI
(),
).
encodeABI
(),
},
}
;
],
'<proposal description>'
)
;
expect
(
await
this
.
token
.
balanceOf
(
this
.
mock
.
address
),
1
);
expect
(
await
this
.
token
.
balanceOf
(
other
),
0
);
});
afterEach
(
async
function
()
{
expect
(
await
this
.
token
.
balanceOf
(
this
.
mock
.
address
),
0
);
expect
(
await
this
.
token
.
balanceOf
(
other
),
1
);
});
runGovernorWorkflow
();
});
});
describe
(
'cancel on timelock is forwarded in state'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
const
timelockid
=
await
this
.
timelock
.
hashOperationBatch
(
...
this
.
settings
.
proposal
.
slice
(
0
,
3
),
'0x0'
,
this
.
descriptionHash
,
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Queued
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
const
receipt
=
await
this
.
timelock
.
cancel
(
timelockid
,
{
from
:
admin
});
expectEvent
(
receipt
,
'Cancelled'
,
{
id
:
timelockid
},
);
expect
(
await
this
.
token
.
balanceOf
(
this
.
mock
.
address
),
0
);
expect
(
await
this
.
token
.
balanceOf
(
other
),
1
);
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Canceled
);
});
runGovernorWorkflow
();
});
expectEvent
.
inTransaction
(
txExecute
.
tx
,
this
.
token
,
'Transfer'
,
{
from
:
this
.
mock
.
address
,
to
:
other
,
value
:
'1'
},
);
});
describe
(
'updateTimelock'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
newTimelock
=
await
Timelock
.
new
(
3600
,
[],
[]);
});
it
(
'protected against other proposers'
,
async
function
()
{
await
this
.
timelock
.
schedule
(
this
.
mock
.
address
,
web3
.
utils
.
toWei
(
'0'
),
this
.
mock
.
contract
.
methods
.
relay
(
constants
.
ZERO_ADDRESS
,
0
,
'0x'
).
encodeABI
(),
constants
.
ZERO_BYTES32
,
constants
.
ZERO_BYTES32
,
3600
,
{
from
:
owner
},
);
it
(
'protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
updateTimelock
(
this
.
newTimelock
.
address
),
'Governor: onlyGovernance'
,
);
await
time
.
increase
(
3600
);
await
expectRevert
(
this
.
timelock
.
execute
(
this
.
mock
.
address
,
web3
.
utils
.
toWei
(
'0'
),
this
.
mock
.
contract
.
methods
.
relay
(
constants
.
ZERO_ADDRESS
,
0
,
'0x'
).
encodeABI
(),
constants
.
ZERO_BYTES32
,
constants
.
ZERO_BYTES32
,
{
from
:
owner
},
),
'TimelockController: underlying transaction reverted'
,
);
});
});
describe
(
'u
sing workflow
'
,
function
()
{
describe
(
'u
pdateTimelock
'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
updateTimelock
(
this
.
newTimelock
.
address
).
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
},
};
this
.
newTimelock
=
await
Timelock
.
new
(
3600
,
[],
[]);
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
},
);
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
it
(
'is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
updateTimelock
(
this
.
newTimelock
.
address
),
'Governor: onlyGovernance'
,
);
});
it
(
'can be executed through governance to'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
updateTimelock
(
this
.
newTimelock
.
address
).
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
const
txExecute
=
await
this
.
helper
.
execute
();
expectEvent
(
t
his
.
receipts
.
e
xecute
,
t
xE
xecute
,
'TimelockChange'
,
{
oldTimelock
:
this
.
timelock
.
address
,
newTimelock
:
this
.
newTimelock
.
address
},
);
expect
(
await
this
.
mock
.
timelock
()).
to
.
be
.
bignumber
.
equal
(
this
.
newTimelock
.
address
);
});
runGovernorWorkflow
();
});
});
describe
(
'clear queue of pending governor calls'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
nonGovernanceFunction
().
encodeABI
()
],
'<proposal description>'
,
],
voters
:
[
{
voter
:
voter
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
queue
:
{
delay
:
3600
},
},
};
});
afterEach
(
async
function
()
{
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
},
);
});
runGovernorWorkflow
();
it
(
'clear queue of pending governor calls'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
nonGovernanceFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
queue
();
await
this
.
helper
.
waitForEta
();
await
this
.
helper
.
execute
();
// This path clears _governanceCall as part of the afterExecute call,
// but we have not way to check that the cleanup actually happened other
// then coverage reports.
});
});
test/governance/extensions/GovernorWeightQuorumFraction.test.js
View file @
f668ffb1
const
{
BN
,
expectEvent
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
BN
,
expectEvent
,
expectRevert
,
time
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
runGovernorWorkflow
,
}
=
require
(
'./../GovernorWorkflow.behavior'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance'
);
const
Token
=
artifacts
.
require
(
'ERC20VotesMock'
);
const
Governor
=
artifacts
.
require
(
'GovernorMock'
);
...
...
@@ -19,24 +17,41 @@ contract('GovernorVotesQuorumFraction', function (accounts) {
const
tokenSupply
=
new
BN
(
web3
.
utils
.
toWei
(
'100'
));
const
ratio
=
new
BN
(
8
);
// percents
const
newRatio
=
new
BN
(
6
);
// percents
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
4
,
16
,
ratio
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
,
votingDelay
,
votingPeriod
,
ratio
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
expect
(
await
this
.
mock
.
name
()).
to
.
be
.
equal
(
name
);
expect
(
await
this
.
mock
.
token
()).
to
.
be
.
equal
(
this
.
token
.
address
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
'4'
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
'16'
);
expect
(
await
this
.
mock
.
votingDelay
()).
to
.
be
.
bignumber
.
equal
(
votingDelay
);
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
expect
(
await
this
.
mock
.
quorum
(
0
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
this
.
mock
.
quorumNumerator
()).
to
.
be
.
bignumber
.
equal
(
ratio
);
expect
(
await
this
.
mock
.
quorumDenominator
()).
to
.
be
.
bignumber
.
equal
(
'100'
);
...
...
@@ -44,51 +59,47 @@ contract('GovernorVotesQuorumFraction', function (accounts) {
.
to
.
be
.
bignumber
.
equal
(
tokenSupply
.
mul
(
ratio
).
divn
(
100
));
});
describe
(
'quroum not reached'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'Governor: proposal not successful'
},
},
};
});
runGovernorWorkflow
();
it
(
'quroum reached'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
});
describe
(
'update quorum ratio through proposal'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
updateQuorumNumerator
(
newRatio
).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
tokenSupply
,
support
:
Enums
.
VoteType
.
For
}
,
]
,
}
;
it
(
'quroum not reached'
,
async
function
()
{
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'Governor: proposal not successful'
);
});
describe
(
'onlyGovernance updates'
,
function
()
{
it
(
'updateQuorumNumerator is protected'
,
async
function
()
{
await
expectRevert
(
this
.
mock
.
updateQuorumNumerator
(
newRatio
)
,
'Governor: onlyGovernance'
,
)
;
});
afterEach
(
async
function
()
{
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
mock
,
'QuorumNumeratorUpdated'
,
it
(
'can updateQuorumNumerator through governance'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
oldQuorumNumerator
:
ratio
,
newQuorumNumerator
:
newRatio
,
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
updateQuorumNumerator
(
newRatio
).
encodeABI
()
,
},
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
expectEvent
(
await
this
.
helper
.
execute
(),
'QuorumNumeratorUpdated'
,
{
oldQuorumNumerator
:
ratio
,
newQuorumNumerator
:
newRatio
},
);
expect
(
await
this
.
mock
.
quorumNumerator
()).
to
.
be
.
bignumber
.
equal
(
newRatio
);
...
...
@@ -96,27 +107,24 @@ contract('GovernorVotesQuorumFraction', function (accounts) {
expect
(
await
time
.
latestBlock
().
then
(
blockNumber
=>
this
.
mock
.
quorum
(
blockNumber
.
subn
(
1
))))
.
to
.
be
.
bignumber
.
equal
(
tokenSupply
.
mul
(
newRatio
).
divn
(
100
));
});
runGovernorWorkflow
();
});
describe
(
'update quorum over the maximum'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
mock
.
address
],
[
web3
.
utils
.
toWei
(
'0'
)
],
[
this
.
mock
.
contract
.
methods
.
updateQuorumNumerator
(
new
BN
(
101
)).
encodeABI
()
],
'<proposal description>'
,
],
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
tokenSupply
,
support
:
Enums
.
VoteType
.
For
},
],
steps
:
{
execute
:
{
error
:
'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator'
},
it
(
'cannot updateQuorumNumerator over the maximum'
,
async
function
()
{
this
.
helper
.
setProposal
([
{
target
:
this
.
mock
.
address
,
data
:
this
.
mock
.
contract
.
methods
.
updateQuorumNumerator
(
'101'
).
encodeABI
(),
},
};
],
'<proposal description>'
);
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter1
});
await
this
.
helper
.
waitForDeadline
();
await
expectRevert
(
this
.
helper
.
execute
(),
'GovernorVotesQuorumFraction: quorumNumerator over quorumDenominator'
,
);
});
runGovernorWorkflow
();
});
});
test/governance/extensions/GovernorWithParams.test.js
View file @
f668ffb1
const
{
BN
,
constants
,
expectEvent
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
web3
}
=
require
(
'@openzeppelin/test-helpers/src/setup'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
BN
,
expectEvent
}
=
require
(
'@openzeppelin/test-helpers'
);
const
{
expect
}
=
require
(
'chai'
);
const
ethSigUtil
=
require
(
'eth-sig-util'
);
const
Wallet
=
require
(
'ethereumjs-wallet'
).
default
;
const
{
EIP712Domain
}
=
require
(
'../../helpers/eip712'
);
const
{
fromRpcSig
}
=
require
(
'ethereumjs-util'
);
const
{
runGovernorWorkflow
}
=
require
(
'../GovernorWorkflow.behavior
'
);
const
{
expect
}
=
require
(
'chai
'
);
const
Enums
=
require
(
'../../helpers/enums'
);
const
{
EIP712Domain
}
=
require
(
'../../helpers/eip712
'
);
const
{
GovernorHelper
}
=
require
(
'../../helpers/governance
'
);
const
Token
=
artifacts
.
require
(
'ERC20VotesCompMock'
);
const
Governor
=
artifacts
.
require
(
'GovernorWithParamsMock'
);
const
CallReceiver
=
artifacts
.
require
(
'CallReceiverMock'
);
const
rawParams
=
{
uintParam
:
new
BN
(
'42'
),
strParam
:
'These are my params'
,
};
const
encodedParams
=
web3
.
eth
.
abi
.
encodeParameters
(
[
'uint256'
,
'string'
],
Object
.
values
(
rawParams
),
);
contract
(
'GovernorWithParams'
,
function
(
accounts
)
{
const
[
owner
,
proposer
,
voter1
,
voter2
,
voter3
,
voter4
]
=
accounts
;
const
[
owner
,
proposer
,
voter1
,
voter2
,
voter3
,
voter4
]
=
accounts
;
const
name
=
'OZ-Governor'
;
const
version
=
'1'
;
...
...
@@ -23,17 +31,32 @@ contract('GovernorWithParams', function (accounts) {
const
tokenSupply
=
web3
.
utils
.
toWei
(
'100'
);
const
votingDelay
=
new
BN
(
4
);
const
votingPeriod
=
new
BN
(
16
);
const
value
=
web3
.
utils
.
toWei
(
'1'
);
beforeEach
(
async
function
()
{
this
.
owner
=
owner
;
this
.
chainId
=
await
web3
.
eth
.
getChainId
()
;
this
.
token
=
await
Token
.
new
(
tokenName
,
tokenSymbol
);
this
.
mock
=
await
Governor
.
new
(
name
,
this
.
token
.
address
);
this
.
receiver
=
await
CallReceiver
.
new
();
this
.
helper
=
new
GovernorHelper
(
this
.
mock
);
await
web3
.
eth
.
sendTransaction
({
from
:
owner
,
to
:
this
.
mock
.
address
,
value
});
await
this
.
token
.
mint
(
owner
,
tokenSupply
);
await
this
.
token
.
delegate
(
voter1
,
{
from
:
voter1
});
await
this
.
token
.
delegate
(
voter2
,
{
from
:
voter2
});
await
this
.
token
.
delegate
(
voter3
,
{
from
:
voter3
});
await
this
.
token
.
delegate
(
voter4
,
{
from
:
voter4
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter1
,
value
:
web3
.
utils
.
toWei
(
'10'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter2
,
value
:
web3
.
utils
.
toWei
(
'7'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter3
,
value
:
web3
.
utils
.
toWei
(
'5'
)
},
{
from
:
owner
});
await
this
.
helper
.
delegate
({
token
:
this
.
token
,
to
:
voter4
,
value
:
web3
.
utils
.
toWei
(
'2'
)
},
{
from
:
owner
});
// default proposal
this
.
proposal
=
this
.
helper
.
setProposal
([
{
target
:
this
.
receiver
.
address
,
value
,
data
:
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
(),
},
],
'<proposal description>'
);
});
it
(
'deployment check'
,
async
function
()
{
...
...
@@ -43,172 +66,57 @@ contract('GovernorWithParams', function (accounts) {
expect
(
await
this
.
mock
.
votingPeriod
()).
to
.
be
.
bignumber
.
equal
(
votingPeriod
);
});
describe
(
'nominal is unaffected'
,
function
()
{
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'1'
),
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
voter
:
voter2
,
weight
:
web3
.
utils
.
toWei
(
'7'
),
support
:
Enums
.
VoteType
.
For
},
{
voter
:
voter3
,
weight
:
web3
.
utils
.
toWei
(
'5'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter4
,
weight
:
web3
.
utils
.
toWei
(
'2'
),
support
:
Enums
.
VoteType
.
Abstain
},
],
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
await
this
.
mock
.
proposalVotes
(
this
.
id
).
then
((
result
)
=>
{
for
(
const
[
key
,
value
]
of
Object
.
entries
(
Enums
.
VoteType
))
{
expect
(
result
[
`
${
key
.
toLowerCase
()}
Votes`
]).
to
.
be
.
bignumber
.
equal
(
Object
.
values
(
this
.
settings
.
voters
)
.
filter
(({
support
})
=>
support
===
value
)
.
reduce
((
acc
,
{
weight
})
=>
acc
.
add
(
new
BN
(
weight
)),
new
BN
(
'0'
)),
);
}
});
const
startBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
);
const
endBlock
=
new
BN
(
this
.
receipts
.
propose
.
blockNumber
).
add
(
votingDelay
).
add
(
votingPeriod
);
expect
(
await
this
.
mock
.
proposalSnapshot
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
startBlock
);
expect
(
await
this
.
mock
.
proposalDeadline
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
endBlock
);
expectEvent
(
this
.
receipts
.
propose
,
'ProposalCreated'
,
{
proposalId
:
this
.
id
,
proposer
,
targets
:
this
.
settings
.
proposal
[
0
],
// values: this.settings.proposal[1].map(value => new BN(value)),
signatures
:
this
.
settings
.
proposal
[
2
].
map
(()
=>
''
),
calldatas
:
this
.
settings
.
proposal
[
2
],
startBlock
,
endBlock
,
description
:
this
.
settings
.
proposal
[
3
],
});
this
.
receipts
.
castVote
.
filter
(
Boolean
).
forEach
((
vote
)
=>
{
const
{
voter
}
=
vote
.
logs
.
filter
(({
event
})
=>
event
===
'VoteCast'
).
find
(
Boolean
).
args
;
expectEvent
(
vote
,
'VoteCast'
,
this
.
settings
.
voters
.
find
(({
address
})
=>
address
===
voter
),
);
});
expectEvent
(
this
.
receipts
.
execute
,
'ProposalExecuted'
,
{
proposalId
:
this
.
id
});
await
expectEvent
.
inTransaction
(
this
.
receipts
.
execute
.
transactionHash
,
this
.
receiver
,
'MockFunctionCalled'
);
});
runGovernorWorkflow
();
it
(
'nominal is unaffected'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'This is nice'
},
{
from
:
voter1
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
},
{
from
:
voter2
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Against
},
{
from
:
voter3
});
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
Abstain
},
{
from
:
voter4
});
await
this
.
helper
.
waitForDeadline
();
await
this
.
helper
.
execute
();
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
owner
)).
to
.
be
.
equal
(
false
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter1
)).
to
.
be
.
equal
(
true
);
expect
(
await
this
.
mock
.
hasVoted
(
this
.
proposal
.
id
,
voter2
)).
to
.
be
.
equal
(
true
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
mock
.
address
)).
to
.
be
.
bignumber
.
equal
(
'0'
);
expect
(
await
web3
.
eth
.
getBalance
(
this
.
receiver
.
address
)).
to
.
be
.
bignumber
.
equal
(
value
);
});
describe
(
'Voting with params is properly supported'
,
function
()
{
const
voter2Weight
=
web3
.
utils
.
toWei
(
'1.0'
);
beforeEach
(
async
function
()
{
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'0.2'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
voter2
,
weight
:
voter2Weight
},
// do not actually vote, only getting tokenss
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
it
(
'Voting with params is properly supported'
,
async
function
()
{
await
this
.
helper
.
propose
({
from
:
proposer
});
await
this
.
helper
.
waitForSnapshot
();
const
weight
=
new
BN
(
web3
.
utils
.
toWei
(
'7'
)).
sub
(
rawParams
.
uintParam
);
const
tx
=
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'no particular reason'
,
params
:
encodedParams
,
},
{
from
:
voter2
});
expectEvent
(
tx
,
'CountParams'
,
{
...
rawParams
});
expectEvent
(
tx
,
'VoteCastWithParams'
,
{
voter
:
voter2
,
proposalId
:
this
.
proposal
.
id
,
support
:
Enums
.
VoteType
.
For
,
weight
,
reason
:
'no particular reason'
,
params
:
encodedParams
,
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
const
uintParam
=
new
BN
(
1
);
const
strParam
=
'These are my params'
;
const
reducedWeight
=
new
BN
(
voter2Weight
).
sub
(
uintParam
);
const
params
=
web3
.
eth
.
abi
.
encodeParameters
([
'uint256'
,
'string'
],
[
uintParam
,
strParam
]);
const
tx
=
await
this
.
mock
.
castVoteWithReasonAndParams
(
this
.
id
,
Enums
.
VoteType
.
For
,
''
,
params
,
{
from
:
voter2
});
expectEvent
(
tx
,
'CountParams'
,
{
uintParam
,
strParam
});
expectEvent
(
tx
,
'VoteCastWithParams'
,
{
voter
:
voter2
,
weight
:
reducedWeight
,
params
});
const
votes
=
await
this
.
mock
.
proposalVotes
(
this
.
id
);
expect
(
votes
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
reducedWeight
);
});
runGovernorWorkflow
();
const
votes
=
await
this
.
mock
.
proposalVotes
(
this
.
proposal
.
id
);
expect
(
votes
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
weight
);
});
describe
(
'Voting with params by signature is properly supported'
,
function
()
{
const
voterBySig
=
Wallet
.
generate
();
// generate voter by signature wallet
const
sigVoterWeight
=
web3
.
utils
.
toWei
(
'1.0'
);
it
(
'Voting with params by signature is properly supported'
,
async
function
()
{
const
voterBySig
=
Wallet
.
generate
();
const
voterBySigAddress
=
web3
.
utils
.
toChecksumAddress
(
voterBySig
.
getAddressString
()
);
beforeEach
(
async
function
()
{
this
.
chainId
=
await
web3
.
eth
.
getChainId
();
this
.
voter
=
web3
.
utils
.
toChecksumAddress
(
voterBySig
.
getAddressString
());
// use delegateBySig to enable vote delegation sig voting wallet
const
{
v
,
r
,
s
}
=
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
data
:
{
types
:
{
EIP712Domain
,
Delegation
:
[
{
name
:
'delegatee'
,
type
:
'address'
},
{
name
:
'nonce'
,
type
:
'uint256'
},
{
name
:
'expiry'
,
type
:
'uint256'
},
],
},
domain
:
{
name
:
tokenName
,
version
:
'1'
,
chainId
:
this
.
chainId
,
verifyingContract
:
this
.
token
.
address
},
primaryType
:
'Delegation'
,
message
:
{
delegatee
:
this
.
voter
,
nonce
:
0
,
expiry
:
constants
.
MAX_UINT256
},
},
}),
);
await
this
.
token
.
delegateBySig
(
this
.
voter
,
0
,
constants
.
MAX_UINT256
,
v
,
r
,
s
);
this
.
settings
=
{
proposal
:
[
[
this
.
receiver
.
address
],
[
0
],
[
this
.
receiver
.
contract
.
methods
.
mockFunction
().
encodeABI
()],
'<proposal description>'
,
],
proposer
,
tokenHolder
:
owner
,
voters
:
[
{
voter
:
voter1
,
weight
:
web3
.
utils
.
toWei
(
'0.2'
),
support
:
Enums
.
VoteType
.
Against
},
{
voter
:
this
.
voter
,
weight
:
sigVoterWeight
},
// do not actually vote, only getting tokens
],
steps
:
{
wait
:
{
enable
:
false
},
execute
:
{
enable
:
false
},
},
};
});
afterEach
(
async
function
()
{
expect
(
await
this
.
mock
.
state
(
this
.
id
)).
to
.
be
.
bignumber
.
equal
(
Enums
.
ProposalState
.
Active
);
const
reason
=
'This is my reason'
;
const
uintParam
=
new
BN
(
1
);
const
strParam
=
'These are my params'
;
const
reducedWeight
=
new
BN
(
sigVoterWeight
).
sub
(
uintParam
);
const
params
=
web3
.
eth
.
abi
.
encodeParameters
([
'uint256'
,
'string'
],
[
uintParam
,
strParam
]);
// prepare signature for vote by signature
const
{
v
,
r
,
s
}
=
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
const
signature
=
async
(
message
)
=>
{
return
fromRpcSig
(
ethSigUtil
.
signTypedMessage
(
voterBySig
.
getPrivateKey
(),
{
data
:
{
types
:
{
EIP712Domain
,
...
...
@@ -221,18 +129,38 @@ contract('GovernorWithParams', function (accounts) {
},
domain
:
{
name
,
version
,
chainId
:
this
.
chainId
,
verifyingContract
:
this
.
mock
.
address
},
primaryType
:
'ExtendedBallot'
,
message
:
{
proposalId
:
this
.
id
,
support
:
Enums
.
VoteType
.
For
,
reason
,
params
}
,
message
,
},
}),
);
},
));
};
await
this
.
token
.
delegate
(
voterBySigAddress
,
{
from
:
voter2
});
const
tx
=
await
this
.
mock
.
castVoteWithReasonAndParamsBySig
(
this
.
id
,
Enums
.
VoteType
.
For
,
reason
,
params
,
v
,
r
,
s
);
// Run proposal
await
this
.
helper
.
propose
();
await
this
.
helper
.
waitForSnapshot
();
expectEvent
(
tx
,
'CountParams'
,
{
uintParam
,
strParam
});
expectEvent
(
tx
,
'VoteCastWithParams'
,
{
voter
:
this
.
voter
,
weight
:
reducedWeight
,
params
});
const
votes
=
await
this
.
mock
.
proposalVotes
(
this
.
id
);
expect
(
votes
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
reducedWeight
);
const
weight
=
new
BN
(
web3
.
utils
.
toWei
(
'7'
)).
sub
(
rawParams
.
uintParam
);
const
tx
=
await
this
.
helper
.
vote
({
support
:
Enums
.
VoteType
.
For
,
reason
:
'no particular reason'
,
params
:
encodedParams
,
signature
,
});
expectEvent
(
tx
,
'CountParams'
,
{
...
rawParams
});
expectEvent
(
tx
,
'VoteCastWithParams'
,
{
voter
:
voterBySigAddress
,
proposalId
:
this
.
proposal
.
id
,
support
:
Enums
.
VoteType
.
For
,
weight
,
reason
:
'no particular reason'
,
params
:
encodedParams
,
});
runGovernorWorkflow
();
const
votes
=
await
this
.
mock
.
proposalVotes
(
this
.
proposal
.
id
);
expect
(
votes
.
forVotes
).
to
.
be
.
bignumber
.
equal
(
weight
);
});
});
test/helpers/governance.js
0 → 100644
View file @
f668ffb1
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
,
};
test/utils/introspection/SupportsInterface.behavior.js
View file @
f668ffb1
...
...
@@ -112,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'
,
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'
,
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
);
}
}
);
}
}
}
);
});
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment