From 7f26c5dc5d290266da244872a0c940662a5edf9e Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 12 Jan 2023 14:22:54 +0200 Subject: [PATCH 001/106] Install open zeppelin contracts --- package.json | 4 +++- yarn.lock | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6d43c22480..e5de8c25d3 100644 --- a/package.json +++ b/package.json @@ -93,5 +93,7 @@ "resolutions": { "**/bignumber.js": "^9.0.2" }, - "dependencies": {} + "dependencies": { + "@openzeppelin/contracts": "^4.8.0" + } } diff --git a/yarn.lock b/yarn.lock index 55aa646505..583368552c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2775,6 +2775,11 @@ dependencies: "@octokit/openapi-types" "^12.11.0" +"@openzeppelin/contracts@^4.8.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" + integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== + "@sindresorhus/slugify@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-0.8.0.tgz#5550b7fa064f3a8a82651463ad635378054c72d0" From 4f39ff1b609a4799ad93e91aa8b8a868b7966c4b Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 13 Jan 2023 09:10:51 +0200 Subject: [PATCH 002/106] Init foundry in governance --- .gitignore | 5 +++++ .gitmodules | 2 ++ contracts/governance/foundry.toml | 6 ++++++ contracts/governance/script/Counter.s.sol | 12 ++++++++++++ contracts/governance/src/Counter.sol | 14 +++++++++++++ contracts/governance/test/Counter.t.sol | 24 +++++++++++++++++++++++ 6 files changed, 63 insertions(+) create mode 100644 contracts/governance/foundry.toml create mode 100644 contracts/governance/script/Counter.s.sol create mode 100644 contracts/governance/src/Counter.sol create mode 100644 contracts/governance/test/Counter.t.sol diff --git a/.gitignore b/.gitignore index 30ad99d0bd..b380ce90f2 100644 --- a/.gitignore +++ b/.gitignore @@ -97,6 +97,11 @@ out/ # typechain wrappers contracts/zero-ex/typechain-wrappers/ +# foundry packages +contracts/governance/lib/forge-std +contracts/governance/cache +contracts/governance/out + # Doc README copy packages/*/docs/README.md diff --git a/.gitmodules b/.gitmodules index 0e1391b9ff..73bb4b5fa9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,6 @@ url = https://github.com/foundry-rs/forge-std [submodule "contracts/erc20/lib/forge-std"] path = contracts/erc20/lib/forge-std +[submodule "contracts/governance/lib/forge-std"] + path = contracts/governance/lib/forge-std url = https://github.com/foundry-rs/forge-std diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml new file mode 100644 index 0000000000..e6810b2b58 --- /dev/null +++ b/contracts/governance/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = 'src' +out = 'out' +libs = ['lib'] + +# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file diff --git a/contracts/governance/script/Counter.s.sol b/contracts/governance/script/Counter.s.sol new file mode 100644 index 0000000000..0e546aba37 --- /dev/null +++ b/contracts/governance/script/Counter.s.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Script.sol"; + +contract CounterScript is Script { + function setUp() public {} + + function run() public { + vm.broadcast(); + } +} diff --git a/contracts/governance/src/Counter.sol b/contracts/governance/src/Counter.sol new file mode 100644 index 0000000000..aded7997b0 --- /dev/null +++ b/contracts/governance/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/contracts/governance/test/Counter.t.sol b/contracts/governance/test/Counter.t.sol new file mode 100644 index 0000000000..30235e8a88 --- /dev/null +++ b/contracts/governance/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function testIncrement() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testSetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} From 2b42a6e23dffe420af9a3253bc2253c8cc1c5729 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 13 Jan 2023 16:13:47 +0200 Subject: [PATCH 003/106] Add wrapped ZRX token --- contracts/governance/script/Counter.s.sol | 12 ----- contracts/governance/src/Counter.sol | 14 ------ contracts/governance/src/ZRXWrappedToken.sol | 48 +++++++++++++++++++ contracts/governance/test/Counter.t.sol | 24 ---------- .../governance/test/ZRXWrappedTokenTest.t.sol | 30 ++++++++++++ 5 files changed, 78 insertions(+), 50 deletions(-) delete mode 100644 contracts/governance/script/Counter.s.sol delete mode 100644 contracts/governance/src/Counter.sol create mode 100644 contracts/governance/src/ZRXWrappedToken.sol delete mode 100644 contracts/governance/test/Counter.t.sol create mode 100644 contracts/governance/test/ZRXWrappedTokenTest.t.sol diff --git a/contracts/governance/script/Counter.s.sol b/contracts/governance/script/Counter.s.sol deleted file mode 100644 index 0e546aba37..0000000000 --- a/contracts/governance/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/contracts/governance/src/Counter.sol b/contracts/governance/src/Counter.sol deleted file mode 100644 index aded7997b0..0000000000 --- a/contracts/governance/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol new file mode 100644 index 0000000000..2d7e86218a --- /dev/null +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; + +contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper { + constructor( + IERC20 wrappedToken + ) ERC20("Wrapped ZRX", "wZRX") ERC20Permit("Wrapped ZRX") ERC20Wrapper(wrappedToken) {} + + // The functions below are the required overrides from the base contracts + + function decimals() public view override(ERC20, ERC20Wrapper) returns (uint8) { + super.decimals(); + } + + function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) { + super._afterTokenTransfer(from, to, amount); + } + + function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) { + super._mint(to, amount); + } + + function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) { + super._burn(account, amount); + } +} diff --git a/contracts/governance/test/Counter.t.sol b/contracts/governance/test/Counter.t.sol deleted file mode 100644 index 30235e8a88..0000000000 --- a/contracts/governance/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Test.sol"; -import "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function testIncrement() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testSetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol new file mode 100644 index 0000000000..43f38cc466 --- /dev/null +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "../src/ZRXWrappedToken.sol"; + +contract ZRXWrappedTokenTest is Test { + ZRXWrappedToken public wToken; + + function setUp() public { + wToken = new ZRXWrappedToken(); + } +} From 1b9f4fedc4500470163139dd412e93752c2a2247 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 14:25:25 +0200 Subject: [PATCH 004/106] Add governance contracts testing to CI --- .github/workflows/ci.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 030f179d78..68b67de317 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,3 +135,22 @@ jobs: path: ./contracts/zero-ex/lcov.info min_coverage: 6.98 exclude: '**/tests' + + - name: Run Forge build on governance contracts + working-directory: contracts/governance + run: | + forge --version + forge build --sizes + + - name: Run Forge tests on governance contracts + working-directory: contracts/governance + run: | + forge test -vvv --gas-report + + - name: Run Forge coverage on governance contracts + working-directory: contracts/governance + run: | + forge coverage --report lcov + + + From 3f18f9d8862b777c020631c99ca61b70bc9bc72a Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 14:30:40 +0200 Subject: [PATCH 005/106] Set optimizer runs to default --- contracts/governance/foundry.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index e6810b2b58..1a85d6e1bb 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -2,5 +2,4 @@ src = 'src' out = 'out' libs = ['lib'] - -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +optimizer_runs = 20_000 From 71cfb20ddaeafc7ab49ab27583fb7876d21752a8 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 14:31:45 +0200 Subject: [PATCH 006/106] Upgrade to patched version of openzeppelin/contracts --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e5de8c25d3..00a1744beb 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,6 @@ "**/bignumber.js": "^9.0.2" }, "dependencies": { - "@openzeppelin/contracts": "^4.8.0" + "@openzeppelin/contracts": "^4.8.1" } } diff --git a/yarn.lock b/yarn.lock index 583368552c..918c1abad3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2775,10 +2775,10 @@ dependencies: "@octokit/openapi-types" "^12.11.0" -"@openzeppelin/contracts@^4.8.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.0.tgz#6854c37df205dd2c056bdfa1b853f5d732109109" - integrity sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw== +"@openzeppelin/contracts@^4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.1.tgz#709cfc4bbb3ca9f4460d60101f15dac6b7a2d5e4" + integrity sha512-xQ6eUZl+RDyb/FiZe1h+U7qr/f4p/SrTSQcTPH2bjur3C5DbuW/zFgCU/b1P/xcIaEqJep+9ju4xDRi3rmChdQ== "@sindresorhus/slugify@^0.8.0": version "0.8.0" From 8ab6af421339e83932c9f5779291e528c3639e07 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 14:39:17 +0200 Subject: [PATCH 007/106] Test stakingakng / unwrapping ZRX --- contracts/governance/test/BaseTest.sol | 43 +++++++++++++++++++ .../governance/test/ZRXWrappedTokenTest.t.sol | 27 ++++++++++-- 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 contracts/governance/test/BaseTest.sol diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol new file mode 100644 index 0000000000..bb3867ff9b --- /dev/null +++ b/contracts/governance/test/BaseTest.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; + +contract BaseTest is Test { + address payable internal account1 = payable(vm.addr(1)); + address payable internal account2 = payable(vm.addr(2)); + address payable internal account3 = payable(vm.addr(3)); + + constructor() public { + vm.deal(account1, 1e20); + vm.deal(account2, 1e20); + vm.deal(account3, 1e20); + } + + function createZRXToken() internal returns (address) { + bytes memory _bytecode = abi.encodePacked(vm.getCode("../../erc20/generated-artifacts/ZRXToken.json")); + address _address; + assembly { + _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) + } + return address(_address); + } +} diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 43f38cc466..0bb30edcab 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -18,13 +18,34 @@ */ pragma solidity ^0.8.17; -import "forge-std/Test.sol"; +import "./BaseTest.sol"; import "../src/ZRXWrappedToken.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -contract ZRXWrappedTokenTest is Test { +contract ZRXWrappedTokenTest is BaseTest { + IERC20 public token; ZRXWrappedToken public wToken; + address public voter1 = account1; function setUp() public { - wToken = new ZRXWrappedToken(); + vm.startPrank(account1); + + token = IERC20(createZRXToken()); + token.transfer(account2, 100e18); + token.transfer(account3, 100e18); + + wToken = new ZRXWrappedToken(token); + + vm.stopPrank(account1); + } + + function testShouldBeAbleToWrapZRX() public { + vm.startPrank(account2); + token.approve(wToken.address, 1e18); + wToken.depositFor(account2, 1e18); + } + + function testShouldBeAbleToUnwrapToZRX() public { + } } From c07065f069e40d5f7dbc7463171c37937e93346c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 14:50:26 +0200 Subject: [PATCH 008/106] Init npm package --- contracts/governance/package.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 contracts/governance/package.json diff --git a/contracts/governance/package.json b/contracts/governance/package.json new file mode 100644 index 0000000000..f3bb61ce78 --- /dev/null +++ b/contracts/governance/package.json @@ -0,0 +1,19 @@ +{ + "name": "@0x/governance", + "version": "1.0.0", + "description": "Governance implementation for the 0x protocol and treasury", + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "forge test" + }, + "repository": { + "type": "git", + "url": "https://github.com/0xProject/protocol.git" + }, + "license": "Apache-2.0", + "dependencies": {} +} From 5ce63d4f220a6d9cf25192be17e0e6411e57d733 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 15:10:46 +0200 Subject: [PATCH 009/106] Lint fix, removing lib from gitignore --- .gitignore | 2 +- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index b380ce90f2..267e7785ae 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ typings/ # built library using in commonjs module syntax lib/ + # UMD bundles that export the global variable _bundles @@ -98,7 +99,6 @@ out/ contracts/zero-ex/typechain-wrappers/ # foundry packages -contracts/governance/lib/forge-std contracts/governance/cache contracts/governance/out diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 0bb30edcab..3e9d17e934 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -45,7 +45,5 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.depositFor(account2, 1e18); } - function testShouldBeAbleToUnwrapToZRX() public { - - } + function testShouldBeAbleToUnwrapToZRX() public {} } From f222bdaff72249f2d72568596332eaec0b79d52f Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 15:12:08 +0200 Subject: [PATCH 010/106] Add openzeppelin contracts git submodule for foundry --- .gitmodules | 3 +++ contracts/governance/lib/openzeppelin-contracts | 1 + 2 files changed, 4 insertions(+) create mode 160000 contracts/governance/lib/openzeppelin-contracts diff --git a/.gitmodules b/.gitmodules index 73bb4b5fa9..d7c1ed2850 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ [submodule "contracts/governance/lib/forge-std"] path = contracts/governance/lib/forge-std url = https://github.com/foundry-rs/forge-std +[submodule "contracts/governance/lib/openzeppelin-contracts"] + path = contracts/governance/lib/openzeppelin-contracts + url = https://github.com/openzeppelin/openzeppelin-contracts diff --git a/contracts/governance/lib/openzeppelin-contracts b/contracts/governance/lib/openzeppelin-contracts new file mode 160000 index 0000000000..0457042d93 --- /dev/null +++ b/contracts/governance/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 0457042d93d9dfd760dbaa06a4d2f1216fdbe297 From ca3070666443ee641b8a9365032db30678dbb24e Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 19 Jan 2023 15:30:42 +0200 Subject: [PATCH 011/106] Add vanilla governor contract --- contracts/governance/src/ZeroExGovernor.sol | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 contracts/governance/src/ZeroExGovernor.sol diff --git a/contracts/governance/src/ZeroExGovernor.sol b/contracts/governance/src/ZeroExGovernor.sol new file mode 100644 index 0000000000..1020dad0bb --- /dev/null +++ b/contracts/governance/src/ZeroExGovernor.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/governance/Governor.sol"; +import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; +import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; +import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; +import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; + +/// @custom:security-contact security@0xproject.com +contract ZeroExGovernor is + Governor, + GovernorSettings, + GovernorCountingSimple, + GovernorVotes, + GovernorVotesQuorumFraction, + GovernorTimelockControl +{ + constructor( + IVotes _token, + TimelockController _timelock + ) + Governor("ZeroExGovernor") + GovernorSettings( + 21600 /* voting delay: 3 days */, + 50400 /* voting period: 1 week */, + 0 /* proposal threshold */ + ) + GovernorVotes(_token) + GovernorVotesQuorumFraction(10) + GovernorTimelockControl(_timelock) + {} + + // The following functions are overrides required by Solidity. + + function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) { + return super.votingDelay(); + } + + function votingPeriod() public view override(IGovernor, GovernorSettings) returns (uint256) { + return super.votingPeriod(); + } + + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + return super.quorum(blockNumber); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} From 265a50c7d90cde9c30065bca375d476afad0fdad Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 20 Jan 2023 12:41:58 +0200 Subject: [PATCH 012/106] Fix reference paths to imported packages --- contracts/governance/ZRXToken.json | 6202 +++++++++++++++++ contracts/governance/foundry.toml | 2 + contracts/governance/src/ZRXWrappedToken.sol | 8 +- contracts/governance/src/ZeroExGovernor.sol | 12 +- contracts/governance/test/BaseTest.sol | 3 +- .../governance/test/ZRXWrappedTokenTest.t.sol | 8 +- 6 files changed, 6219 insertions(+), 16 deletions(-) create mode 100644 contracts/governance/ZRXToken.json diff --git a/contracts/governance/ZRXToken.json b/contracts/governance/ZRXToken.json new file mode 100644 index 0000000000..1c29022118 --- /dev/null +++ b/contracts/governance/ZRXToken.json @@ -0,0 +1,6202 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "ZRXToken", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "inputs": [], + "payable": false, + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ], + "devdoc": { + "methods": { + "transferFrom(address,address,uint256)": { + "details": "ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.", + "params": { + "_from": "Address to transfer from.", + "_to": "Address to transfer to.", + "_value": "Amount to transfer." + }, + "return": "Success of transfer." + } + } + }, + "evm": { + "assembly": " /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\n mstore(0x40, 0x60)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5056:5064 10 ** 27 */\n 0x33b2e3c9fd0803ce8000000\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n 0x3\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5208:5286 function ZRXToken() public {... */\n jumpi(tag_1, iszero(callvalue))\n invalid\ntag_1:\ntag_2:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5268:5279 totalSupply */\n sload(0x3)\n sub(exp(0x2, 0xa0), 0x1)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5254:5264 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5265 balances[msg.sender] */\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5253 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5265 balances[msg.sender] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5279 balances[msg.sender] = totalSupply */\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5208:5286 function ZRXToken() public {... */\ntag_3:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\ntag_4:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n return\nstop\n\nsub_0: assembly {\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(calldatasize))\n and(div(calldataload(0x0), 0x100000000000000000000000000000000000000000000000000000000), 0xffffffff)\n 0x6fdde03\n dup2\n eq\n tag_2\n jumpi\n dup1\n 0x95ea7b3\n eq\n tag_3\n jumpi\n dup1\n 0x18160ddd\n eq\n tag_4\n jumpi\n dup1\n 0x23b872dd\n eq\n tag_5\n jumpi\n dup1\n 0x313ce567\n eq\n tag_6\n jumpi\n dup1\n 0x70a08231\n eq\n tag_7\n jumpi\n dup1\n 0x95d89b41\n eq\n tag_8\n jumpi\n dup1\n 0xa9059cbb\n eq\n tag_9\n jumpi\n dup1\n 0xdd62ed3e\n eq\n tag_10\n jumpi\n tag_1:\n invalid\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5109:5158 string public constant name = \"0x Protocol Token\" */\n tag_2:\n jumpi(tag_11, iszero(callvalue))\n invalid\n tag_11:\n tag_12\n jump(tag_13)\n tag_12:\n 0x40\n dup1\n mload\n 0x20\n dup1\n dup3\n mstore\n dup4\n mload\n dup2\n dup4\n add\n mstore\n dup4\n mload\n swap2\n swap3\n dup4\n swap3\n swap1\n dup4\n add\n swap2\n dup6\n add\n swap1\n dup1\n dup4\n dup4\n /* \"--CODEGEN--\":18:20 */\n dup3\n iszero\n /* \"--CODEGEN--\":13:16 */\n tag_14\n /* \"--CODEGEN--\":7:12 */\n jumpi\n /* \"--CODEGEN--\":32:37 */\n tag_15:\n /* \"--CODEGEN--\":59:62 */\n dup1\n /* \"--CODEGEN--\":53:58 */\n mload\n /* \"--CODEGEN--\":48:51 */\n dup3\n /* \"--CODEGEN--\":41:47 */\n mstore\n /* \"--CODEGEN--\":93:95 */\n 0x20\n /* \"--CODEGEN--\":88:91 */\n dup4\n /* \"--CODEGEN--\":85:87 */\n gt\n /* \"--CODEGEN--\":78:84 */\n iszero\n /* \"--CODEGEN--\":73:76 */\n tag_14\n /* \"--CODEGEN--\":67:72 */\n jumpi\n /* \"--CODEGEN--\":152:155 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0\n swap1\n swap3\n add\n swap2\n /* \"--CODEGEN--\":117:119 */\n 0x20\n /* \"--CODEGEN--\":108:111 */\n swap2\n dup3\n add\n swap2\n /* \"--CODEGEN--\":130:133 */\n add\n /* \"--CODEGEN--\":172:177 */\n tag_15\n /* \"--CODEGEN--\":167:171 */\n jump\n /* \"--CODEGEN--\":181:184 */\n tag_14:\n /* \"--CODEGEN--\":3:189 */\n pop\n pop\n pop\n swap1\n pop\n swap1\n dup2\n add\n swap1\n 0x1f\n and\n dup1\n iszero\n tag_16\n jumpi\n dup1\n dup3\n sub\n dup1\n mload\n 0x1\n dup4\n 0x20\n sub\n 0x100\n exp\n sub\n not\n and\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n tag_16:\n pop\n swap3\n pop\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_3:\n jumpi(tag_17, iszero(callvalue))\n invalid\n tag_17:\n tag_18\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n calldataload(0x24)\n jump(tag_19)\n tag_18:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n tag_4:\n jumpi(tag_20, iszero(callvalue))\n invalid\n tag_20:\n tag_21\n jump(tag_22)\n tag_21:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_5:\n jumpi(tag_23, iszero(callvalue))\n invalid\n tag_23:\n tag_18\n 0xffffffffffffffffffffffffffffffffffffffff\n calldataload(0x4)\n dup2\n and\n swap1\n calldataload(0x24)\n and\n calldataload(0x44)\n jump(tag_25)\n tag_24:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n tag_6:\n jumpi(tag_26, iszero(callvalue))\n invalid\n tag_26:\n tag_27\n jump(tag_28)\n tag_27:\n 0x40\n dup1\n mload\n 0xff\n swap1\n swap3\n and\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_7:\n jumpi(tag_29, iszero(callvalue))\n invalid\n tag_29:\n tag_21\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n jump(tag_31)\n tag_30:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5164:5201 string public constant symbol = \"ZRX\" */\n tag_8:\n jumpi(tag_32, iszero(callvalue))\n invalid\n tag_32:\n tag_12\n jump(tag_34)\n tag_33:\n 0x40\n dup1\n mload\n 0x20\n dup1\n dup3\n mstore\n dup4\n mload\n dup2\n dup4\n add\n mstore\n dup4\n mload\n swap2\n swap3\n dup4\n swap3\n swap1\n dup4\n add\n swap2\n dup6\n add\n swap1\n dup1\n dup4\n dup4\n /* \"--CODEGEN--\":18:20 */\n dup3\n iszero\n /* \"--CODEGEN--\":13:16 */\n tag_14\n /* \"--CODEGEN--\":7:12 */\n jumpi\n /* \"--CODEGEN--\":32:37 */\n tag_36:\n /* \"--CODEGEN--\":59:62 */\n dup1\n /* \"--CODEGEN--\":53:58 */\n mload\n /* \"--CODEGEN--\":48:51 */\n dup3\n /* \"--CODEGEN--\":41:47 */\n mstore\n /* \"--CODEGEN--\":93:95 */\n 0x20\n /* \"--CODEGEN--\":88:91 */\n dup4\n /* \"--CODEGEN--\":85:87 */\n gt\n /* \"--CODEGEN--\":78:84 */\n iszero\n /* \"--CODEGEN--\":73:76 */\n tag_14\n /* \"--CODEGEN--\":67:72 */\n jumpi\n /* \"--CODEGEN--\":152:155 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0\n swap1\n swap3\n add\n swap2\n /* \"--CODEGEN--\":117:119 */\n 0x20\n /* \"--CODEGEN--\":108:111 */\n swap2\n dup3\n add\n swap2\n /* \"--CODEGEN--\":130:133 */\n add\n /* \"--CODEGEN--\":172:177 */\n tag_15\n /* \"--CODEGEN--\":167:171 */\n jump\n /* \"--CODEGEN--\":181:184 */\n tag_35:\n /* \"--CODEGEN--\":3:189 */\n pop\n pop\n pop\n swap1\n pop\n swap1\n dup2\n add\n swap1\n 0x1f\n and\n dup1\n iszero\n tag_16\n jumpi\n dup1\n dup3\n sub\n dup1\n mload\n 0x1\n dup4\n 0x20\n sub\n 0x100\n exp\n sub\n not\n and\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n tag_37:\n pop\n swap3\n pop\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_9:\n jumpi(tag_38, iszero(callvalue))\n invalid\n tag_38:\n tag_18\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n calldataload(0x24)\n jump(tag_40)\n tag_39:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_10:\n jumpi(tag_41, iszero(callvalue))\n invalid\n tag_41:\n tag_21\n 0xffffffffffffffffffffffffffffffffffffffff\n calldataload(0x4)\n dup2\n and\n swap1\n calldataload(0x24)\n and\n jump(tag_43)\n tag_42:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5109:5158 string public constant name = \"0x Protocol Token\" */\n tag_13:\n 0x40\n dup1\n mload\n dup1\n dup3\n add\n swap1\n swap2\n mstore\n 0x11\n dup2\n mstore\n 0x30782050726f746f636f6c20546f6b656e000000000000000000000000000000\n 0x20\n dup3\n add\n mstore\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_19:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3607:3617 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3583:3587 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n dup2\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3606 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3628 allowed[msg.sender][_spender] */\n swap5\n dup8\n and\n dup1\n dup5\n mstore\n swap5\n dup3\n mstore\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3637 allowed[msg.sender][_spender] = _value */\n dup7\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3647:3685 Approval(msg.sender, _spender, _value) */\n dup1\n mload\n dup7\n dup2\n mstore\n swap1\n mload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3583:3587 bool */\n swap3\n swap5\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3628 allowed[msg.sender][_spender] */\n swap4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n swap3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3647:3685 Approval(msg.sender, _spender, _value) */\n 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\n swap3\n swap2\n dup2\n swap1\n sub\n swap1\n swap2\n add\n swap1\n log3\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3702:3706 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_44:\n swap3\n swap2\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n tag_22:\n sload(0x3)\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_25:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup5\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4451:4455 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n dup2\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4494 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4502:4512 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4513 allowed[_from][msg.sender] */\n swap1\n swap6\n and\n dup4\n mstore\n swap4\n dup2\n mstore\n dup4\n dup3\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4542 balances[_from] */\n swap3\n dup3\n mstore\n dup2\n swap1\n mstore\n swap2\n dup3\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4552 balances[_from] >= _value */\n dup4\n swap1\n lt\n dup1\n iszero\n swap1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4575 balances[_from] >= _value && allowance >= _value */\n tag_46\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4569:4575 _value */\n dup3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4556:4565 allowance */\n dup2\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4556:4575 allowance >= _value */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4575 balances[_from] >= _value && allowance >= _value */\n tag_46:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4618 balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to] */\n dup1\n iszero\n tag_47\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4618 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup5\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4613 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4618 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4579:4601 balances[_to] + _value */\n dup4\n dup2\n add\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4579:4618 balances[_to] + _value >= balances[_to] */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4618 balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to] */\n tag_47:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n iszero\n tag_48\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4647 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup6\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4642 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4647 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n dup1\n dup3\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4657 balances[_to] += _value */\n dup1\n sload\n dup8\n add\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4671:4686 balances[_from] */\n swap2\n dup8\n and\n dup2\n mstore\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4671:4696 balances[_from] -= _value */\n dup1\n sload\n dup5\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4069:4081 2 ** 256 - 1 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4714:4734 allowance < MAX_UINT */\n dup2\n lt\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4710:4805 if (allowance < MAX_UINT) {... */\n iszero\n tag_49\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4768 allowed[_from] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup7\n and\n 0x0\n swap1\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4761 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4768 allowed[_from] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4769:4779 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4780 allowed[_from][msg.sender] */\n swap1\n swap5\n and\n dup4\n mstore\n swap3\n swap1\n mstore\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4790 allowed[_from][msg.sender] -= _value */\n dup1\n sload\n dup5\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4710:4805 if (allowance < MAX_UINT) {... */\n tag_49:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4834:4837 _to */\n dup4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n 0xffffffffffffffffffffffffffffffffffffffff\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4827:4832 _from */\n dup6\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n 0xffffffffffffffffffffffffffffffffffffffff\n and\n 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4839:4845 _value */\n dup6\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n mload(0x40)\n dup1\n dup3\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n log3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4867:4871 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4860:4871 return true */\n swap2\n pop\n jump(tag_50)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_48:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4909:4914 false */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4902:4914 return false */\n swap2\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_50:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_45:\n pop\n swap4\n swap3\n pop\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n tag_28:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5019:5021 18 */\n 0x12\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_31:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3494:3510 balances[_owner] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3468:3475 uint256 */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3494:3510 balances[_owner] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_51:\n swap2\n swap1\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5164:5201 string public constant symbol = \"ZRX\" */\n tag_34:\n 0x40\n dup1\n mload\n dup1\n dup3\n add\n swap1\n swap2\n mstore\n 0x3\n dup2\n mstore\n 0x5a52580000000000000000000000000000000000000000000000000000000000\n 0x20\n dup3\n add\n mstore\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_40:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2644:2654 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2546:2550 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n dup2\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2665 balances[msg.sender] >= _value */\n dup3\n swap1\n lt\n dup1\n iszero\n swap1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2708 balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to] */\n tag_53\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2708 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup4\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2703 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2708 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2669:2691 balances[_to] + _value */\n dup3\n dup2\n add\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2669:2708 balances[_to] + _value >= balances[_to] */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2708 balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to] */\n tag_53:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n iszero\n tag_54\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2733:2743 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2732 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n dup2\n dup2\n mstore\n 0x20\n dup2\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2754 balances[msg.sender] -= _value */\n dup1\n sload\n dup9\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2781 balances[_to] */\n swap4\n dup8\n and\n dup1\n dup4\n mstore\n swap2\n dup5\n swap1\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2791 balances[_to] += _value */\n dup1\n sload\n dup8\n add\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2805:2838 Transfer(msg.sender, _to, _value) */\n dup4\n mload\n dup7\n dup2\n mstore\n swap4\n mload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2781 balances[_to] */\n swap2\n swap4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2805:2838 Transfer(msg.sender, _to, _value) */\n 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n swap3\n swap1\n dup2\n swap1\n sub\n swap1\n swap2\n add\n swap1\n log3\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2859:2863 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2852:2863 return true */\n jump(tag_44)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_54:\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2901:2906 false */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2894:2906 return false */\n jump(tag_44)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_55:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_52:\n swap3\n swap2\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_43:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup4\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3790:3797 uint256 */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n swap1\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3823 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3841 allowed[_owner][_spender] */\n swap4\n dup6\n and\n dup4\n mstore\n swap3\n swap1\n mstore\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_56:\n swap3\n swap2\n pop\n pop\n jump\t// out\n}\n", + "bytecode": { + "linkReferences": {}, + "object": "0x60606040526b033b2e3c9fd0803ce8000000600355341561001c57fe5b5b600354600160a060020a0333166000908152602081905260409020555b5b61078d8061004a6000396000f300606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a72305820b706acfbca681a492299535a7c37132e2b3dd130311b59423d04596c21a624170029", + "opcodes": "PUSH1 0x60 PUSH1 0x40 MSTORE PUSH12 0x33B2E3C9FD0803CE8000000 PUSH1 0x3 SSTORE CALLVALUE ISZERO PUSH2 0x1C JUMPI INVALID JUMPDEST JUMPDEST PUSH1 0x3 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SSTORE JUMPDEST JUMPDEST PUSH2 0x78D DUP1 PUSH2 0x4A PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE CALLDATASIZE ISZERO PUSH2 0x96 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x98 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x146 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x186 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x1A8 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1EE JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x214 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x24F JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x2FD JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x33D JUMPI JUMPDEST INVALID JUMPDEST CALLVALUE ISZERO PUSH2 0xA0 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x37E JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x14E JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x3B5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x18E JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH2 0x42D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1B0 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1F6 JUMPI INVALID JUMPDEST PUSH2 0x1FE PUSH2 0x5D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x21C JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x5D9 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x257 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x305 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x63C JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x345 JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x727 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE DUP1 DUP4 SHA3 DUP7 SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP5 SWAP4 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP6 AND DUP4 MSTORE SWAP4 DUP2 MSTORE DUP4 DUP3 SHA3 SLOAD SWAP3 DUP3 MSTORE DUP2 SWAP1 MSTORE SWAP2 DUP3 SHA3 SLOAD DUP4 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x483 JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x4B6 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x5C6 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x558 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x5CB JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 SHA3 SLOAD DUP3 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x699 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x718 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 DUP1 SLOAD DUP9 SWAP1 SUB SWAP1 SSTORE SWAP4 DUP8 AND DUP1 DUP4 MSTORE SWAP2 DUP5 SWAP1 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP4 MLOAD DUP7 DUP2 MSTORE SWAP4 MLOAD SWAP2 SWAP4 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x427 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x427 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 SLOAD JUMPDEST SWAP3 SWAP2 POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 SHA3 0xb7 MOD 0xac 0xfb 0xca PUSH9 0x1A492299535A7C3713 0x2e 0x2b 0x3d 0xd1 ADDRESS BALANCE 0x1b MSIZE TIMESTAMP 0x3d DIV MSIZE PUSH13 0x21A62417002900000000000000 ", + "sourceMap": "4935:353:0:-;;;5056:8;5027:37;;5208:78;;;;;;;5268:11;;-1:-1:-1;;;;;5254:10:0;5245:20;:8;:20;;;;;;;;;;:34;5208:78;4935:353;;;;;;;" + }, + "deployedBytecode": { + "linkReferences": {}, + "object": "0x606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a72305820b706acfbca681a492299535a7c37132e2b3dd130311b59423d04596c21a624170029", + "opcodes": "PUSH1 0x60 PUSH1 0x40 MSTORE CALLDATASIZE ISZERO PUSH2 0x96 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x98 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x146 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x186 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x1A8 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1EE JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x214 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x24F JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x2FD JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x33D JUMPI JUMPDEST INVALID JUMPDEST CALLVALUE ISZERO PUSH2 0xA0 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x37E JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x14E JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x3B5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x18E JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH2 0x42D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1B0 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1F6 JUMPI INVALID JUMPDEST PUSH2 0x1FE PUSH2 0x5D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x21C JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x5D9 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x257 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x305 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x63C JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x345 JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x727 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE DUP1 DUP4 SHA3 DUP7 SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP5 SWAP4 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP6 AND DUP4 MSTORE SWAP4 DUP2 MSTORE DUP4 DUP3 SHA3 SLOAD SWAP3 DUP3 MSTORE DUP2 SWAP1 MSTORE SWAP2 DUP3 SHA3 SLOAD DUP4 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x483 JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x4B6 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x5C6 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x558 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x5CB JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 SHA3 SLOAD DUP3 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x699 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x718 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 DUP1 SLOAD DUP9 SWAP1 SUB SWAP1 SSTORE SWAP4 DUP8 AND DUP1 DUP4 MSTORE SWAP2 DUP5 SWAP1 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP4 MLOAD DUP7 DUP2 MSTORE SWAP4 MLOAD SWAP2 SWAP4 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x427 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x427 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 SLOAD JUMPDEST SWAP3 SWAP2 POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 SHA3 0xb7 MOD 0xac 0xfb 0xca PUSH9 0x1A492299535A7C3713 0x2e 0x2b 0x3d 0xd1 ADDRESS BALANCE 0x1b MSIZE TIMESTAMP 0x3d DIV MSIZE PUSH13 0x21A62417002900000000000000 ", + "sourceMap": "4935:353:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3523:190:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5027:37;;;;;;;;;;;;;;;;;;;;;;;;;;4369:562;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4986:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3415:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5164:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2490:433:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3719:129;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;:::o;3523:190::-;3599:19;3607:10;3599:19;;3583:4;3599:19;;;:7;:19;;;;;;;;:29;;;;;;;;;;;;:38;;;3647;;;;;;;3583:4;;3599:29;:19;3647:38;;;;;;;;;;;-1:-1:-1;3702:4:0;3523:190;;;;;:::o;5027:37::-;;;;:::o;4369:562::-;4487:14;;;;4451:4;4487:14;;;:7;:14;;;;;;;;4502:10;4487:26;;;;;;;;;;;;4527:15;;;;;;;;;;:25;;;;;;:48;;;4569:6;4556:9;:19;;4527:48;:91;;;;-1:-1:-1;4605:13:0;;;:8;:13;;;;;;;;;;;4579:22;;;:39;;4527:91;4523:402;;;4634:13;;;;:8;:13;;;;;;;;;;;:23;;;;;;4671:15;;;;;;:25;;;;;;;4069:12;4714:20;;4710:95;;;4754:14;;;;;;;;:7;:14;;;;;;;;4769:10;4754:26;;;;;;;;;:36;;;;;;;4710:95;4834:3;4818:28;;4827:5;4818:28;;;4839:6;4818:28;;;;;;;;;;;;;;;;;;4867:4;4860:11;;;;4523:402;4909:5;4902:12;;4523:402;4369:562;;;;;;;:::o;4986:35::-;5019:2;4986:35;:::o;3415:102::-;3494:16;;;3468:7;3494:16;;;;;;;;;;;3415:102;;;;:::o;5164:37::-;;;;;;;;;;;;;;;;;;;:::o;2490:433::-;2635:20;2644:10;2635:20;2546:4;2635:20;;;;;;;;;;;:30;;;;;;:73;;-1:-1:-1;2695:13:0;;;:8;:13;;;;;;;;;;;2669:22;;;:39;;2635:73;2631:286;;;2724:20;2733:10;2724:20;;:8;:20;;;;;;;;;;;:30;;;;;;;2768:13;;;;;;;;;;:23;;;;;;2805:33;;;;;;;2768:13;;2805:33;;;;;;;;;;;-1:-1:-1;2859:4:0;2852:11;;2631:286;-1:-1:-1;2901:5:0;2894:12;;2631:286;2490:433;;;;;:::o;3719:129::-;3816:15;;;;3790:7;3816:15;;;:7;:15;;;;;;;;:25;;;;;;;;;;3719:129;;;;;:::o" + }, + "gasEstimates": { + "creation": { + "codeDepositCost": "386600", + "executionCost": "40780", + "totalCost": "427380" + }, + "external": { + "allowance(address,address)": "737", + "approve(address,uint256)": "22218", + "balanceOf(address)": "579", + "decimals()": "270", + "name()": "530", + "symbol()": "662", + "totalSupply()": "417", + "transfer(address,uint256)": "43393", + "transferFrom(address,address,uint256)": "64116" + } + }, + "legacyAssembly": { + ".code": [ + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "60" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4935, + "end": 5288, + "name": "MSTORE" + }, + { + "begin": 5056, + "end": 5064, + "name": "PUSH", + "value": "33B2E3C9FD0803CE8000000" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH", + "value": "3" + }, + { + "begin": 5027, + "end": 5064, + "name": "SSTORE" + }, + { + "begin": 5208, + "end": 5286, + "name": "CALLVALUE" + }, + { + "begin": 5208, + "end": 5286, + "name": "ISZERO" + }, + { + "begin": 5208, + "end": 5286, + "name": "PUSH [tag]", + "value": "1" + }, + { + "begin": 5208, + "end": 5286, + "name": "JUMPI" + }, + { + "begin": 5208, + "end": 5286, + "name": "INVALID" + }, + { + "begin": 5208, + "end": 5286, + "name": "tag", + "value": "1" + }, + { + "begin": 5208, + "end": 5286, + "name": "JUMPDEST" + }, + { + "begin": 5208, + "end": 5286, + "name": "tag", + "value": "2" + }, + { + "begin": 5208, + "end": 5286, + "name": "JUMPDEST" + }, + { + "begin": 5268, + "end": 5279, + "name": "PUSH", + "value": "3" + }, + { + "begin": 5268, + "end": 5279, + "name": "SLOAD" + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "value": "1" + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "value": "A0" + }, + { + "begin": -1, + "end": -1, + "name": "PUSH", + "value": "2" + }, + { + "begin": -1, + "end": -1, + "name": "EXP" + }, + { + "begin": -1, + "end": -1, + "name": "SUB" + }, + { + "begin": 5254, + "end": 5264, + "name": "CALLER" + }, + { + "begin": 5245, + "end": 5265, + "name": "AND" + }, + { + "begin": 5245, + "end": 5253, + "name": "PUSH", + "value": "0" + }, + { + "begin": 5245, + "end": 5265, + "name": "SWAP1" + }, + { + "begin": 5245, + "end": 5265, + "name": "DUP2" + }, + { + "begin": 5245, + "end": 5265, + "name": "MSTORE" + }, + { + "begin": 5245, + "end": 5265, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5245, + "end": 5265, + "name": "DUP2" + }, + { + "begin": 5245, + "end": 5265, + "name": "SWAP1" + }, + { + "begin": 5245, + "end": 5265, + "name": "MSTORE" + }, + { + "begin": 5245, + "end": 5265, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5245, + "end": 5265, + "name": "SWAP1" + }, + { + "begin": 5245, + "end": 5265, + "name": "SHA3" + }, + { + "begin": 5245, + "end": 5279, + "name": "SSTORE" + }, + { + "begin": 5208, + "end": 5286, + "name": "tag", + "value": "3" + }, + { + "begin": 5208, + "end": 5286, + "name": "JUMPDEST" + }, + { + "begin": 4935, + "end": 5288, + "name": "tag", + "value": "4" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPDEST" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH #[$]", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [$]", + "value": "0000000000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4935, + "end": 5288, + "name": "CODECOPY" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4935, + "end": 5288, + "name": "RETURN" + } + ], + ".data": { + "0": { + ".code": [ + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "60" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4935, + "end": 5288, + "name": "MSTORE" + }, + { + "begin": 4935, + "end": 5288, + "name": "CALLDATASIZE" + }, + { + "begin": 4935, + "end": 5288, + "name": "ISZERO" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "1" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "FFFFFFFF" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "100000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4935, + "end": 5288, + "name": "CALLDATALOAD" + }, + { + "begin": 4935, + "end": 5288, + "name": "DIV" + }, + { + "begin": 4935, + "end": 5288, + "name": "AND" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "6FDDE03" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP2" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "2" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "95EA7B3" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "3" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "18160DDD" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "4" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "23B872DD" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "5" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "313CE567" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "6" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "70A08231" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "7" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "95D89B41" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "8" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "A9059CBB" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "9" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "DUP1" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH", + "value": "DD62ED3E" + }, + { + "begin": 4935, + "end": 5288, + "name": "EQ" + }, + { + "begin": 4935, + "end": 5288, + "name": "PUSH [tag]", + "value": "10" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPI" + }, + { + "begin": 4935, + "end": 5288, + "name": "tag", + "value": "1" + }, + { + "begin": 4935, + "end": 5288, + "name": "JUMPDEST" + }, + { + "begin": 4935, + "end": 5288, + "name": "INVALID" + }, + { + "begin": 5109, + "end": 5158, + "name": "tag", + "value": "2" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMPDEST" + }, + { + "begin": 5109, + "end": 5158, + "name": "CALLVALUE" + }, + { + "begin": 5109, + "end": 5158, + "name": "ISZERO" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH [tag]", + "value": "11" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMPI" + }, + { + "begin": 5109, + "end": 5158, + "name": "INVALID" + }, + { + "begin": 5109, + "end": 5158, + "name": "tag", + "value": "11" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMPDEST" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH [tag]", + "value": "12" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH [tag]", + "value": "13" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMP" + }, + { + "begin": 5109, + "end": 5158, + "name": "tag", + "value": "12" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMPDEST" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "MLOAD" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP3" + }, + { + "begin": 5109, + "end": 5158, + "name": "MSTORE" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "MLOAD" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "ADD" + }, + { + "begin": 5109, + "end": 5158, + "name": "MSTORE" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "MLOAD" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP3" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP3" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "ADD" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP6" + }, + { + "begin": 5109, + "end": 5158, + "name": "ADD" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP4" + }, + { + "begin": 18, + "end": 20, + "name": "DUP3" + }, + { + "begin": 18, + "end": 20, + "name": "ISZERO" + }, + { + "begin": 13, + "end": 16, + "name": "PUSH [tag]", + "value": "14" + }, + { + "begin": 7, + "end": 12, + "name": "JUMPI" + }, + { + "begin": 32, + "end": 37, + "name": "tag", + "value": "15" + }, + { + "begin": 32, + "end": 37, + "name": "JUMPDEST" + }, + { + "begin": 59, + "end": 62, + "name": "DUP1" + }, + { + "begin": 53, + "end": 58, + "name": "MLOAD" + }, + { + "begin": 48, + "end": 51, + "name": "DUP3" + }, + { + "begin": 41, + "end": 47, + "name": "MSTORE" + }, + { + "begin": 93, + "end": 95, + "name": "PUSH", + "value": "20" + }, + { + "begin": 88, + "end": 91, + "name": "DUP4" + }, + { + "begin": 85, + "end": 87, + "name": "GT" + }, + { + "begin": 78, + "end": 84, + "name": "ISZERO" + }, + { + "begin": 73, + "end": 76, + "name": "PUSH [tag]", + "value": "14" + }, + { + "begin": 67, + "end": 72, + "name": "JUMPI" + }, + { + "begin": 152, + "end": 155, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP1" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP3" + }, + { + "begin": 152, + "end": 155, + "name": "ADD" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP2" + }, + { + "begin": 117, + "end": 119, + "name": "PUSH", + "value": "20" + }, + { + "begin": 108, + "end": 111, + "name": "SWAP2" + }, + { + "begin": 108, + "end": 111, + "name": "DUP3" + }, + { + "begin": 108, + "end": 111, + "name": "ADD" + }, + { + "begin": 108, + "end": 111, + "name": "SWAP2" + }, + { + "begin": 130, + "end": 133, + "name": "ADD" + }, + { + "begin": 172, + "end": 177, + "name": "PUSH [tag]", + "value": "15" + }, + { + "begin": 167, + "end": 171, + "name": "JUMP" + }, + { + "begin": 181, + "end": 184, + "name": "tag", + "value": "14" + }, + { + "begin": 181, + "end": 184, + "name": "JUMPDEST" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP2" + }, + { + "begin": 3, + "end": 189, + "name": "ADD" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "1F" + }, + { + "begin": 3, + "end": 189, + "name": "AND" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "ISZERO" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH [tag]", + "value": "16" + }, + { + "begin": 3, + "end": 189, + "name": "JUMPI" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP3" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "MLOAD" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP4" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "100" + }, + { + "begin": 3, + "end": 189, + "name": "EXP" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "NOT" + }, + { + "begin": 3, + "end": 189, + "name": "AND" + }, + { + "begin": 3, + "end": 189, + "name": "DUP2" + }, + { + "begin": 3, + "end": 189, + "name": "MSTORE" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3, + "end": 189, + "name": "ADD" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP2" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "tag", + "value": "16" + }, + { + "begin": 3, + "end": 189, + "name": "JUMPDEST" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP3" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3, + "end": 189, + "name": "MLOAD" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP2" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "RETURN" + }, + { + "begin": 3523, + "end": 3713, + "name": "tag", + "value": "3" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPDEST" + }, + { + "begin": 3523, + "end": 3713, + "name": "CALLVALUE" + }, + { + "begin": 3523, + "end": 3713, + "name": "ISZERO" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH [tag]", + "value": "17" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPI" + }, + { + "begin": 3523, + "end": 3713, + "name": "INVALID" + }, + { + "begin": 3523, + "end": 3713, + "name": "tag", + "value": "17" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPDEST" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH [tag]", + "value": "18" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH", + "value": "4" + }, + { + "begin": 3523, + "end": 3713, + "name": "CALLDATALOAD" + }, + { + "begin": 3523, + "end": 3713, + "name": "AND" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH", + "value": "24" + }, + { + "begin": 3523, + "end": 3713, + "name": "CALLDATALOAD" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH [tag]", + "value": "19" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMP" + }, + { + "begin": 3523, + "end": 3713, + "name": "tag", + "value": "18" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPDEST" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3523, + "end": 3713, + "name": "DUP1" + }, + { + "begin": 3523, + "end": 3713, + "name": "MLOAD" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP2" + }, + { + "begin": 3523, + "end": 3713, + "name": "ISZERO" + }, + { + "begin": 3523, + "end": 3713, + "name": "ISZERO" + }, + { + "begin": 3523, + "end": 3713, + "name": "DUP3" + }, + { + "begin": 3523, + "end": 3713, + "name": "MSTORE" + }, + { + "begin": 3523, + "end": 3713, + "name": "MLOAD" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP1" + }, + { + "begin": 3523, + "end": 3713, + "name": "DUP2" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP1" + }, + { + "begin": 3523, + "end": 3713, + "name": "SUB" + }, + { + "begin": 3523, + "end": 3713, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3523, + "end": 3713, + "name": "ADD" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP1" + }, + { + "begin": 3523, + "end": 3713, + "name": "RETURN" + }, + { + "begin": 5027, + "end": 5064, + "name": "tag", + "value": "4" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMPDEST" + }, + { + "begin": 5027, + "end": 5064, + "name": "CALLVALUE" + }, + { + "begin": 5027, + "end": 5064, + "name": "ISZERO" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH [tag]", + "value": "20" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMPI" + }, + { + "begin": 5027, + "end": 5064, + "name": "INVALID" + }, + { + "begin": 5027, + "end": 5064, + "name": "tag", + "value": "20" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMPDEST" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH [tag]", + "value": "21" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH [tag]", + "value": "22" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMP" + }, + { + "begin": 5027, + "end": 5064, + "name": "tag", + "value": "21" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMPDEST" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5027, + "end": 5064, + "name": "DUP1" + }, + { + "begin": 5027, + "end": 5064, + "name": "MLOAD" + }, + { + "begin": 5027, + "end": 5064, + "name": "SWAP2" + }, + { + "begin": 5027, + "end": 5064, + "name": "DUP3" + }, + { + "begin": 5027, + "end": 5064, + "name": "MSTORE" + }, + { + "begin": 5027, + "end": 5064, + "name": "MLOAD" + }, + { + "begin": 5027, + "end": 5064, + "name": "SWAP1" + }, + { + "begin": 5027, + "end": 5064, + "name": "DUP2" + }, + { + "begin": 5027, + "end": 5064, + "name": "SWAP1" + }, + { + "begin": 5027, + "end": 5064, + "name": "SUB" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5027, + "end": 5064, + "name": "ADD" + }, + { + "begin": 5027, + "end": 5064, + "name": "SWAP1" + }, + { + "begin": 5027, + "end": 5064, + "name": "RETURN" + }, + { + "begin": 4369, + "end": 4931, + "name": "tag", + "value": "5" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPDEST" + }, + { + "begin": 4369, + "end": 4931, + "name": "CALLVALUE" + }, + { + "begin": 4369, + "end": 4931, + "name": "ISZERO" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH [tag]", + "value": "23" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPI" + }, + { + "begin": 4369, + "end": 4931, + "name": "INVALID" + }, + { + "begin": 4369, + "end": 4931, + "name": "tag", + "value": "23" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPDEST" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH [tag]", + "value": "18" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "4" + }, + { + "begin": 4369, + "end": 4931, + "name": "CALLDATALOAD" + }, + { + "begin": 4369, + "end": 4931, + "name": "DUP2" + }, + { + "begin": 4369, + "end": 4931, + "name": "AND" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP1" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "24" + }, + { + "begin": 4369, + "end": 4931, + "name": "CALLDATALOAD" + }, + { + "begin": 4369, + "end": 4931, + "name": "AND" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "44" + }, + { + "begin": 4369, + "end": 4931, + "name": "CALLDATALOAD" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH [tag]", + "value": "25" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMP" + }, + { + "begin": 4369, + "end": 4931, + "name": "tag", + "value": "24" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPDEST" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4369, + "end": 4931, + "name": "DUP1" + }, + { + "begin": 4369, + "end": 4931, + "name": "MLOAD" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP2" + }, + { + "begin": 4369, + "end": 4931, + "name": "ISZERO" + }, + { + "begin": 4369, + "end": 4931, + "name": "ISZERO" + }, + { + "begin": 4369, + "end": 4931, + "name": "DUP3" + }, + { + "begin": 4369, + "end": 4931, + "name": "MSTORE" + }, + { + "begin": 4369, + "end": 4931, + "name": "MLOAD" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP1" + }, + { + "begin": 4369, + "end": 4931, + "name": "DUP2" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP1" + }, + { + "begin": 4369, + "end": 4931, + "name": "SUB" + }, + { + "begin": 4369, + "end": 4931, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4369, + "end": 4931, + "name": "ADD" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP1" + }, + { + "begin": 4369, + "end": 4931, + "name": "RETURN" + }, + { + "begin": 4986, + "end": 5021, + "name": "tag", + "value": "6" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMPDEST" + }, + { + "begin": 4986, + "end": 5021, + "name": "CALLVALUE" + }, + { + "begin": 4986, + "end": 5021, + "name": "ISZERO" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH [tag]", + "value": "26" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMPI" + }, + { + "begin": 4986, + "end": 5021, + "name": "INVALID" + }, + { + "begin": 4986, + "end": 5021, + "name": "tag", + "value": "26" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMPDEST" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH [tag]", + "value": "27" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH [tag]", + "value": "28" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMP" + }, + { + "begin": 4986, + "end": 5021, + "name": "tag", + "value": "27" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMPDEST" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4986, + "end": 5021, + "name": "DUP1" + }, + { + "begin": 4986, + "end": 5021, + "name": "MLOAD" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH", + "value": "FF" + }, + { + "begin": 4986, + "end": 5021, + "name": "SWAP1" + }, + { + "begin": 4986, + "end": 5021, + "name": "SWAP3" + }, + { + "begin": 4986, + "end": 5021, + "name": "AND" + }, + { + "begin": 4986, + "end": 5021, + "name": "DUP3" + }, + { + "begin": 4986, + "end": 5021, + "name": "MSTORE" + }, + { + "begin": 4986, + "end": 5021, + "name": "MLOAD" + }, + { + "begin": 4986, + "end": 5021, + "name": "SWAP1" + }, + { + "begin": 4986, + "end": 5021, + "name": "DUP2" + }, + { + "begin": 4986, + "end": 5021, + "name": "SWAP1" + }, + { + "begin": 4986, + "end": 5021, + "name": "SUB" + }, + { + "begin": 4986, + "end": 5021, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4986, + "end": 5021, + "name": "ADD" + }, + { + "begin": 4986, + "end": 5021, + "name": "SWAP1" + }, + { + "begin": 4986, + "end": 5021, + "name": "RETURN" + }, + { + "begin": 3415, + "end": 3517, + "name": "tag", + "value": "7" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPDEST" + }, + { + "begin": 3415, + "end": 3517, + "name": "CALLVALUE" + }, + { + "begin": 3415, + "end": 3517, + "name": "ISZERO" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH [tag]", + "value": "29" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPI" + }, + { + "begin": 3415, + "end": 3517, + "name": "INVALID" + }, + { + "begin": 3415, + "end": 3517, + "name": "tag", + "value": "29" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPDEST" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH [tag]", + "value": "21" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH", + "value": "4" + }, + { + "begin": 3415, + "end": 3517, + "name": "CALLDATALOAD" + }, + { + "begin": 3415, + "end": 3517, + "name": "AND" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH [tag]", + "value": "31" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMP" + }, + { + "begin": 3415, + "end": 3517, + "name": "tag", + "value": "30" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPDEST" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3415, + "end": 3517, + "name": "DUP1" + }, + { + "begin": 3415, + "end": 3517, + "name": "MLOAD" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP2" + }, + { + "begin": 3415, + "end": 3517, + "name": "DUP3" + }, + { + "begin": 3415, + "end": 3517, + "name": "MSTORE" + }, + { + "begin": 3415, + "end": 3517, + "name": "MLOAD" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP1" + }, + { + "begin": 3415, + "end": 3517, + "name": "DUP2" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP1" + }, + { + "begin": 3415, + "end": 3517, + "name": "SUB" + }, + { + "begin": 3415, + "end": 3517, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3415, + "end": 3517, + "name": "ADD" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP1" + }, + { + "begin": 3415, + "end": 3517, + "name": "RETURN" + }, + { + "begin": 5164, + "end": 5201, + "name": "tag", + "value": "8" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMPDEST" + }, + { + "begin": 5164, + "end": 5201, + "name": "CALLVALUE" + }, + { + "begin": 5164, + "end": 5201, + "name": "ISZERO" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH [tag]", + "value": "32" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMPI" + }, + { + "begin": 5164, + "end": 5201, + "name": "INVALID" + }, + { + "begin": 5164, + "end": 5201, + "name": "tag", + "value": "32" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMPDEST" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH [tag]", + "value": "12" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH [tag]", + "value": "34" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMP" + }, + { + "begin": 5164, + "end": 5201, + "name": "tag", + "value": "33" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMPDEST" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "MLOAD" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP3" + }, + { + "begin": 5164, + "end": 5201, + "name": "MSTORE" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "MLOAD" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "ADD" + }, + { + "begin": 5164, + "end": 5201, + "name": "MSTORE" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "MLOAD" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP3" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP3" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "ADD" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP6" + }, + { + "begin": 5164, + "end": 5201, + "name": "ADD" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP4" + }, + { + "begin": 18, + "end": 20, + "name": "DUP3" + }, + { + "begin": 18, + "end": 20, + "name": "ISZERO" + }, + { + "begin": 13, + "end": 16, + "name": "PUSH [tag]", + "value": "14" + }, + { + "begin": 7, + "end": 12, + "name": "JUMPI" + }, + { + "begin": 32, + "end": 37, + "name": "tag", + "value": "36" + }, + { + "begin": 32, + "end": 37, + "name": "JUMPDEST" + }, + { + "begin": 59, + "end": 62, + "name": "DUP1" + }, + { + "begin": 53, + "end": 58, + "name": "MLOAD" + }, + { + "begin": 48, + "end": 51, + "name": "DUP3" + }, + { + "begin": 41, + "end": 47, + "name": "MSTORE" + }, + { + "begin": 93, + "end": 95, + "name": "PUSH", + "value": "20" + }, + { + "begin": 88, + "end": 91, + "name": "DUP4" + }, + { + "begin": 85, + "end": 87, + "name": "GT" + }, + { + "begin": 78, + "end": 84, + "name": "ISZERO" + }, + { + "begin": 73, + "end": 76, + "name": "PUSH [tag]", + "value": "14" + }, + { + "begin": 67, + "end": 72, + "name": "JUMPI" + }, + { + "begin": 152, + "end": 155, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP1" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP3" + }, + { + "begin": 152, + "end": 155, + "name": "ADD" + }, + { + "begin": 152, + "end": 155, + "name": "SWAP2" + }, + { + "begin": 117, + "end": 119, + "name": "PUSH", + "value": "20" + }, + { + "begin": 108, + "end": 111, + "name": "SWAP2" + }, + { + "begin": 108, + "end": 111, + "name": "DUP3" + }, + { + "begin": 108, + "end": 111, + "name": "ADD" + }, + { + "begin": 108, + "end": 111, + "name": "SWAP2" + }, + { + "begin": 130, + "end": 133, + "name": "ADD" + }, + { + "begin": 172, + "end": 177, + "name": "PUSH [tag]", + "value": "15" + }, + { + "begin": 167, + "end": 171, + "name": "JUMP" + }, + { + "begin": 181, + "end": 184, + "name": "tag", + "value": "35" + }, + { + "begin": 181, + "end": 184, + "name": "JUMPDEST" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP2" + }, + { + "begin": 3, + "end": 189, + "name": "ADD" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "1F" + }, + { + "begin": 3, + "end": 189, + "name": "AND" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "ISZERO" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH [tag]", + "value": "16" + }, + { + "begin": 3, + "end": 189, + "name": "JUMPI" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP3" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "MLOAD" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "1" + }, + { + "begin": 3, + "end": 189, + "name": "DUP4" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "100" + }, + { + "begin": 3, + "end": 189, + "name": "EXP" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "NOT" + }, + { + "begin": 3, + "end": 189, + "name": "AND" + }, + { + "begin": 3, + "end": 189, + "name": "DUP2" + }, + { + "begin": 3, + "end": 189, + "name": "MSTORE" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3, + "end": 189, + "name": "ADD" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP2" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "tag", + "value": "37" + }, + { + "begin": 3, + "end": 189, + "name": "JUMPDEST" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP3" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "POP" + }, + { + "begin": 3, + "end": 189, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3, + "end": 189, + "name": "MLOAD" + }, + { + "begin": 3, + "end": 189, + "name": "DUP1" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP2" + }, + { + "begin": 3, + "end": 189, + "name": "SUB" + }, + { + "begin": 3, + "end": 189, + "name": "SWAP1" + }, + { + "begin": 3, + "end": 189, + "name": "RETURN" + }, + { + "begin": 2490, + "end": 2923, + "name": "tag", + "value": "9" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPDEST" + }, + { + "begin": 2490, + "end": 2923, + "name": "CALLVALUE" + }, + { + "begin": 2490, + "end": 2923, + "name": "ISZERO" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH [tag]", + "value": "38" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPI" + }, + { + "begin": 2490, + "end": 2923, + "name": "INVALID" + }, + { + "begin": 2490, + "end": 2923, + "name": "tag", + "value": "38" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPDEST" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH [tag]", + "value": "18" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH", + "value": "4" + }, + { + "begin": 2490, + "end": 2923, + "name": "CALLDATALOAD" + }, + { + "begin": 2490, + "end": 2923, + "name": "AND" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH", + "value": "24" + }, + { + "begin": 2490, + "end": 2923, + "name": "CALLDATALOAD" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH [tag]", + "value": "40" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMP" + }, + { + "begin": 2490, + "end": 2923, + "name": "tag", + "value": "39" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPDEST" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH", + "value": "40" + }, + { + "begin": 2490, + "end": 2923, + "name": "DUP1" + }, + { + "begin": 2490, + "end": 2923, + "name": "MLOAD" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP2" + }, + { + "begin": 2490, + "end": 2923, + "name": "ISZERO" + }, + { + "begin": 2490, + "end": 2923, + "name": "ISZERO" + }, + { + "begin": 2490, + "end": 2923, + "name": "DUP3" + }, + { + "begin": 2490, + "end": 2923, + "name": "MSTORE" + }, + { + "begin": 2490, + "end": 2923, + "name": "MLOAD" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP1" + }, + { + "begin": 2490, + "end": 2923, + "name": "DUP2" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP1" + }, + { + "begin": 2490, + "end": 2923, + "name": "SUB" + }, + { + "begin": 2490, + "end": 2923, + "name": "PUSH", + "value": "20" + }, + { + "begin": 2490, + "end": 2923, + "name": "ADD" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP1" + }, + { + "begin": 2490, + "end": 2923, + "name": "RETURN" + }, + { + "begin": 3719, + "end": 3848, + "name": "tag", + "value": "10" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPDEST" + }, + { + "begin": 3719, + "end": 3848, + "name": "CALLVALUE" + }, + { + "begin": 3719, + "end": 3848, + "name": "ISZERO" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH [tag]", + "value": "41" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPI" + }, + { + "begin": 3719, + "end": 3848, + "name": "INVALID" + }, + { + "begin": 3719, + "end": 3848, + "name": "tag", + "value": "41" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPDEST" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH [tag]", + "value": "21" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH", + "value": "4" + }, + { + "begin": 3719, + "end": 3848, + "name": "CALLDATALOAD" + }, + { + "begin": 3719, + "end": 3848, + "name": "DUP2" + }, + { + "begin": 3719, + "end": 3848, + "name": "AND" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP1" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH", + "value": "24" + }, + { + "begin": 3719, + "end": 3848, + "name": "CALLDATALOAD" + }, + { + "begin": 3719, + "end": 3848, + "name": "AND" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH [tag]", + "value": "43" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMP" + }, + { + "begin": 3719, + "end": 3848, + "name": "tag", + "value": "42" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPDEST" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3719, + "end": 3848, + "name": "DUP1" + }, + { + "begin": 3719, + "end": 3848, + "name": "MLOAD" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP2" + }, + { + "begin": 3719, + "end": 3848, + "name": "DUP3" + }, + { + "begin": 3719, + "end": 3848, + "name": "MSTORE" + }, + { + "begin": 3719, + "end": 3848, + "name": "MLOAD" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP1" + }, + { + "begin": 3719, + "end": 3848, + "name": "DUP2" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP1" + }, + { + "begin": 3719, + "end": 3848, + "name": "SUB" + }, + { + "begin": 3719, + "end": 3848, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3719, + "end": 3848, + "name": "ADD" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP1" + }, + { + "begin": 3719, + "end": 3848, + "name": "RETURN" + }, + { + "begin": 5109, + "end": 5158, + "name": "tag", + "value": "13" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMPDEST" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "MLOAD" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP3" + }, + { + "begin": 5109, + "end": 5158, + "name": "ADD" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP1" + }, + { + "begin": 5109, + "end": 5158, + "name": "SWAP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "MSTORE" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "11" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "MSTORE" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "30782050726F746F636F6C20546F6B656E000000000000000000000000000000" + }, + { + "begin": 5109, + "end": 5158, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP3" + }, + { + "begin": 5109, + "end": 5158, + "name": "ADD" + }, + { + "begin": 5109, + "end": 5158, + "name": "MSTORE" + }, + { + "begin": 5109, + "end": 5158, + "name": "DUP2" + }, + { + "begin": 5109, + "end": 5158, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 3523, + "end": 3713, + "name": "tag", + "value": "19" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPDEST" + }, + { + "begin": 3599, + "end": 3618, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3607, + "end": 3617, + "name": "CALLER" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP2" + }, + { + "begin": 3599, + "end": 3618, + "name": "AND" + }, + { + "begin": 3583, + "end": 3587, + "name": "PUSH", + "value": "0" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP2" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP2" + }, + { + "begin": 3599, + "end": 3618, + "name": "MSTORE" + }, + { + "begin": 3599, + "end": 3606, + "name": "PUSH", + "value": "1" + }, + { + "begin": 3599, + "end": 3618, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3599, + "end": 3618, + "name": "SWAP1" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP2" + }, + { + "begin": 3599, + "end": 3618, + "name": "MSTORE" + }, + { + "begin": 3599, + "end": 3618, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP1" + }, + { + "begin": 3599, + "end": 3618, + "name": "DUP4" + }, + { + "begin": 3599, + "end": 3618, + "name": "SHA3" + }, + { + "begin": 3599, + "end": 3628, + "name": "SWAP5" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP8" + }, + { + "begin": 3599, + "end": 3628, + "name": "AND" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP1" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP5" + }, + { + "begin": 3599, + "end": 3628, + "name": "MSTORE" + }, + { + "begin": 3599, + "end": 3628, + "name": "SWAP5" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP3" + }, + { + "begin": 3599, + "end": 3628, + "name": "MSTORE" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP1" + }, + { + "begin": 3599, + "end": 3628, + "name": "DUP4" + }, + { + "begin": 3599, + "end": 3628, + "name": "SHA3" + }, + { + "begin": 3599, + "end": 3637, + "name": "DUP7" + }, + { + "begin": 3599, + "end": 3637, + "name": "SWAP1" + }, + { + "begin": 3599, + "end": 3637, + "name": "SSTORE" + }, + { + "begin": 3647, + "end": 3685, + "name": "DUP1" + }, + { + "begin": 3647, + "end": 3685, + "name": "MLOAD" + }, + { + "begin": 3647, + "end": 3685, + "name": "DUP7" + }, + { + "begin": 3647, + "end": 3685, + "name": "DUP2" + }, + { + "begin": 3647, + "end": 3685, + "name": "MSTORE" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP1" + }, + { + "begin": 3647, + "end": 3685, + "name": "MLOAD" + }, + { + "begin": 3583, + "end": 3587, + "name": "SWAP3" + }, + { + "begin": 3583, + "end": 3587, + "name": "SWAP5" + }, + { + "begin": 3599, + "end": 3628, + "name": "SWAP4" + }, + { + "begin": 3599, + "end": 3618, + "name": "SWAP3" + }, + { + "begin": 3647, + "end": 3685, + "name": "PUSH", + "value": "8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP3" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP2" + }, + { + "begin": 3647, + "end": 3685, + "name": "DUP2" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP1" + }, + { + "begin": 3647, + "end": 3685, + "name": "SUB" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP1" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP2" + }, + { + "begin": 3647, + "end": 3685, + "name": "ADD" + }, + { + "begin": 3647, + "end": 3685, + "name": "SWAP1" + }, + { + "begin": 3647, + "end": 3685, + "name": "LOG3" + }, + { + "begin": -1, + "end": -1, + "name": "POP" + }, + { + "begin": 3702, + "end": 3706, + "name": "PUSH", + "value": "1" + }, + { + "begin": 3523, + "end": 3713, + "name": "tag", + "value": "44" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMPDEST" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP3" + }, + { + "begin": 3523, + "end": 3713, + "name": "SWAP2" + }, + { + "begin": 3523, + "end": 3713, + "name": "POP" + }, + { + "begin": 3523, + "end": 3713, + "name": "POP" + }, + { + "begin": 3523, + "end": 3713, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 5027, + "end": 5064, + "name": "tag", + "value": "22" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMPDEST" + }, + { + "begin": 5027, + "end": 5064, + "name": "PUSH", + "value": "3" + }, + { + "begin": 5027, + "end": 5064, + "name": "SLOAD" + }, + { + "begin": 5027, + "end": 5064, + "name": "DUP2" + }, + { + "begin": 5027, + "end": 5064, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 4369, + "end": 4931, + "name": "tag", + "value": "25" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPDEST" + }, + { + "begin": 4487, + "end": 4501, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP1" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP5" + }, + { + "begin": 4487, + "end": 4501, + "name": "AND" + }, + { + "begin": 4451, + "end": 4455, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP2" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP2" + }, + { + "begin": 4487, + "end": 4501, + "name": "MSTORE" + }, + { + "begin": 4487, + "end": 4494, + "name": "PUSH", + "value": "1" + }, + { + "begin": 4487, + "end": 4501, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4487, + "end": 4501, + "name": "SWAP1" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP2" + }, + { + "begin": 4487, + "end": 4501, + "name": "MSTORE" + }, + { + "begin": 4487, + "end": 4501, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP1" + }, + { + "begin": 4487, + "end": 4501, + "name": "DUP4" + }, + { + "begin": 4487, + "end": 4501, + "name": "SHA3" + }, + { + "begin": 4502, + "end": 4512, + "name": "CALLER" + }, + { + "begin": 4487, + "end": 4513, + "name": "SWAP1" + }, + { + "begin": 4487, + "end": 4513, + "name": "SWAP6" + }, + { + "begin": 4487, + "end": 4513, + "name": "AND" + }, + { + "begin": 4487, + "end": 4513, + "name": "DUP4" + }, + { + "begin": 4487, + "end": 4513, + "name": "MSTORE" + }, + { + "begin": 4487, + "end": 4513, + "name": "SWAP4" + }, + { + "begin": 4487, + "end": 4513, + "name": "DUP2" + }, + { + "begin": 4487, + "end": 4513, + "name": "MSTORE" + }, + { + "begin": 4487, + "end": 4513, + "name": "DUP4" + }, + { + "begin": 4487, + "end": 4513, + "name": "DUP3" + }, + { + "begin": 4487, + "end": 4513, + "name": "SHA3" + }, + { + "begin": 4487, + "end": 4513, + "name": "SLOAD" + }, + { + "begin": 4527, + "end": 4542, + "name": "SWAP3" + }, + { + "begin": 4527, + "end": 4542, + "name": "DUP3" + }, + { + "begin": 4527, + "end": 4542, + "name": "MSTORE" + }, + { + "begin": 4527, + "end": 4542, + "name": "DUP2" + }, + { + "begin": 4527, + "end": 4542, + "name": "SWAP1" + }, + { + "begin": 4527, + "end": 4542, + "name": "MSTORE" + }, + { + "begin": 4527, + "end": 4542, + "name": "SWAP2" + }, + { + "begin": 4527, + "end": 4542, + "name": "DUP3" + }, + { + "begin": 4527, + "end": 4542, + "name": "SHA3" + }, + { + "begin": 4527, + "end": 4542, + "name": "SLOAD" + }, + { + "begin": 4527, + "end": 4552, + "name": "DUP4" + }, + { + "begin": 4527, + "end": 4552, + "name": "SWAP1" + }, + { + "begin": 4527, + "end": 4552, + "name": "LT" + }, + { + "begin": 4527, + "end": 4552, + "name": "DUP1" + }, + { + "begin": 4527, + "end": 4552, + "name": "ISZERO" + }, + { + "begin": 4527, + "end": 4552, + "name": "SWAP1" + }, + { + "begin": 4527, + "end": 4575, + "name": "PUSH [tag]", + "value": "46" + }, + { + "begin": 4527, + "end": 4575, + "name": "JUMPI" + }, + { + "begin": 4527, + "end": 4575, + "name": "POP" + }, + { + "begin": 4569, + "end": 4575, + "name": "DUP3" + }, + { + "begin": 4556, + "end": 4565, + "name": "DUP2" + }, + { + "begin": 4556, + "end": 4575, + "name": "LT" + }, + { + "begin": 4556, + "end": 4575, + "name": "ISZERO" + }, + { + "begin": 4527, + "end": 4575, + "name": "tag", + "value": "46" + }, + { + "begin": 4527, + "end": 4575, + "name": "JUMPDEST" + }, + { + "begin": 4527, + "end": 4618, + "name": "DUP1" + }, + { + "begin": 4527, + "end": 4618, + "name": "ISZERO" + }, + { + "begin": 4527, + "end": 4618, + "name": "PUSH [tag]", + "value": "47" + }, + { + "begin": 4527, + "end": 4618, + "name": "JUMPI" + }, + { + "begin": -1, + "end": -1, + "name": "POP" + }, + { + "begin": 4605, + "end": 4618, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4605, + "end": 4618, + "name": "DUP5" + }, + { + "begin": 4605, + "end": 4618, + "name": "AND" + }, + { + "begin": 4605, + "end": 4613, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4605, + "end": 4618, + "name": "SWAP1" + }, + { + "begin": 4605, + "end": 4618, + "name": "DUP2" + }, + { + "begin": 4605, + "end": 4618, + "name": "MSTORE" + }, + { + "begin": 4605, + "end": 4618, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4605, + "end": 4618, + "name": "DUP2" + }, + { + "begin": 4605, + "end": 4618, + "name": "SWAP1" + }, + { + "begin": 4605, + "end": 4618, + "name": "MSTORE" + }, + { + "begin": 4605, + "end": 4618, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4605, + "end": 4618, + "name": "SWAP1" + }, + { + "begin": 4605, + "end": 4618, + "name": "SHA3" + }, + { + "begin": 4605, + "end": 4618, + "name": "SLOAD" + }, + { + "begin": 4579, + "end": 4601, + "name": "DUP4" + }, + { + "begin": 4579, + "end": 4601, + "name": "DUP2" + }, + { + "begin": 4579, + "end": 4601, + "name": "ADD" + }, + { + "begin": 4579, + "end": 4618, + "name": "LT" + }, + { + "begin": 4579, + "end": 4618, + "name": "ISZERO" + }, + { + "begin": 4527, + "end": 4618, + "name": "tag", + "value": "47" + }, + { + "begin": 4527, + "end": 4618, + "name": "JUMPDEST" + }, + { + "begin": 4523, + "end": 4925, + "name": "ISZERO" + }, + { + "begin": 4523, + "end": 4925, + "name": "PUSH [tag]", + "value": "48" + }, + { + "begin": 4523, + "end": 4925, + "name": "JUMPI" + }, + { + "begin": 4634, + "end": 4647, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP1" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP6" + }, + { + "begin": 4634, + "end": 4647, + "name": "AND" + }, + { + "begin": 4634, + "end": 4642, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4634, + "end": 4647, + "name": "SWAP1" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP2" + }, + { + "begin": 4634, + "end": 4647, + "name": "MSTORE" + }, + { + "begin": 4634, + "end": 4647, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP2" + }, + { + "begin": 4634, + "end": 4647, + "name": "SWAP1" + }, + { + "begin": 4634, + "end": 4647, + "name": "MSTORE" + }, + { + "begin": 4634, + "end": 4647, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP1" + }, + { + "begin": 4634, + "end": 4647, + "name": "DUP3" + }, + { + "begin": 4634, + "end": 4647, + "name": "SHA3" + }, + { + "begin": 4634, + "end": 4657, + "name": "DUP1" + }, + { + "begin": 4634, + "end": 4657, + "name": "SLOAD" + }, + { + "begin": 4634, + "end": 4657, + "name": "DUP8" + }, + { + "begin": 4634, + "end": 4657, + "name": "ADD" + }, + { + "begin": 4634, + "end": 4657, + "name": "SWAP1" + }, + { + "begin": 4634, + "end": 4657, + "name": "SSTORE" + }, + { + "begin": 4671, + "end": 4686, + "name": "SWAP2" + }, + { + "begin": 4671, + "end": 4686, + "name": "DUP8" + }, + { + "begin": 4671, + "end": 4686, + "name": "AND" + }, + { + "begin": 4671, + "end": 4686, + "name": "DUP2" + }, + { + "begin": 4671, + "end": 4686, + "name": "MSTORE" + }, + { + "begin": 4671, + "end": 4686, + "name": "SHA3" + }, + { + "begin": 4671, + "end": 4696, + "name": "DUP1" + }, + { + "begin": 4671, + "end": 4696, + "name": "SLOAD" + }, + { + "begin": 4671, + "end": 4696, + "name": "DUP5" + }, + { + "begin": 4671, + "end": 4696, + "name": "SWAP1" + }, + { + "begin": 4671, + "end": 4696, + "name": "SUB" + }, + { + "begin": 4671, + "end": 4696, + "name": "SWAP1" + }, + { + "begin": 4671, + "end": 4696, + "name": "SSTORE" + }, + { + "begin": 4069, + "end": 4081, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4714, + "end": 4734, + "name": "DUP2" + }, + { + "begin": 4714, + "end": 4734, + "name": "LT" + }, + { + "begin": 4710, + "end": 4805, + "name": "ISZERO" + }, + { + "begin": 4710, + "end": 4805, + "name": "PUSH [tag]", + "value": "49" + }, + { + "begin": 4710, + "end": 4805, + "name": "JUMPI" + }, + { + "begin": 4754, + "end": 4768, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP1" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP7" + }, + { + "begin": 4754, + "end": 4768, + "name": "AND" + }, + { + "begin": 4754, + "end": 4768, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4754, + "end": 4768, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP2" + }, + { + "begin": 4754, + "end": 4768, + "name": "MSTORE" + }, + { + "begin": 4754, + "end": 4761, + "name": "PUSH", + "value": "1" + }, + { + "begin": 4754, + "end": 4768, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4754, + "end": 4768, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP2" + }, + { + "begin": 4754, + "end": 4768, + "name": "MSTORE" + }, + { + "begin": 4754, + "end": 4768, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP1" + }, + { + "begin": 4754, + "end": 4768, + "name": "DUP4" + }, + { + "begin": 4754, + "end": 4768, + "name": "SHA3" + }, + { + "begin": 4769, + "end": 4779, + "name": "CALLER" + }, + { + "begin": 4754, + "end": 4780, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4780, + "name": "SWAP5" + }, + { + "begin": 4754, + "end": 4780, + "name": "AND" + }, + { + "begin": 4754, + "end": 4780, + "name": "DUP4" + }, + { + "begin": 4754, + "end": 4780, + "name": "MSTORE" + }, + { + "begin": 4754, + "end": 4780, + "name": "SWAP3" + }, + { + "begin": 4754, + "end": 4780, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4780, + "name": "MSTORE" + }, + { + "begin": 4754, + "end": 4780, + "name": "SHA3" + }, + { + "begin": 4754, + "end": 4790, + "name": "DUP1" + }, + { + "begin": 4754, + "end": 4790, + "name": "SLOAD" + }, + { + "begin": 4754, + "end": 4790, + "name": "DUP5" + }, + { + "begin": 4754, + "end": 4790, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4790, + "name": "SUB" + }, + { + "begin": 4754, + "end": 4790, + "name": "SWAP1" + }, + { + "begin": 4754, + "end": 4790, + "name": "SSTORE" + }, + { + "begin": 4710, + "end": 4805, + "name": "tag", + "value": "49" + }, + { + "begin": 4710, + "end": 4805, + "name": "JUMPDEST" + }, + { + "begin": 4834, + "end": 4837, + "name": "DUP4" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4818, + "end": 4846, + "name": "AND" + }, + { + "begin": 4827, + "end": 4832, + "name": "DUP6" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 4818, + "end": 4846, + "name": "AND" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF" + }, + { + "begin": 4839, + "end": 4845, + "name": "DUP6" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4818, + "end": 4846, + "name": "MLOAD" + }, + { + "begin": 4818, + "end": 4846, + "name": "DUP1" + }, + { + "begin": 4818, + "end": 4846, + "name": "DUP3" + }, + { + "begin": 4818, + "end": 4846, + "name": "DUP2" + }, + { + "begin": 4818, + "end": 4846, + "name": "MSTORE" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "20" + }, + { + "begin": 4818, + "end": 4846, + "name": "ADD" + }, + { + "begin": 4818, + "end": 4846, + "name": "SWAP2" + }, + { + "begin": 4818, + "end": 4846, + "name": "POP" + }, + { + "begin": 4818, + "end": 4846, + "name": "POP" + }, + { + "begin": 4818, + "end": 4846, + "name": "PUSH", + "value": "40" + }, + { + "begin": 4818, + "end": 4846, + "name": "MLOAD" + }, + { + "begin": 4818, + "end": 4846, + "name": "DUP1" + }, + { + "begin": 4818, + "end": 4846, + "name": "SWAP2" + }, + { + "begin": 4818, + "end": 4846, + "name": "SUB" + }, + { + "begin": 4818, + "end": 4846, + "name": "SWAP1" + }, + { + "begin": 4818, + "end": 4846, + "name": "LOG3" + }, + { + "begin": 4867, + "end": 4871, + "name": "PUSH", + "value": "1" + }, + { + "begin": 4860, + "end": 4871, + "name": "SWAP2" + }, + { + "begin": 4860, + "end": 4871, + "name": "POP" + }, + { + "begin": 4860, + "end": 4871, + "name": "PUSH [tag]", + "value": "50" + }, + { + "begin": 4860, + "end": 4871, + "name": "JUMP" + }, + { + "begin": 4523, + "end": 4925, + "name": "tag", + "value": "48" + }, + { + "begin": 4523, + "end": 4925, + "name": "JUMPDEST" + }, + { + "begin": 4909, + "end": 4914, + "name": "PUSH", + "value": "0" + }, + { + "begin": 4902, + "end": 4914, + "name": "SWAP2" + }, + { + "begin": 4902, + "end": 4914, + "name": "POP" + }, + { + "begin": 4523, + "end": 4925, + "name": "tag", + "value": "50" + }, + { + "begin": 4523, + "end": 4925, + "name": "JUMPDEST" + }, + { + "begin": 4369, + "end": 4931, + "name": "tag", + "value": "45" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMPDEST" + }, + { + "begin": 4369, + "end": 4931, + "name": "POP" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP4" + }, + { + "begin": 4369, + "end": 4931, + "name": "SWAP3" + }, + { + "begin": 4369, + "end": 4931, + "name": "POP" + }, + { + "begin": 4369, + "end": 4931, + "name": "POP" + }, + { + "begin": 4369, + "end": 4931, + "name": "POP" + }, + { + "begin": 4369, + "end": 4931, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 4986, + "end": 5021, + "name": "tag", + "value": "28" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMPDEST" + }, + { + "begin": 5019, + "end": 5021, + "name": "PUSH", + "value": "12" + }, + { + "begin": 4986, + "end": 5021, + "name": "DUP2" + }, + { + "begin": 4986, + "end": 5021, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 3415, + "end": 3517, + "name": "tag", + "value": "31" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPDEST" + }, + { + "begin": 3494, + "end": 3510, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3494, + "end": 3510, + "name": "DUP2" + }, + { + "begin": 3494, + "end": 3510, + "name": "AND" + }, + { + "begin": 3468, + "end": 3475, + "name": "PUSH", + "value": "0" + }, + { + "begin": 3494, + "end": 3510, + "name": "SWAP1" + }, + { + "begin": 3494, + "end": 3510, + "name": "DUP2" + }, + { + "begin": 3494, + "end": 3510, + "name": "MSTORE" + }, + { + "begin": 3494, + "end": 3510, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3494, + "end": 3510, + "name": "DUP2" + }, + { + "begin": 3494, + "end": 3510, + "name": "SWAP1" + }, + { + "begin": 3494, + "end": 3510, + "name": "MSTORE" + }, + { + "begin": 3494, + "end": 3510, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3494, + "end": 3510, + "name": "SWAP1" + }, + { + "begin": 3494, + "end": 3510, + "name": "SHA3" + }, + { + "begin": 3494, + "end": 3510, + "name": "SLOAD" + }, + { + "begin": 3415, + "end": 3517, + "name": "tag", + "value": "51" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMPDEST" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP2" + }, + { + "begin": 3415, + "end": 3517, + "name": "SWAP1" + }, + { + "begin": 3415, + "end": 3517, + "name": "POP" + }, + { + "begin": 3415, + "end": 3517, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 5164, + "end": 5201, + "name": "tag", + "value": "34" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMPDEST" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "40" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "MLOAD" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP3" + }, + { + "begin": 5164, + "end": 5201, + "name": "ADD" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP1" + }, + { + "begin": 5164, + "end": 5201, + "name": "SWAP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "MSTORE" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "3" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "MSTORE" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "5A52580000000000000000000000000000000000000000000000000000000000" + }, + { + "begin": 5164, + "end": 5201, + "name": "PUSH", + "value": "20" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP3" + }, + { + "begin": 5164, + "end": 5201, + "name": "ADD" + }, + { + "begin": 5164, + "end": 5201, + "name": "MSTORE" + }, + { + "begin": 5164, + "end": 5201, + "name": "DUP2" + }, + { + "begin": 5164, + "end": 5201, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 2490, + "end": 2923, + "name": "tag", + "value": "40" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPDEST" + }, + { + "begin": 2635, + "end": 2655, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 2644, + "end": 2654, + "name": "CALLER" + }, + { + "begin": 2635, + "end": 2655, + "name": "AND" + }, + { + "begin": 2546, + "end": 2550, + "name": "PUSH", + "value": "0" + }, + { + "begin": 2635, + "end": 2655, + "name": "SWAP1" + }, + { + "begin": 2635, + "end": 2655, + "name": "DUP2" + }, + { + "begin": 2635, + "end": 2655, + "name": "MSTORE" + }, + { + "begin": 2635, + "end": 2655, + "name": "PUSH", + "value": "20" + }, + { + "begin": 2635, + "end": 2655, + "name": "DUP2" + }, + { + "begin": 2635, + "end": 2655, + "name": "SWAP1" + }, + { + "begin": 2635, + "end": 2655, + "name": "MSTORE" + }, + { + "begin": 2635, + "end": 2655, + "name": "PUSH", + "value": "40" + }, + { + "begin": 2635, + "end": 2655, + "name": "DUP2" + }, + { + "begin": 2635, + "end": 2655, + "name": "SHA3" + }, + { + "begin": 2635, + "end": 2655, + "name": "SLOAD" + }, + { + "begin": 2635, + "end": 2665, + "name": "DUP3" + }, + { + "begin": 2635, + "end": 2665, + "name": "SWAP1" + }, + { + "begin": 2635, + "end": 2665, + "name": "LT" + }, + { + "begin": 2635, + "end": 2665, + "name": "DUP1" + }, + { + "begin": 2635, + "end": 2665, + "name": "ISZERO" + }, + { + "begin": 2635, + "end": 2665, + "name": "SWAP1" + }, + { + "begin": 2635, + "end": 2708, + "name": "PUSH [tag]", + "value": "53" + }, + { + "begin": 2635, + "end": 2708, + "name": "JUMPI" + }, + { + "begin": -1, + "end": -1, + "name": "POP" + }, + { + "begin": 2695, + "end": 2708, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 2695, + "end": 2708, + "name": "DUP4" + }, + { + "begin": 2695, + "end": 2708, + "name": "AND" + }, + { + "begin": 2695, + "end": 2703, + "name": "PUSH", + "value": "0" + }, + { + "begin": 2695, + "end": 2708, + "name": "SWAP1" + }, + { + "begin": 2695, + "end": 2708, + "name": "DUP2" + }, + { + "begin": 2695, + "end": 2708, + "name": "MSTORE" + }, + { + "begin": 2695, + "end": 2708, + "name": "PUSH", + "value": "20" + }, + { + "begin": 2695, + "end": 2708, + "name": "DUP2" + }, + { + "begin": 2695, + "end": 2708, + "name": "SWAP1" + }, + { + "begin": 2695, + "end": 2708, + "name": "MSTORE" + }, + { + "begin": 2695, + "end": 2708, + "name": "PUSH", + "value": "40" + }, + { + "begin": 2695, + "end": 2708, + "name": "SWAP1" + }, + { + "begin": 2695, + "end": 2708, + "name": "SHA3" + }, + { + "begin": 2695, + "end": 2708, + "name": "SLOAD" + }, + { + "begin": 2669, + "end": 2691, + "name": "DUP3" + }, + { + "begin": 2669, + "end": 2691, + "name": "DUP2" + }, + { + "begin": 2669, + "end": 2691, + "name": "ADD" + }, + { + "begin": 2669, + "end": 2708, + "name": "LT" + }, + { + "begin": 2669, + "end": 2708, + "name": "ISZERO" + }, + { + "begin": 2635, + "end": 2708, + "name": "tag", + "value": "53" + }, + { + "begin": 2635, + "end": 2708, + "name": "JUMPDEST" + }, + { + "begin": 2631, + "end": 2917, + "name": "ISZERO" + }, + { + "begin": 2631, + "end": 2917, + "name": "PUSH [tag]", + "value": "54" + }, + { + "begin": 2631, + "end": 2917, + "name": "JUMPI" + }, + { + "begin": 2724, + "end": 2744, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 2733, + "end": 2743, + "name": "CALLER" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP2" + }, + { + "begin": 2724, + "end": 2744, + "name": "AND" + }, + { + "begin": 2724, + "end": 2732, + "name": "PUSH", + "value": "0" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP2" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP2" + }, + { + "begin": 2724, + "end": 2744, + "name": "MSTORE" + }, + { + "begin": 2724, + "end": 2744, + "name": "PUSH", + "value": "20" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP2" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP2" + }, + { + "begin": 2724, + "end": 2744, + "name": "MSTORE" + }, + { + "begin": 2724, + "end": 2744, + "name": "PUSH", + "value": "40" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP1" + }, + { + "begin": 2724, + "end": 2744, + "name": "DUP4" + }, + { + "begin": 2724, + "end": 2744, + "name": "SHA3" + }, + { + "begin": 2724, + "end": 2754, + "name": "DUP1" + }, + { + "begin": 2724, + "end": 2754, + "name": "SLOAD" + }, + { + "begin": 2724, + "end": 2754, + "name": "DUP9" + }, + { + "begin": 2724, + "end": 2754, + "name": "SWAP1" + }, + { + "begin": 2724, + "end": 2754, + "name": "SUB" + }, + { + "begin": 2724, + "end": 2754, + "name": "SWAP1" + }, + { + "begin": 2724, + "end": 2754, + "name": "SSTORE" + }, + { + "begin": 2768, + "end": 2781, + "name": "SWAP4" + }, + { + "begin": 2768, + "end": 2781, + "name": "DUP8" + }, + { + "begin": 2768, + "end": 2781, + "name": "AND" + }, + { + "begin": 2768, + "end": 2781, + "name": "DUP1" + }, + { + "begin": 2768, + "end": 2781, + "name": "DUP4" + }, + { + "begin": 2768, + "end": 2781, + "name": "MSTORE" + }, + { + "begin": 2768, + "end": 2781, + "name": "SWAP2" + }, + { + "begin": 2768, + "end": 2781, + "name": "DUP5" + }, + { + "begin": 2768, + "end": 2781, + "name": "SWAP1" + }, + { + "begin": 2768, + "end": 2781, + "name": "SHA3" + }, + { + "begin": 2768, + "end": 2791, + "name": "DUP1" + }, + { + "begin": 2768, + "end": 2791, + "name": "SLOAD" + }, + { + "begin": 2768, + "end": 2791, + "name": "DUP8" + }, + { + "begin": 2768, + "end": 2791, + "name": "ADD" + }, + { + "begin": 2768, + "end": 2791, + "name": "SWAP1" + }, + { + "begin": 2768, + "end": 2791, + "name": "SSTORE" + }, + { + "begin": 2805, + "end": 2838, + "name": "DUP4" + }, + { + "begin": 2805, + "end": 2838, + "name": "MLOAD" + }, + { + "begin": 2805, + "end": 2838, + "name": "DUP7" + }, + { + "begin": 2805, + "end": 2838, + "name": "DUP2" + }, + { + "begin": 2805, + "end": 2838, + "name": "MSTORE" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP4" + }, + { + "begin": 2805, + "end": 2838, + "name": "MLOAD" + }, + { + "begin": 2768, + "end": 2781, + "name": "SWAP2" + }, + { + "begin": 2768, + "end": 2781, + "name": "SWAP4" + }, + { + "begin": 2805, + "end": 2838, + "name": "PUSH", + "value": "DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP3" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP1" + }, + { + "begin": 2805, + "end": 2838, + "name": "DUP2" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP1" + }, + { + "begin": 2805, + "end": 2838, + "name": "SUB" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP1" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP2" + }, + { + "begin": 2805, + "end": 2838, + "name": "ADD" + }, + { + "begin": 2805, + "end": 2838, + "name": "SWAP1" + }, + { + "begin": 2805, + "end": 2838, + "name": "LOG3" + }, + { + "begin": -1, + "end": -1, + "name": "POP" + }, + { + "begin": 2859, + "end": 2863, + "name": "PUSH", + "value": "1" + }, + { + "begin": 2852, + "end": 2863, + "name": "PUSH [tag]", + "value": "44" + }, + { + "begin": 2852, + "end": 2863, + "name": "JUMP" + }, + { + "begin": 2631, + "end": 2917, + "name": "tag", + "value": "54" + }, + { + "begin": 2631, + "end": 2917, + "name": "JUMPDEST" + }, + { + "begin": -1, + "end": -1, + "name": "POP" + }, + { + "begin": 2901, + "end": 2906, + "name": "PUSH", + "value": "0" + }, + { + "begin": 2894, + "end": 2906, + "name": "PUSH [tag]", + "value": "44" + }, + { + "begin": 2894, + "end": 2906, + "name": "JUMP" + }, + { + "begin": 2631, + "end": 2917, + "name": "tag", + "value": "55" + }, + { + "begin": 2631, + "end": 2917, + "name": "JUMPDEST" + }, + { + "begin": 2490, + "end": 2923, + "name": "tag", + "value": "52" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMPDEST" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP3" + }, + { + "begin": 2490, + "end": 2923, + "name": "SWAP2" + }, + { + "begin": 2490, + "end": 2923, + "name": "POP" + }, + { + "begin": 2490, + "end": 2923, + "name": "POP" + }, + { + "begin": 2490, + "end": 2923, + "name": "JUMP", + "value": "[out]" + }, + { + "begin": 3719, + "end": 3848, + "name": "tag", + "value": "43" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPDEST" + }, + { + "begin": 3816, + "end": 3831, + "name": "PUSH", + "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP1" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP4" + }, + { + "begin": 3816, + "end": 3831, + "name": "AND" + }, + { + "begin": 3790, + "end": 3797, + "name": "PUSH", + "value": "0" + }, + { + "begin": 3816, + "end": 3831, + "name": "SWAP1" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP2" + }, + { + "begin": 3816, + "end": 3831, + "name": "MSTORE" + }, + { + "begin": 3816, + "end": 3823, + "name": "PUSH", + "value": "1" + }, + { + "begin": 3816, + "end": 3831, + "name": "PUSH", + "value": "20" + }, + { + "begin": 3816, + "end": 3831, + "name": "SWAP1" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP2" + }, + { + "begin": 3816, + "end": 3831, + "name": "MSTORE" + }, + { + "begin": 3816, + "end": 3831, + "name": "PUSH", + "value": "40" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP1" + }, + { + "begin": 3816, + "end": 3831, + "name": "DUP4" + }, + { + "begin": 3816, + "end": 3831, + "name": "SHA3" + }, + { + "begin": 3816, + "end": 3841, + "name": "SWAP4" + }, + { + "begin": 3816, + "end": 3841, + "name": "DUP6" + }, + { + "begin": 3816, + "end": 3841, + "name": "AND" + }, + { + "begin": 3816, + "end": 3841, + "name": "DUP4" + }, + { + "begin": 3816, + "end": 3841, + "name": "MSTORE" + }, + { + "begin": 3816, + "end": 3841, + "name": "SWAP3" + }, + { + "begin": 3816, + "end": 3841, + "name": "SWAP1" + }, + { + "begin": 3816, + "end": 3841, + "name": "MSTORE" + }, + { + "begin": 3816, + "end": 3841, + "name": "SHA3" + }, + { + "begin": 3816, + "end": 3841, + "name": "SLOAD" + }, + { + "begin": 3719, + "end": 3848, + "name": "tag", + "value": "56" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMPDEST" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP3" + }, + { + "begin": 3719, + "end": 3848, + "name": "SWAP2" + }, + { + "begin": 3719, + "end": 3848, + "name": "POP" + }, + { + "begin": 3719, + "end": 3848, + "name": "POP" + }, + { + "begin": 3719, + "end": 3848, + "name": "JUMP", + "value": "[out]" + } + ] + } + } + }, + "methodIdentifiers": { + "allowance(address,address)": "dd62ed3e", + "approve(address,uint256)": "095ea7b3", + "balanceOf(address)": "70a08231", + "decimals()": "313ce567", + "name()": "06fdde03", + "symbol()": "95d89b41", + "totalSupply()": "18160ddd", + "transfer(address,uint256)": "a9059cbb", + "transferFrom(address,address,uint256)": "23b872dd" + } + }, + "metadata": "{\"compiler\":{\"version\":\"0.4.11+commit.68ef5810\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}],\"devdoc\":{\"methods\":{\"transferFrom(address,address,uint256)\":{\"details\":\"ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\",\"params\":{\"_from\":\"Address to transfer from.\",\"_to\":\"Address to transfer to.\",\"_value\":\"Amount to transfer.\"},\"return\":\"Success of transfer.\"}}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":\"ZRXToken\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":@0x/contracts-utils=/Users/Elena/Source/protocol/contracts/erc20/node_modules/@0x/contracts-utils\"]},\"sources\":{\"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":{\"keccak256\":\"0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47\",\"urls\":[\"bzzr://ef728dddbaa1e26baa6cc9fe0f83de5055bc0b17dfe488018f4ee59d68ccb5dd\"]}},\"version\":1}", + "userdoc": { + "methods": {} + } + }, + "sourceTreeHashHex": "0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47", + "sources": { + "./ZRXToken.sol": { + "id": 0, + "content": "/*\n\n Copyright 2019 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.11;\n\ncontract Token {\n /// @return total amount of tokens\n function totalSupply() constant returns (uint256 supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint256 balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint256 _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint256 _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event Approval(address indexed _owner, address indexed _spender, uint256 _value);\n}\n\ncontract ERC20Token is Token {\n function transfer(address _to, uint256 _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function transferFrom(address _from, address _to, uint256 _value) returns (bool) {\n if (\n balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function balanceOf(address _owner) constant returns (uint256) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint256 _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint256) {\n return allowed[_owner][_spender];\n }\n\n mapping(address => uint256) balances;\n mapping(address => mapping(address => uint256)) allowed;\n uint256 public totalSupply;\n}\n\ncontract UnlimitedAllowanceToken is ERC20Token {\n uint256 constant MAX_UINT = 2 ** 256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {\n uint256 allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n uint8 public constant decimals = 18;\n uint256 public totalSupply = 10 ** 27; // 1 billion tokens, 18 decimal places\n string public constant name = \"0x Protocol Token\";\n string public constant symbol = \"ZRX\";\n\n function ZRXToken() public {\n balances[msg.sender] = totalSupply;\n }\n}\n" + } + }, + "sourceCodes": { + "./ZRXToken.sol": "/*\n\n Copyright 2019 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.11;\n\ncontract Token {\n /// @return total amount of tokens\n function totalSupply() constant returns (uint256 supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint256 balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint256 _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint256 _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event Approval(address indexed _owner, address indexed _spender, uint256 _value);\n}\n\ncontract ERC20Token is Token {\n function transfer(address _to, uint256 _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function transferFrom(address _from, address _to, uint256 _value) returns (bool) {\n if (\n balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function balanceOf(address _owner) constant returns (uint256) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint256 _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint256) {\n return allowed[_owner][_spender];\n }\n\n mapping(address => uint256) balances;\n mapping(address => mapping(address => uint256)) allowed;\n uint256 public totalSupply;\n}\n\ncontract UnlimitedAllowanceToken is ERC20Token {\n uint256 constant MAX_UINT = 2 ** 256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {\n uint256 allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n uint8 public constant decimals = 18;\n uint256 public totalSupply = 10 ** 27; // 1 billion tokens, 18 decimal places\n string public constant name = \"0x Protocol Token\";\n string public constant symbol = \"ZRX\";\n\n function ZRXToken() public {\n balances[msg.sender] = totalSupply;\n }\n}\n" + }, + "compiler": { + "name": "solc", + "version": "0.4.11+commit.68ef5810", + "settings": { + "remappings": [ + "@0x/contracts-utils=/Users/Elena/Source/protocol/contracts/erc20/node_modules/@0x/contracts-utils" + ], + "optimizer": { + "enabled": true, + "runs": 1000000, + "details": { + "yul": true, + "deduplicate": true, + "cse": true, + "constantOptimizer": true + } + }, + "outputSelection": { + "*": { + "*": [ + "abi", + "devdoc", + "evm.bytecode.object", + "evm.bytecode.sourceMap", + "evm.deployedBytecode.object", + "evm.deployedBytecode.sourceMap", + "devdoc" + ] + } + }, + "evmVersion": "istanbul" + } + }, + "chains": {} +} diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index 1a85d6e1bb..3660d4075e 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -2,4 +2,6 @@ src = 'src' out = 'out' libs = ['lib'] +fs_permissions = [{ access = "read", path = "./"}] +remappings = ['@openzeppelin/=./lib/openzeppelin-contracts/contracts/'] optimizer_runs = 20_000 diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 2d7e86218a..834ca78961 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -18,10 +18,10 @@ */ pragma solidity ^0.8.17; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Wrapper.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/token/ERC20/extensions/draft-ERC20Permit.sol"; +import "@openzeppelin/token/ERC20/extensions/ERC20Votes.sol"; +import "@openzeppelin/token/ERC20/extensions/ERC20Wrapper.sol"; contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper { constructor( diff --git a/contracts/governance/src/ZeroExGovernor.sol b/contracts/governance/src/ZeroExGovernor.sol index 1020dad0bb..95598d6f49 100644 --- a/contracts/governance/src/ZeroExGovernor.sol +++ b/contracts/governance/src/ZeroExGovernor.sol @@ -18,12 +18,12 @@ */ pragma solidity ^0.8.17; -import "@openzeppelin/contracts/governance/Governor.sol"; -import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol"; -import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol"; -import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol"; -import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol"; -import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol"; +import "@openzeppelin/governance/Governor.sol"; +import "@openzeppelin/governance/extensions/GovernorSettings.sol"; +import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; +import "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import "@openzeppelin/governance/extensions/GovernorVotesQuorumFraction.sol"; +import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; /// @custom:security-contact security@0xproject.com contract ZeroExGovernor is diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol index bb3867ff9b..31bf7a40b3 100644 --- a/contracts/governance/test/BaseTest.sol +++ b/contracts/governance/test/BaseTest.sol @@ -33,11 +33,12 @@ contract BaseTest is Test { } function createZRXToken() internal returns (address) { - bytes memory _bytecode = abi.encodePacked(vm.getCode("../../erc20/generated-artifacts/ZRXToken.json")); + bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); address _address; assembly { _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) } + console.log(address(_address)); return address(_address); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 3e9d17e934..081cdb256f 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -20,7 +20,7 @@ pragma solidity ^0.8.17; import "./BaseTest.sol"; import "../src/ZRXWrappedToken.sol"; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; contract ZRXWrappedTokenTest is BaseTest { IERC20 public token; @@ -29,19 +29,17 @@ contract ZRXWrappedTokenTest is BaseTest { function setUp() public { vm.startPrank(account1); - token = IERC20(createZRXToken()); token.transfer(account2, 100e18); token.transfer(account3, 100e18); wToken = new ZRXWrappedToken(token); - - vm.stopPrank(account1); + vm.stopPrank(); } function testShouldBeAbleToWrapZRX() public { vm.startPrank(account2); - token.approve(wToken.address, 1e18); + token.approve(address(wToken), 1e18); wToken.depositFor(account2, 1e18); } From 7546b9cf49ff3ac1b0bb2b98dfebb7378883e3b5 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 18:06:28 +0200 Subject: [PATCH 013/106] Temporarily switch to using a mocked version of ZRX --- contracts/governance/test/BaseTest.sol | 18 ++++++----- contracts/governance/test/ZRXMock.sol | 31 +++++++++++++++++++ .../governance/test/ZRXWrappedTokenTest.t.sol | 1 + 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 contracts/governance/test/ZRXMock.sol diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol index 31bf7a40b3..dcedf5f05b 100644 --- a/contracts/governance/test/BaseTest.sol +++ b/contracts/governance/test/BaseTest.sol @@ -20,6 +20,8 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; +import "./ZRXMock.sol"; contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); @@ -33,12 +35,14 @@ contract BaseTest is Test { } function createZRXToken() internal returns (address) { - bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); - address _address; - assembly { - _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) - } - console.log(address(_address)); - return address(_address); + // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved + // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); + // address _address; + // assembly { + // _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) + // } + // return address(_address); + + return address(new ZRXMock()); } } diff --git a/contracts/governance/test/ZRXMock.sol b/contracts/governance/test/ZRXMock.sol new file mode 100644 index 0000000000..9db1291172 --- /dev/null +++ b/contracts/governance/test/ZRXMock.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.8.17; + +import "forge-std/Test.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; + +// TODO remove this contract and work with an instance of ZRX compiled with 0.4 +// when the following is resolved https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry +contract ZRXMock is ERC20 { + constructor() ERC20("0x Protocol Token", "ZRX") { + _mint(msg.sender, 10 ** 27); + } +} diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 081cdb256f..654d1f8064 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -39,6 +39,7 @@ contract ZRXWrappedTokenTest is BaseTest { function testShouldBeAbleToWrapZRX() public { vm.startPrank(account2); + token.approve(address(wToken), 1e18); wToken.depositFor(account2, 1e18); } From 07610c8c987a792ddd145e9ed4e046065713b12a Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 18:25:27 +0200 Subject: [PATCH 014/106] Ignore foundry's lib in link checker --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 00a1744beb..4d135dd4a3 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "test:all": "wsrun --fast-exit --serial --exclude-missing -p $PKG -c test", "test:contracts": "wsrun --serial -p $(echo ${npm_package_config_contractsPackages} ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') --fast-exit --exclude-missing -c test", "test:contracts:all": "wsrun --serial -p ${npm_package_config_contractsPackages} --fast-exit --exclude-missing -c test", - "test:links": "yarn check-md --ignore **/forge-std/README.md,**/lib/openzeppelin-contracts,**/node_modules", + "test:links": "yarn check-md --ignore **/forge-std/README.md,**/lib/openzeppelin-contracts,**/node_modules,**/lib", "generate_doc": "node ./node_modules/@0x/monorepo-scripts/lib/doc_generate.js --config ./doc-gen-config.json", "upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun --exclude-missing -c s3:sync_md_docs", "diff_md_docs:ci": "wsrun --exclude-missing -c diff_docs", From de21d3dbbd4fff18404cfacd0f0d524695d28f5c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 19:09:56 +0200 Subject: [PATCH 015/106] Fix a conflict in gitignore between forge lib adn built lib --- .gitignore | 10 +++++++++- contracts/governance/lib/forge-std | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) create mode 160000 contracts/governance/lib/forge-std diff --git a/.gitignore b/.gitignore index 267e7785ae..8da666fbfb 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,15 @@ typings/ .env # built library using in commonjs module syntax -lib/ +contracts/erc20/lib/ +contracts/test-utils/lib/ +contracts/treasury/lib/ +contracts/utils/lib/ +contracts/zero-ex/lib/ +packages/contract-addresses/lib/ +packages/contract-artifacts/lib/ +packages/contract-wrappers/lib/ +packages/protocol-utils/lib/ # UMD bundles that export the global variable _bundles diff --git a/contracts/governance/lib/forge-std b/contracts/governance/lib/forge-std new file mode 160000 index 0000000000..eb980e1d4f --- /dev/null +++ b/contracts/governance/lib/forge-std @@ -0,0 +1 @@ +Subproject commit eb980e1d4f0e8173ec27da77297ae411840c8ccb From 7bf9dced64149a384b9a4684eb61061cae33ec63 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 19:37:59 +0200 Subject: [PATCH 016/106] Upload governance code coverage report to coveralls --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 68b67de317..f678069818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -152,5 +152,12 @@ jobs: run: | forge coverage --report lcov + - name: Upload the coverage report to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + base-path: ./contracts/governance/ + path-to-lcov: ./contracts/governance/lcov.info + From d80789c2cf27a21611f9166ac238c08f39485af1 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 19:58:15 +0200 Subject: [PATCH 017/106] Flesh out test scenarios for wrapping/unwrapping --- contracts/governance/test/BaseTest.sol | 2 + .../governance/test/ZRXWrappedTokenTest.t.sol | 45 ++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol index dcedf5f05b..0844a977ee 100644 --- a/contracts/governance/test/BaseTest.sol +++ b/contracts/governance/test/BaseTest.sol @@ -27,11 +27,13 @@ contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); address payable internal account2 = payable(vm.addr(2)); address payable internal account3 = payable(vm.addr(3)); + address payable internal account4 = payable(vm.addr(4)); constructor() public { vm.deal(account1, 1e20); vm.deal(account2, 1e20); vm.deal(account3, 1e20); + vm.deal(account4, 1e20); } function createZRXToken() internal returns (address) { diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 654d1f8064..0e131ca204 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -40,9 +40,52 @@ contract ZRXWrappedTokenTest is BaseTest { function testShouldBeAbleToWrapZRX() public { vm.startPrank(account2); + // Approve the wrapped token and deposit 1e18 ZRX token.approve(address(wToken), 1e18); wToken.depositFor(account2, 1e18); + + // Check the token balances even out + uint256 wTokenBalance = wToken.balanceOf(account2); + assertEq(wTokenBalance, 1e18); + uint256 tokenBalance = token.balanceOf(account2); + assertEq(tokenBalance, 100e18 - wTokenBalance); + } + + function testShouldBeAbleToUnwrapToZRX() public { + vm.startPrank(account2); + + // Approve the wrapped token and deposit 1e18 ZRX + token.approve(address(wToken), 1e18); + wToken.depositFor(account2, 1e18); + + // Withdraw 1e6 wZRX back to ZRX to own account + wToken.withdrawTo(account2, 1e6); + + // Check token balances even out + uint256 wTokenBalance = wToken.balanceOf(account2); + assertEq(wTokenBalance, 1e18 - 1e6); + uint256 tokenBalance = token.balanceOf(account2); + assertEq(tokenBalance, 100e18 - wTokenBalance); } - function testShouldBeAbleToUnwrapToZRX() public {} + function testShouldBeAbleToUnwrapToZRXToAnotherAccount() public { + vm.startPrank(account2); + + // Approve the wrapped token and deposit 1e18 ZRX + token.approve(address(wToken), 1e18); + wToken.depositFor(account2, 1e18); + + // Withdraw 1e7 wZRX back to ZRX to account4 (which owns no tokens to start with) + wToken.withdrawTo(account4, 1e7); + + // Check token balances even out + uint256 wTokenBalance2 = wToken.balanceOf(account2); + assertEq(wTokenBalance2, 1e18 - 1e7); + + uint256 tokenBalance4 = token.balanceOf(account4); + assertEq(tokenBalance4, 1e7); + + uint256 tokenBalance2 = token.balanceOf(account2); + assertEq(tokenBalance2, 100e18 - wTokenBalance2 - tokenBalance4); + } } From a36277fac1142603eabb4611eb5dd079195888d8 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 21 Jan 2023 20:16:52 +0200 Subject: [PATCH 018/106] Add basic ERC20 name and symbol tests --- .../governance/test/ZRXWrappedTokenTest.t.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 0e131ca204..4100c45689 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -37,6 +37,22 @@ contract ZRXWrappedTokenTest is BaseTest { vm.stopPrank(); } + function testShouldReturnCorrectSymbol() public { + string memory wZRXSymbol = wToken.symbol(); + assertEq(wZRXSymbol, "wZRX"); + } + + function testShouldReturnCorrectName() public { + string memory wZRXName = wToken.name(); + assertEq(wZRXName, "Wrapped ZRX"); + } + + function shouldReturnCorrectNumberOfDecimals() public { + // TODO: decimals is set to 0 + uint8 wZRXDecimals = wToken.decimals(); + assertEq(wZRXDecimals, 18); + } + function testShouldBeAbleToWrapZRX() public { vm.startPrank(account2); From 5433b8a2c66c68803e6973c683dd5686c9705f3c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 22 Jan 2023 16:11:40 +0200 Subject: [PATCH 019/106] Wire in basic timelock controller and governor test setup --- contracts/governance/src/ZeroExTimelock.sol | 33 +++++++++++++ .../governance/test/ZRXWrappedTokenTest.t.sol | 1 - .../governance/test/ZeroExGovernor.t.sol | 48 +++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 contracts/governance/src/ZeroExTimelock.sol create mode 100644 contracts/governance/test/ZeroExGovernor.t.sol diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol new file mode 100644 index 0000000000..152eb1abc5 --- /dev/null +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/governance/TimelockController.sol"; + +/// @custom:security-contact security@0xproject.com +contract ZeroExTimelock is TimelockController { + // minDelay is how long you have to wait before executing + // proposers is the list of addresses that can propose + // executors is the list of addresses that can execute + constructor( + uint256 minDelay, + address[] memory proposers, + address[] memory executors + ) TimelockController(minDelay, proposers, executors, address(0)) {} +} diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 4100c45689..d91fc52b38 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -25,7 +25,6 @@ import "@openzeppelin/token/ERC20/ERC20.sol"; contract ZRXWrappedTokenTest is BaseTest { IERC20 public token; ZRXWrappedToken public wToken; - address public voter1 = account1; function setUp() public { vm.startPrank(account1); diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExGovernor.t.sol new file mode 100644 index 0000000000..821d997e62 --- /dev/null +++ b/contracts/governance/test/ZeroExGovernor.t.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "./BaseTest.sol"; +import "../src/ZeroExTimelock.sol"; +import "../src/ZeroExGovernor.sol"; +import "../src/ZRXWrappedToken.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; + +contract ZeroExGovernorTest is BaseTest { + IERC20 public token; + ZRXWrappedToken public wToken; + ZeroExTimelock public zeroExTimelock; + ZeroExGovernor public zeroExGovernor; + + function setUp() public { + vm.startPrank(account1); + token = IERC20(createZRXToken()); + token.transfer(account2, 100e18); + token.transfer(account3, 200e18); + + wToken = new ZRXWrappedToken(token); + + address[] memory proposers = new address[](0); + address[] memory executors = new address[](0); + zeroExTimelock = new ZeroExTimelock(7 days, proposers, executors); + + zeroExGovernor = new ZeroExGovernor(wToken, zeroExTimelock); + vm.stopPrank(); + } +} From 4e41229a0c4431f5c8e1796f19ad859aae767bb3 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 22 Jan 2023 16:17:27 +0200 Subject: [PATCH 020/106] Test basic governor properties --- .../governance/test/ZeroExGovernor.t.sol | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExGovernor.t.sol index 821d997e62..601c027f7f 100644 --- a/contracts/governance/test/ZeroExGovernor.t.sol +++ b/contracts/governance/test/ZeroExGovernor.t.sol @@ -27,8 +27,8 @@ import "@openzeppelin/token/ERC20/ERC20.sol"; contract ZeroExGovernorTest is BaseTest { IERC20 public token; ZRXWrappedToken public wToken; - ZeroExTimelock public zeroExTimelock; - ZeroExGovernor public zeroExGovernor; + ZeroExTimelock public timelock; + ZeroExGovernor public governor; function setUp() public { vm.startPrank(account1); @@ -40,9 +40,37 @@ contract ZeroExGovernorTest is BaseTest { address[] memory proposers = new address[](0); address[] memory executors = new address[](0); - zeroExTimelock = new ZeroExTimelock(7 days, proposers, executors); + timelock = new ZeroExTimelock(7 days, proposers, executors); - zeroExGovernor = new ZeroExGovernor(wToken, zeroExTimelock); + governor = new ZeroExGovernor(wToken, timelock); vm.stopPrank(); } + + function testShouldReturnCorrectName() public { + assertEq(governor.name(), "ZeroExGovernor"); + } + + function testShouldReturnCorrectVotingDelay() public { + assertEq(governor.votingDelay(), 21600); + } + + function testShouldReturnCorrectVotingPeriod() public { + assertEq(governor.votingPeriod(), 50400); + } + + function testShouldReturnCorrectProposalThreshold() public { + assertEq(governor.proposalThreshold(), 0); + } + + function testShouldReturnCorrectQuorum() public { + assertEq(governor.quorumNumerator(), 10); + } + + function testShouldReturnCorrectToken() public { + assertEq(address(governor.token()), address(wToken)); + } + + function testShouldReturnCorrectTimelock() public { + assertEq(address(governor.timelock()), address(timelock)); + } } From f4db93959098201729cc7b3282eec4bddbdda368 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 23 Jan 2023 14:35:27 +0200 Subject: [PATCH 021/106] Add basic voting power delegation tests --- .../governance/test/ZRXWrappedTokenTest.t.sol | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index d91fc52b38..ea94761c7d 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -103,4 +103,36 @@ contract ZRXWrappedTokenTest is BaseTest { uint256 tokenBalance2 = token.balanceOf(account2); assertEq(tokenBalance2, 100e18 - wTokenBalance2 - tokenBalance4); } + + function testShouldBeAbleToSelfDelegateVotingPower() public { + // Check voting power initially is 0 + uint256 votingPowerAccount2 = wToken.getVotes(account2); + assertEq(votingPowerAccount2, 0); + + // Wrap ZRX and delegate voting power to themselves + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account2); + + // Check voting power is now = token balance + votingPowerAccount2 = wToken.getVotes(account2); + assertEq(votingPowerAccount2, 100e18); + } + + function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { + // Check voting power initially is 0 + uint256 votingPowerAccount3 = wToken.getVotes(account3); + assertEq(votingPowerAccount3, 0); + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + wToken.delegate(account3); + + // Check voting power is now = token balance + votingPowerAccount3 = wToken.getVotes(account3); + assertEq(votingPowerAccount3, 10e18); + } } From cccaf31527a688aee4980951e78417362bdc8c31 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 23 Jan 2023 16:57:26 +0200 Subject: [PATCH 022/106] Add proposal execution happy path test --- contracts/governance/src/ZeroExTimelock.sol | 5 +- contracts/governance/test/BaseTest.sol | 1 + .../governance/test/ZeroExGovernor.t.sol | 85 ++++++++++++++++++- 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index 152eb1abc5..724e21a6d2 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -28,6 +28,7 @@ contract ZeroExTimelock is TimelockController { constructor( uint256 minDelay, address[] memory proposers, - address[] memory executors - ) TimelockController(minDelay, proposers, executors, address(0)) {} + address[] memory executors, + address admin + ) TimelockController(minDelay, proposers, executors, admin) {} } diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol index 0844a977ee..2d61b586a8 100644 --- a/contracts/governance/test/BaseTest.sol +++ b/contracts/governance/test/BaseTest.sol @@ -20,6 +20,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; +import "forge-std/console.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; import "./ZRXMock.sol"; diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExGovernor.t.sol index 601c027f7f..a7857751ca 100644 --- a/contracts/governance/test/ZeroExGovernor.t.sol +++ b/contracts/governance/test/ZeroExGovernor.t.sol @@ -23,27 +23,56 @@ import "../src/ZeroExTimelock.sol"; import "../src/ZeroExGovernor.sol"; import "../src/ZRXWrappedToken.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/mocks/CallReceiverMock.sol"; contract ZeroExGovernorTest is BaseTest { IERC20 public token; - ZRXWrappedToken public wToken; - ZeroExTimelock public timelock; - ZeroExGovernor public governor; + ZRXWrappedToken internal wToken; + ZeroExTimelock internal timelock; + ZeroExGovernor internal governor; + CallReceiverMock internal callReceiverMock; function setUp() public { vm.startPrank(account1); token = IERC20(createZRXToken()); token.transfer(account2, 100e18); token.transfer(account3, 200e18); + token.transfer(account4, 50e18); wToken = new ZRXWrappedToken(token); + vm.stopPrank(); + + // Setup accounts 2,3 and 4 to vote + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account2); + vm.stopPrank(); + + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account3); + vm.stopPrank(); + + vm.startPrank(account4); + token.approve(address(wToken), 50e18); + wToken.depositFor(account4, 50e18); + wToken.delegate(account4); + vm.stopPrank(); address[] memory proposers = new address[](0); address[] memory executors = new address[](0); - timelock = new ZeroExTimelock(7 days, proposers, executors); + timelock = new ZeroExTimelock(7 days, proposers, executors, account1); governor = new ZeroExGovernor(wToken, timelock); + + vm.startPrank(account1); + timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); + timelock.grantRole(timelock.EXECUTOR_ROLE(), address(governor)); vm.stopPrank(); + + callReceiverMock = new CallReceiverMock(); } function testShouldReturnCorrectName() public { @@ -73,4 +102,52 @@ contract ZeroExGovernorTest is BaseTest { function testShouldReturnCorrectTimelock() public { assertEq(address(governor.timelock()), address(timelock)); } + + function testShouldBeAbleToExecuteASuccessfulProposal() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 0); // Vote "against" + vm.stopPrank(); + vm.prank(account3); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + vm.prank(account4); + governor.castVote(proposalId, 2); // Vote "abstain" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Get vote results + (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); + assertEq(votesAgainst, 100e18); + assertEq(votesFor, 200e18); + assertEq(votesAbstain, 50e18); + + IGovernor.ProposalState state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.warp(governor.proposalEta(proposalId) + 1); + + governor.execute(targets, values, calldatas, keccak256("Proposal description")); + state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); + } } From e07260f5f3e2789014ca29a6608ba17bfc6b5b04 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 30 Jan 2023 17:50:28 +0200 Subject: [PATCH 023/106] Split ERC20Votes logic between wrapped token and ZeroExVotes contracts --- contracts/governance/src/IZeroExVotes.sol | 69 +++++++ contracts/governance/src/ZRXWrappedToken.sol | 91 +++++++- contracts/governance/src/ZeroExGovernor.sol | 4 +- contracts/governance/src/ZeroExVotes.sol | 195 ++++++++++++++++++ contracts/governance/test/BaseTest.sol | 22 +- .../governance/test/ZRXWrappedTokenTest.t.sol | 17 +- .../governance/test/ZeroExGovernor.t.sol | 18 +- 7 files changed, 381 insertions(+), 35 deletions(-) create mode 100644 contracts/governance/src/IZeroExVotes.sol create mode 100644 contracts/governance/src/ZeroExVotes.sol diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol new file mode 100644 index 0000000000..8167886111 --- /dev/null +++ b/contracts/governance/src/IZeroExVotes.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +/// @custom:security-contact security@0xproject.com +interface IZeroExVotes { + struct Checkpoint { + uint32 fromBlock; + uint224 votes; + } + + /** + * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + + function checkpoints(address account, uint32 pos) external view returns (Checkpoint memory); + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) external view returns (uint32); + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) external view returns (uint256); + + /** + * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is but NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); + + function moveVotingPower(address src, address dst, uint256 amount) external; + + function writeCheckpointAddTotalSupply(uint256 delta) external returns (uint256 oldWeight, uint256 newWeight); + + function writeCheckpointSubTotalSupply(uint256 delta) external returns (uint256 oldWeight, uint256 newWeight); +} diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 834ca78961..ff104f486f 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -20,13 +20,28 @@ pragma solidity ^0.8.17; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/token/ERC20/extensions/draft-ERC20Permit.sol"; -import "@openzeppelin/token/ERC20/extensions/ERC20Votes.sol"; import "@openzeppelin/token/ERC20/extensions/ERC20Wrapper.sol"; +import "@openzeppelin/governance/utils/IVotes.sol"; +import "./IZeroExVotes.sol"; -contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper { +contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { constructor( - IERC20 wrappedToken - ) ERC20("Wrapped ZRX", "wZRX") ERC20Permit("Wrapped ZRX") ERC20Wrapper(wrappedToken) {} + IERC20 wrappedToken, + IZeroExVotes _zeroExVotes + ) ERC20("Wrapped ZRX", "wZRX") ERC20Permit("Wrapped ZRX") ERC20Wrapper(wrappedToken) { + zeroExVotes = _zeroExVotes; + } + + IZeroExVotes public zeroExVotes; + mapping(address => address) private _delegates; + + bytes32 private constant _DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + + /** + * @dev Emitted when an account changes their delegate. + */ + event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate); // The functions below are the required overrides from the base contracts @@ -34,15 +49,77 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Votes, ERC20Wrapper { super.decimals(); } - function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20, ERC20Votes) { + function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) { super._afterTokenTransfer(from, to, amount); + + // Move voting power when tokens are transferred. + zeroExVotes.moveVotingPower(delegates(from), delegates(to), amount); } - function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) { + /** + * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). + */ + function _maxSupply() internal view virtual returns (uint224) { + return type(uint224).max; + } + + function _mint(address to, uint256 amount) internal override(ERC20) { super._mint(to, amount); + + // Snapshots the totalSupply after it has been increased. + require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); + + zeroExVotes.writeCheckpointAddTotalSupply(amount); } - function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) { + function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); + + // Snapshots the totalSupply after it has been decreased. + zeroExVotes.writeCheckpointSubTotalSupply(amount); + } + + /** + * @dev Get the address `account` is currently delegating to. + */ + function delegates(address account) public view returns (address) { + return _delegates[account]; + } + + /** + * @dev Delegate votes from the sender to `delegatee`. + */ + function delegate(address delegatee) public { + _delegate(_msgSender(), delegatee); + } + + /** + * @dev Delegates votes from signer to `delegatee` + */ + function delegateBySig(address delegatee, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) public { + require(block.timestamp <= expiry, "ERC20Votes: signature expired"); + address signer = ECDSA.recover( + _hashTypedDataV4(keccak256(abi.encode(_DELEGATION_TYPEHASH, delegatee, nonce, expiry))), + v, + r, + s + ); + require(nonce == _useNonce(signer), "ERC20Votes: invalid nonce"); + _delegate(signer, delegatee); + } + + /** + * @dev Change delegation for `delegator` to `delegatee`. + * + * Emits events {DelegateChanged} and {IZeroExVotes-DelegateVotesChanged}. + */ + function _delegate(address delegator, address delegatee) internal virtual { + address currentDelegate = delegates(delegator); + uint256 delegatorBalance = balanceOf(delegator); + _delegates[delegator] = delegatee; + + emit DelegateChanged(delegator, currentDelegate, delegatee); + + zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance); } } diff --git a/contracts/governance/src/ZeroExGovernor.sol b/contracts/governance/src/ZeroExGovernor.sol index 95598d6f49..e88a6fa50f 100644 --- a/contracts/governance/src/ZeroExGovernor.sol +++ b/contracts/governance/src/ZeroExGovernor.sol @@ -35,7 +35,7 @@ contract ZeroExGovernor is GovernorTimelockControl { constructor( - IVotes _token, + IVotes _tokenVotes, TimelockController _timelock ) Governor("ZeroExGovernor") @@ -44,7 +44,7 @@ contract ZeroExGovernor is 50400 /* voting period: 1 week */, 0 /* proposal threshold */ ) - GovernorVotes(_token) + GovernorVotes(_tokenVotes) GovernorVotesQuorumFraction(10) GovernorTimelockControl(_timelock) {} diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol new file mode 100644 index 0000000000..3031f2b70e --- /dev/null +++ b/contracts/governance/src/ZeroExVotes.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/utils/math/SafeCast.sol"; +import "@openzeppelin/utils/math/Math.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/governance/utils/IVotes.sol"; +import "./IZeroExVotes.sol"; + +/// @custom:security-contact security@0xproject.com +contract ZeroExVotes is IZeroExVotes { + address public token; + + mapping(address => Checkpoint[]) private _checkpoints; + Checkpoint[] private _totalSupplyCheckpoints; + + modifier onlyToken() { + require(msg.sender == token, "Only the token is allowed to perform this operation"); + _; + } + + function initialize(address _token) public { + token = _token; + } + + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ + function checkpoints(address account, uint32 pos) public view override returns (Checkpoint memory) { + return _checkpoints[account][pos]; + } + + /** + * @dev Get number of checkpoints for `account`. + */ + function numCheckpoints(address account) public view override returns (uint32) { + return SafeCast.toUint32(_checkpoints[account].length); + } + + /** + * @dev Gets the current votes balance for `account` + */ + function getVotes(address account) public view override returns (uint256) { + uint256 pos = _checkpoints[account].length; + return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; + } + + /** + * @inheritdoc IZeroExVotes + */ + function getPastVotes(address account, uint256 blockNumber) public view override returns (uint256) { + require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); + return _checkpointsLookup(_checkpoints[account], blockNumber); + } + + /** + * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. + * It is but NOT the sum of all the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { + require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); + return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + } + + function moveVotingPower(address src, address dst, uint256 amount) public override onlyToken { + return _moveVotingPower(src, dst, amount); + } + + function writeCheckpointAddTotalSupply( + uint256 delta + ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { + _writeCheckpoint(_totalSupplyCheckpoints, _add, delta); + } + + function writeCheckpointSubTotalSupply( + uint256 delta + ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, delta); + } + + function _moveVotingPower(address src, address dst, uint256 amount) private { + if (src != dst && amount > 0) { + if (src != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + emit DelegateVotesChanged(src, oldWeight, newWeight); + } + + if (dst != address(0)) { + (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + emit DelegateVotesChanged(dst, oldWeight, newWeight); + } + } + } + + /** + * @dev Lookup a value in a list of (sorted) checkpoints. + */ + function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // Initially we check if the block is recent to narrow the search range. + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the + // invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 length = ckpts.length; + + uint256 low = 0; + uint256 high = length; + + if (length > 5) { + uint256 mid = length - Math.sqrt(length); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); + + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + function _add(uint256 a, uint256 b) public pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) public pure returns (uint256) { + return a - b; + } + + /** + * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + */ + function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { + assembly { + mstore(0, ckpts.slot) + result.slot := add(keccak256(0, 0x20), pos) + } + } +} diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.sol index 2d61b586a8..316ac14880 100644 --- a/contracts/governance/test/BaseTest.sol +++ b/contracts/governance/test/BaseTest.sol @@ -23,6 +23,10 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; import "./ZRXMock.sol"; +import "../src/ZRXWrappedToken.sol"; +import "../src/ZeroExVotes.sol"; +import "../src/ZeroExTimelock.sol"; +import "../src/ZeroExGovernor.sol"; contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); @@ -37,7 +41,7 @@ contract BaseTest is Test { vm.deal(account4, 1e20); } - function createZRXToken() internal returns (address) { + function setupGovernance() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExGovernor) { // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); // address _address; @@ -46,6 +50,20 @@ contract BaseTest is Test { // } // return address(_address); - return address(new ZRXMock()); + ZRXMock mockZRX = new ZRXMock(); + ZeroExVotes votes = new ZeroExVotes(); + ZRXWrappedToken token = new ZRXWrappedToken(mockZRX, votes); + votes.initialize(address(token)); + + address[] memory proposers = new address[](0); + address[] memory executors = new address[](0); + + ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExGovernor governor = new ZeroExGovernor(IVotes(address(votes)), timelock); + + timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); + timelock.grantRole(timelock.EXECUTOR_ROLE(), address(governor)); + + return (mockZRX, token, votes, timelock, governor); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index ea94761c7d..52b7a5fae3 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -23,16 +23,15 @@ import "../src/ZRXWrappedToken.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; contract ZRXWrappedTokenTest is BaseTest { - IERC20 public token; - ZRXWrappedToken public wToken; + IERC20 private token; + ZRXWrappedToken private wToken; + ZeroExVotes private votes; function setUp() public { vm.startPrank(account1); - token = IERC20(createZRXToken()); + (token, wToken, votes, , ) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 100e18); - - wToken = new ZRXWrappedToken(token); vm.stopPrank(); } @@ -106,7 +105,7 @@ contract ZRXWrappedTokenTest is BaseTest { function testShouldBeAbleToSelfDelegateVotingPower() public { // Check voting power initially is 0 - uint256 votingPowerAccount2 = wToken.getVotes(account2); + uint256 votingPowerAccount2 = votes.getVotes(account2); assertEq(votingPowerAccount2, 0); // Wrap ZRX and delegate voting power to themselves @@ -116,13 +115,13 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.delegate(account2); // Check voting power is now = token balance - votingPowerAccount2 = wToken.getVotes(account2); + votingPowerAccount2 = votes.getVotes(account2); assertEq(votingPowerAccount2, 100e18); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { // Check voting power initially is 0 - uint256 votingPowerAccount3 = wToken.getVotes(account3); + uint256 votingPowerAccount3 = votes.getVotes(account3); assertEq(votingPowerAccount3, 0); // Account 2 wraps ZRX and delegates voting power to account3 @@ -132,7 +131,7 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.delegate(account3); // Check voting power is now = token balance - votingPowerAccount3 = wToken.getVotes(account3); + votingPowerAccount3 = votes.getVotes(account3); assertEq(votingPowerAccount3, 10e18); } } diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExGovernor.t.sol index a7857751ca..4a8a6e1b0b 100644 --- a/contracts/governance/test/ZeroExGovernor.t.sol +++ b/contracts/governance/test/ZeroExGovernor.t.sol @@ -28,18 +28,17 @@ import "@openzeppelin/mocks/CallReceiverMock.sol"; contract ZeroExGovernorTest is BaseTest { IERC20 public token; ZRXWrappedToken internal wToken; + ZeroExVotes internal votes; ZeroExTimelock internal timelock; ZeroExGovernor internal governor; CallReceiverMock internal callReceiverMock; function setUp() public { vm.startPrank(account1); - token = IERC20(createZRXToken()); + (token, wToken, votes, timelock, governor) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 200e18); token.transfer(account4, 50e18); - - wToken = new ZRXWrappedToken(token); vm.stopPrank(); // Setup accounts 2,3 and 4 to vote @@ -61,17 +60,6 @@ contract ZeroExGovernorTest is BaseTest { wToken.delegate(account4); vm.stopPrank(); - address[] memory proposers = new address[](0); - address[] memory executors = new address[](0); - - timelock = new ZeroExTimelock(7 days, proposers, executors, account1); - governor = new ZeroExGovernor(wToken, timelock); - - vm.startPrank(account1); - timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); - timelock.grantRole(timelock.EXECUTOR_ROLE(), address(governor)); - vm.stopPrank(); - callReceiverMock = new CallReceiverMock(); } @@ -96,7 +84,7 @@ contract ZeroExGovernorTest is BaseTest { } function testShouldReturnCorrectToken() public { - assertEq(address(governor.token()), address(wToken)); + assertEq(address(governor.token()), address(votes)); } function testShouldReturnCorrectTimelock() public { From 0685866db6b56034b8c20ecbce87eaf2a096fafa Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 1 Feb 2023 14:19:04 +0200 Subject: [PATCH 024/106] Exclude BaseTest from coverage in coveralls --- contracts/governance/test/{BaseTest.sol => BaseTest.t.sol} | 0 contracts/governance/test/ZRXWrappedTokenTest.t.sol | 2 +- contracts/governance/test/ZeroExGovernor.t.sol | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename contracts/governance/test/{BaseTest.sol => BaseTest.t.sol} (100%) diff --git a/contracts/governance/test/BaseTest.sol b/contracts/governance/test/BaseTest.t.sol similarity index 100% rename from contracts/governance/test/BaseTest.sol rename to contracts/governance/test/BaseTest.t.sol diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 52b7a5fae3..326c4804d0 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.17; -import "./BaseTest.sol"; +import "./BaseTest.t.sol"; import "../src/ZRXWrappedToken.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExGovernor.t.sol index 4a8a6e1b0b..33b37cf6c1 100644 --- a/contracts/governance/test/ZeroExGovernor.t.sol +++ b/contracts/governance/test/ZeroExGovernor.t.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.17; -import "./BaseTest.sol"; +import "./BaseTest.t.sol"; import "../src/ZeroExTimelock.sol"; import "../src/ZeroExGovernor.sol"; import "../src/ZRXWrappedToken.sol"; From adf73a7b51abb0cb389e213e383a7075c3ca9fe0 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 3 Feb 2023 15:43:02 +0200 Subject: [PATCH 025/106] Add protocol specific governor with produciton governance settings --- ...overnor.sol => ZeroExProtocolGovernor.sol} | 28 ++++-------- contracts/governance/test/BaseTest.t.sol | 9 ++-- ...nor.t.sol => ZeroExProtocolGovernor.t.sol} | 45 ++++++++++--------- 3 files changed, 39 insertions(+), 43 deletions(-) rename contracts/governance/src/{ZeroExGovernor.sol => ZeroExProtocolGovernor.sol} (82%) rename contracts/governance/test/{ZeroExGovernor.t.sol => ZeroExProtocolGovernor.t.sol} (79%) diff --git a/contracts/governance/src/ZeroExGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol similarity index 82% rename from contracts/governance/src/ZeroExGovernor.sol rename to contracts/governance/src/ZeroExProtocolGovernor.sol index e88a6fa50f..a210f7d87a 100644 --- a/contracts/governance/src/ZeroExGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -22,33 +22,29 @@ import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/governance/extensions/GovernorVotes.sol"; -import "@openzeppelin/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; -/// @custom:security-contact security@0xproject.com -contract ZeroExGovernor is +contract ZeroExProtocolGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes, - GovernorVotesQuorumFraction, GovernorTimelockControl { constructor( - IVotes _tokenVotes, + IVotes _token, TimelockController _timelock ) - Governor("ZeroExGovernor") - GovernorSettings( - 21600 /* voting delay: 3 days */, - 50400 /* voting period: 1 week */, - 0 /* proposal threshold */ - ) - GovernorVotes(_tokenVotes) - GovernorVotesQuorumFraction(10) + Governor("ZeroExProtocolGovernor") + GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 1000000e18) + GovernorVotes(_token) GovernorTimelockControl(_timelock) {} + function quorum(uint256 blockNumber) public pure override returns (uint256) { + return 10000000e18; + } + // The following functions are overrides required by Solidity. function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) { @@ -59,12 +55,6 @@ contract ZeroExGovernor is return super.votingPeriod(); } - function quorum( - uint256 blockNumber - ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { - return super.quorum(blockNumber); - } - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { return super.state(proposalId); } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 316ac14880..d45ea21026 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -26,7 +26,7 @@ import "./ZRXMock.sol"; import "../src/ZRXWrappedToken.sol"; import "../src/ZeroExVotes.sol"; import "../src/ZeroExTimelock.sol"; -import "../src/ZeroExGovernor.sol"; +import "../src/ZeroExProtocolGovernor.sol"; contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); @@ -41,7 +41,10 @@ contract BaseTest is Test { vm.deal(account4, 1e20); } - function setupGovernance() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExGovernor) { + function setupGovernance() + internal + returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor) + { // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); // address _address; @@ -59,7 +62,7 @@ contract BaseTest is Test { address[] memory executors = new address[](0); ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); - ZeroExGovernor governor = new ZeroExGovernor(IVotes(address(votes)), timelock); + ZeroExProtocolGovernor governor = new ZeroExProtocolGovernor(IVotes(address(votes)), timelock); timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); timelock.grantRole(timelock.EXECUTOR_ROLE(), address(governor)); diff --git a/contracts/governance/test/ZeroExGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol similarity index 79% rename from contracts/governance/test/ZeroExGovernor.t.sol rename to contracts/governance/test/ZeroExProtocolGovernor.t.sol index 33b37cf6c1..ce83e7a69e 100644 --- a/contracts/governance/test/ZeroExGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -20,43 +20,43 @@ pragma solidity ^0.8.17; import "./BaseTest.t.sol"; import "../src/ZeroExTimelock.sol"; -import "../src/ZeroExGovernor.sol"; +import "../src/ZeroExProtocolGovernor.sol"; import "../src/ZRXWrappedToken.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/mocks/CallReceiverMock.sol"; -contract ZeroExGovernorTest is BaseTest { +contract ZeroExProtocolGovernorTest is BaseTest { IERC20 public token; ZRXWrappedToken internal wToken; ZeroExVotes internal votes; ZeroExTimelock internal timelock; - ZeroExGovernor internal governor; + ZeroExProtocolGovernor internal governor; CallReceiverMock internal callReceiverMock; function setUp() public { vm.startPrank(account1); (token, wToken, votes, timelock, governor) = setupGovernance(); - token.transfer(account2, 100e18); - token.transfer(account3, 200e18); - token.transfer(account4, 50e18); + token.transfer(account2, 10000000e18); + token.transfer(account3, 2000000e18); + token.transfer(account4, 3000000e18); vm.stopPrank(); // Setup accounts 2,3 and 4 to vote vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); + token.approve(address(wToken), 10000000e18); + wToken.depositFor(account2, 10000000e18); wToken.delegate(account2); vm.stopPrank(); vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 200e18); + token.approve(address(wToken), 2000000e18); + wToken.depositFor(account3, 2000000e18); wToken.delegate(account3); vm.stopPrank(); vm.startPrank(account4); - token.approve(address(wToken), 50e18); - wToken.depositFor(account4, 50e18); + token.approve(address(wToken), 3000000e18); + wToken.depositFor(account4, 3000000e18); wToken.delegate(account4); vm.stopPrank(); @@ -64,11 +64,11 @@ contract ZeroExGovernorTest is BaseTest { } function testShouldReturnCorrectName() public { - assertEq(governor.name(), "ZeroExGovernor"); + assertEq(governor.name(), "ZeroExProtocolGovernor"); } function testShouldReturnCorrectVotingDelay() public { - assertEq(governor.votingDelay(), 21600); + assertEq(governor.votingDelay(), 14400); } function testShouldReturnCorrectVotingPeriod() public { @@ -76,11 +76,11 @@ contract ZeroExGovernorTest is BaseTest { } function testShouldReturnCorrectProposalThreshold() public { - assertEq(governor.proposalThreshold(), 0); + assertEq(governor.proposalThreshold(), 1000000e18); } function testShouldReturnCorrectQuorum() public { - assertEq(governor.quorumNumerator(), 10); + assertEq(governor.quorum(block.number), 10000000e18); } function testShouldReturnCorrectToken() public { @@ -102,17 +102,20 @@ contract ZeroExGovernorTest is BaseTest { bytes[] memory calldatas = new bytes[](1); calldatas[0] = abi.encodeWithSignature("mockFunction()"); + vm.roll(2); + vm.startPrank(account2); uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); // Fast forward to after vote start vm.roll(governor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - governor.castVote(proposalId, 0); // Vote "against" + governor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); vm.prank(account3); - governor.castVote(proposalId, 1); // Vote "for" + governor.castVote(proposalId, 0); // Vote "against" vm.stopPrank(); vm.prank(account4); governor.castVote(proposalId, 2); // Vote "abstain" @@ -123,9 +126,9 @@ contract ZeroExGovernorTest is BaseTest { // Get vote results (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); - assertEq(votesAgainst, 100e18); - assertEq(votesFor, 200e18); - assertEq(votesAbstain, 50e18); + assertEq(votesFor, 10000000e18); + assertEq(votesAgainst, 2000000e18); + assertEq(votesAbstain, 3000000e18); IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); From 7e1b858610b152258158341d1b44bc4f861c2389 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 3 Feb 2023 16:10:12 +0200 Subject: [PATCH 026/106] Add a dedicated instance for the treasury governor This is currently using the default 1 token 1 vote mechanism but will be migrated --- .../governance/src/ZeroExTreasuryGovernor.sol | 103 +++++++++++++ contracts/governance/test/BaseTest.t.sol | 18 ++- .../governance/test/ZRXWrappedTokenTest.t.sol | 2 +- .../test/ZeroExProtocolGovernor.t.sol | 2 +- .../test/ZeroExTreasuryGovernor.t.sol | 144 ++++++++++++++++++ 5 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 contracts/governance/src/ZeroExTreasuryGovernor.sol create mode 100644 contracts/governance/test/ZeroExTreasuryGovernor.t.sol diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol new file mode 100644 index 0000000000..06d4ebe1e6 --- /dev/null +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/governance/Governor.sol"; +import "@openzeppelin/governance/extensions/GovernorSettings.sol"; +import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; +import "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; + +contract ZeroExTreasuryGovernor is + Governor, + GovernorSettings, + GovernorCountingSimple, + GovernorVotes, + GovernorTimelockControl +{ + constructor( + IVotes _token, + TimelockController _timelock + ) + Governor("ZeroExTreasuryGovernor") + GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 100000e18) + GovernorVotes(_token) + GovernorTimelockControl(_timelock) + {} + + function quorum(uint256 blockNumber) public pure override returns (uint256) { + return 5000000e18; + } + + // The following functions are overrides required by Solidity. + + function votingDelay() public view override(IGovernor, GovernorSettings) returns (uint256) { + return super.votingDelay(); + } + + function votingPeriod() public view override(IGovernor, GovernorSettings) returns (uint256) { + return super.votingPeriod(); + } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { + return super.proposalThreshold(); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index d45ea21026..25592ad425 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -27,6 +27,7 @@ import "../src/ZRXWrappedToken.sol"; import "../src/ZeroExVotes.sol"; import "../src/ZeroExTimelock.sol"; import "../src/ZeroExProtocolGovernor.sol"; +import "../src/ZeroExTreasuryGovernor.sol"; contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); @@ -43,7 +44,7 @@ contract BaseTest is Test { function setupGovernance() internal - returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor) + returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor, ZeroExTreasuryGovernor) { // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); @@ -62,11 +63,18 @@ contract BaseTest is Test { address[] memory executors = new address[](0); ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); - ZeroExProtocolGovernor governor = new ZeroExProtocolGovernor(IVotes(address(votes)), timelock); + ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), timelock); - timelock.grantRole(timelock.PROPOSER_ROLE(), address(governor)); - timelock.grantRole(timelock.EXECUTOR_ROLE(), address(governor)); + timelock.grantRole(timelock.PROPOSER_ROLE(), address(protocolGovernor)); + timelock.grantRole(timelock.EXECUTOR_ROLE(), address(protocolGovernor)); - return (mockZRX, token, votes, timelock, governor); + // TODO use a separate timelock instance + // ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), timelock); + + timelock.grantRole(timelock.PROPOSER_ROLE(), address(treasuryGovernor)); + timelock.grantRole(timelock.EXECUTOR_ROLE(), address(treasuryGovernor)); + + return (mockZRX, token, votes, timelock, protocolGovernor, treasuryGovernor); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 326c4804d0..eaede0ef07 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -29,7 +29,7 @@ contract ZRXWrappedTokenTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, , ) = setupGovernance(); + (token, wToken, votes, , , ) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 100e18); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index ce83e7a69e..ecdb0fc142 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExProtocolGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, governor) = setupGovernance(); + (token, wToken, votes, timelock, governor, ) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol new file mode 100644 index 0000000000..d2444adfb5 --- /dev/null +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "./BaseTest.t.sol"; +import "../src/ZeroExTimelock.sol"; +import "../src/ZeroExTreasuryGovernor.sol"; +import "../src/ZRXWrappedToken.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/mocks/CallReceiverMock.sol"; + +contract ZeroExTreasuryGovernorTest is BaseTest { + IERC20 public token; + ZRXWrappedToken internal wToken; + ZeroExVotes internal votes; + ZeroExTimelock internal timelock; + ZeroExTreasuryGovernor internal governor; + CallReceiverMock internal callReceiverMock; + + function setUp() public { + vm.startPrank(account1); + (token, wToken, votes, timelock, , governor) = setupGovernance(); + token.transfer(account2, 10000000e18); + token.transfer(account3, 2000000e18); + token.transfer(account4, 3000000e18); + vm.stopPrank(); + + // Setup accounts 2,3 and 4 to vote + vm.startPrank(account2); + token.approve(address(wToken), 10000000e18); + wToken.depositFor(account2, 10000000e18); + wToken.delegate(account2); + vm.stopPrank(); + + vm.startPrank(account3); + token.approve(address(wToken), 2000000e18); + wToken.depositFor(account3, 2000000e18); + wToken.delegate(account3); + vm.stopPrank(); + + vm.startPrank(account4); + token.approve(address(wToken), 3000000e18); + wToken.depositFor(account4, 3000000e18); + wToken.delegate(account4); + vm.stopPrank(); + + callReceiverMock = new CallReceiverMock(); + } + + function testShouldReturnCorrectName() public { + assertEq(governor.name(), "ZeroExTreasuryGovernor"); + } + + function testShouldReturnCorrectVotingDelay() public { + assertEq(governor.votingDelay(), 14400); + } + + function testShouldReturnCorrectVotingPeriod() public { + assertEq(governor.votingPeriod(), 50400); + } + + function testShouldReturnCorrectProposalThreshold() public { + assertEq(governor.proposalThreshold(), 100000e18); + } + + function testShouldReturnCorrectQuorum() public { + assertEq(governor.quorum(block.number), 5000000e18); + } + + function testShouldReturnCorrectToken() public { + assertEq(address(governor.token()), address(votes)); + } + + function testShouldReturnCorrectTimelock() public { + assertEq(address(governor.timelock()), address(timelock)); + } + + function testShouldBeAbleToExecuteASuccessfulProposal() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + vm.prank(account3); + governor.castVote(proposalId, 0); // Vote "against" + vm.stopPrank(); + vm.prank(account4); + governor.castVote(proposalId, 2); // Vote "abstain" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Get vote results + (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); + assertEq(votesFor, 10000000e18); + assertEq(votesAgainst, 2000000e18); + assertEq(votesAbstain, 3000000e18); + + IGovernor.ProposalState state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.warp(governor.proposalEta(proposalId) + 1); + + governor.execute(targets, values, calldatas, keccak256("Proposal description")); + state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); + } +} From a6563653e65a7fff0483ab24b1450113f06657f2 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Feb 2023 16:51:22 +0200 Subject: [PATCH 027/106] Add test for updating governance settings for voting delay, voting period and proposal threshold --- .../test/ZeroExProtocolGovernor.t.sol | 111 ++++++++++++++++++ .../test/ZeroExTreasuryGovernor.t.sol | 111 ++++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index ecdb0fc142..29cdba3392 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -141,4 +141,115 @@ contract ZeroExProtocolGovernorTest is BaseTest { state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } + + function testCanUpdateVotingDelaySetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingDelay.selector, 3 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); + + uint256 votingDelay = governor.votingDelay(); + assertEq(votingDelay, 3 days); + } + + function testCanUpdateVotingPeriodSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingPeriod.selector, 14 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting period to 14 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); + + uint256 votingPeriod = governor.votingPeriod(); + assertEq(votingPeriod, 14 days); + } + + function testCanUpdateProposalThresholdSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setProposalThreshold.selector, 2000000e18); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase proposal threshold to 2000000e18"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); + + uint256 proposalThreshold = governor.proposalThreshold(); + assertEq(proposalThreshold, 2000000e18); + } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index d2444adfb5..b98e568d16 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -141,4 +141,115 @@ contract ZeroExTreasuryGovernorTest is BaseTest { state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } + + function testCanUpdateVotingDelaySetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingDelay.selector, 3 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); + + uint256 votingDelay = governor.votingDelay(); + assertEq(votingDelay, 3 days); + } + + function testCanUpdateVotingPeriodSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingPeriod.selector, 14 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting period to 14 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); + + uint256 votingPeriod = governor.votingPeriod(); + assertEq(votingPeriod, 14 days); + } + + function testCanUpdateProposalThresholdSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setProposalThreshold.selector, 2000000e18); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase proposal threshold to 2000000e18"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); + + uint256 proposalThreshold = governor.proposalThreshold(); + assertEq(proposalThreshold, 2000000e18); + } } From 73bb6a4a6cee54d1177930d460f3174a96d71577 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 6 Feb 2023 17:10:59 +0200 Subject: [PATCH 028/106] Create seperate timelock contract instance for treasury and protocol --- contracts/governance/test/BaseTest.t.sol | 29 ++++++++++++------- .../governance/test/ZRXWrappedTokenTest.t.sol | 2 +- .../test/ZeroExProtocolGovernor.t.sol | 2 +- .../test/ZeroExTreasuryGovernor.t.sol | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 25592ad425..941fea0d90 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -44,7 +44,15 @@ contract BaseTest is Test { function setupGovernance() internal - returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor, ZeroExTreasuryGovernor) + returns ( + IERC20, + ZRXWrappedToken, + ZeroExVotes, + ZeroExTimelock, + ZeroExTimelock, + ZeroExProtocolGovernor, + ZeroExTreasuryGovernor + ) { // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); @@ -62,19 +70,18 @@ contract BaseTest is Test { address[] memory proposers = new address[](0); address[] memory executors = new address[](0); - ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); - ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), timelock); + ZeroExTimelock protocolTimelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); - timelock.grantRole(timelock.PROPOSER_ROLE(), address(protocolGovernor)); - timelock.grantRole(timelock.EXECUTOR_ROLE(), address(protocolGovernor)); + protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); + protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); - // TODO use a separate timelock instance - // ZeroExTimelock timelock = new ZeroExTimelock(7 days, proposers, executors, account1); - ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), timelock); + ZeroExTimelock treasuryTimelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); - timelock.grantRole(timelock.PROPOSER_ROLE(), address(treasuryGovernor)); - timelock.grantRole(timelock.EXECUTOR_ROLE(), address(treasuryGovernor)); + treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); + treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); - return (mockZRX, token, votes, timelock, protocolGovernor, treasuryGovernor); + return (mockZRX, token, votes, protocolTimelock, treasuryTimelock, protocolGovernor, treasuryGovernor); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index eaede0ef07..8b0fe4da55 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -29,7 +29,7 @@ contract ZRXWrappedTokenTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, , , ) = setupGovernance(); + (token, wToken, votes, , , , ) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 100e18); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 29cdba3392..c6228e6fcd 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExProtocolGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, governor, ) = setupGovernance(); + (token, wToken, votes, timelock, , governor, ) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index b98e568d16..f86dbe38fd 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExTreasuryGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, , governor) = setupGovernance(); + (token, wToken, votes, , timelock, , governor) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); From 6cff23d4da1a6623ae53ff20226fd54dd644a528 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 6 Feb 2023 17:14:33 +0200 Subject: [PATCH 029/106] Test updating the timlock min delay --- .../test/ZeroExProtocolGovernor.t.sol | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index c6228e6fcd..5b6f31243d 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -252,4 +252,41 @@ contract ZeroExProtocolGovernorTest is BaseTest { uint256 proposalThreshold = governor.proposalThreshold(); assertEq(proposalThreshold, 2000000e18); } + + function testCanUpdateTimelockDelay() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(timelock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(timelock.updateDelay.selector, 10 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase timelock delay to 10 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 10 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 10 days")); + + uint256 timelockDelay = timelock.getMinDelay(); + assertEq(timelockDelay, 10 days); + } } From 7691aef6995245cdc561801313e6fc7ca243aab4 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 6 Feb 2023 17:37:01 +0200 Subject: [PATCH 030/106] Set timelock delay to 2 days for protocol and 1 sec for treasury --- contracts/governance/test/BaseTest.t.sol | 4 ++-- contracts/governance/test/ZeroExProtocolGovernor.t.sol | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 941fea0d90..b3c4c548b4 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -70,13 +70,13 @@ contract BaseTest is Test { address[] memory proposers = new address[](0); address[] memory executors = new address[](0); - ZeroExTimelock protocolTimelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExTimelock protocolTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); - ZeroExTimelock treasuryTimelock = new ZeroExTimelock(7 days, proposers, executors, account1); + ZeroExTimelock treasuryTimelock = new ZeroExTimelock(1, proposers, executors, account1); ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 5b6f31243d..b16623d9df 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -262,11 +262,11 @@ contract ZeroExProtocolGovernorTest is BaseTest { values[0] = 0; bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(timelock.updateDelay.selector, 10 days); + calldatas[0] = abi.encodeWithSelector(timelock.updateDelay.selector, 7 days); vm.roll(2); vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase timelock delay to 10 days"); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase timelock delay to 7 days"); vm.stopPrank(); // Fast forward to after vote start @@ -281,12 +281,12 @@ contract ZeroExProtocolGovernorTest is BaseTest { vm.roll(governor.proposalDeadline(proposalId) + 1); // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 10 days"))); + governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 7 days"))); vm.warp(governor.proposalEta(proposalId) + 1); // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 10 days")); + governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 7 days")); uint256 timelockDelay = timelock.getMinDelay(); - assertEq(timelockDelay, 10 days); + assertEq(timelockDelay, 7 days); } } From 0223e16b794e48b5f5a864712417c2f8ae28572e Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 6 Feb 2023 17:45:04 +0200 Subject: [PATCH 031/106] Remove timelock from treasury governor --- .../governance/src/ZeroExTreasuryGovernor.sol | 55 +------------------ contracts/governance/test/BaseTest.t.sol | 19 +------ .../governance/test/ZRXWrappedTokenTest.t.sol | 2 +- .../test/ZeroExProtocolGovernor.t.sol | 2 +- .../test/ZeroExTreasuryGovernor.t.sol | 19 +------ 5 files changed, 8 insertions(+), 89 deletions(-) diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 06d4ebe1e6..fb3ef1b480 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -22,23 +22,14 @@ import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/governance/extensions/GovernorVotes.sol"; -import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; -contract ZeroExTreasuryGovernor is - Governor, - GovernorSettings, - GovernorCountingSimple, - GovernorVotes, - GovernorTimelockControl -{ +contract ZeroExTreasuryGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes { constructor( - IVotes _token, - TimelockController _timelock + IVotes _token ) Governor("ZeroExTreasuryGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 100000e18) GovernorVotes(_token) - GovernorTimelockControl(_timelock) {} function quorum(uint256 blockNumber) public pure override returns (uint256) { @@ -55,49 +46,7 @@ contract ZeroExTreasuryGovernor is return super.votingPeriod(); } - function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { - return super.state(proposalId); - } - - function propose( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - string memory description - ) public override(Governor, IGovernor) returns (uint256) { - return super.propose(targets, values, calldatas, description); - } - function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { return super.proposalThreshold(); } - - function _execute( - uint256 proposalId, - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) { - super._execute(proposalId, targets, values, calldatas, descriptionHash); - } - - function _cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) internal override(Governor, GovernorTimelockControl) returns (uint256) { - return super._cancel(targets, values, calldatas, descriptionHash); - } - - function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { - return super._executor(); - } - - function supportsInterface( - bytes4 interfaceId - ) public view override(Governor, GovernorTimelockControl) returns (bool) { - return super.supportsInterface(interfaceId); - } } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index b3c4c548b4..0129ff5145 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -44,15 +44,7 @@ contract BaseTest is Test { function setupGovernance() internal - returns ( - IERC20, - ZRXWrappedToken, - ZeroExVotes, - ZeroExTimelock, - ZeroExTimelock, - ZeroExProtocolGovernor, - ZeroExTreasuryGovernor - ) + returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor, ZeroExTreasuryGovernor) { // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); @@ -76,12 +68,7 @@ contract BaseTest is Test { protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); - ZeroExTimelock treasuryTimelock = new ZeroExTimelock(1, proposers, executors, account1); - ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); - - treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); - treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); - - return (mockZRX, token, votes, protocolTimelock, treasuryTimelock, protocolGovernor, treasuryGovernor); + ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes))); + return (mockZRX, token, votes, protocolTimelock, protocolGovernor, treasuryGovernor); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 8b0fe4da55..eaede0ef07 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -29,7 +29,7 @@ contract ZRXWrappedTokenTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, , , , ) = setupGovernance(); + (token, wToken, votes, , , ) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 100e18); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index b16623d9df..3411867a4d 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExProtocolGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, , governor, ) = setupGovernance(); + (token, wToken, votes, timelock, governor, ) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index f86dbe38fd..4f3596f098 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExTreasuryGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, , timelock, , governor) = setupGovernance(); + (token, wToken, votes, timelock, , governor) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); @@ -87,10 +87,6 @@ contract ZeroExTreasuryGovernorTest is BaseTest { assertEq(address(governor.token()), address(votes)); } - function testShouldReturnCorrectTimelock() public { - assertEq(address(governor.timelock()), address(timelock)); - } - function testShouldBeAbleToExecuteASuccessfulProposal() public { // Create a proposal address[] memory targets = new address[](1); @@ -133,10 +129,6 @@ contract ZeroExTreasuryGovernorTest is BaseTest { IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); - vm.warp(governor.proposalEta(proposalId) + 1); - governor.execute(targets, values, calldatas, keccak256("Proposal description")); state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); @@ -169,9 +161,6 @@ contract ZeroExTreasuryGovernorTest is BaseTest { // Fast forward to vote end vm.roll(governor.proposalDeadline(proposalId) + 1); - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); - vm.warp(governor.proposalEta(proposalId) + 1); // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); @@ -206,9 +195,6 @@ contract ZeroExTreasuryGovernorTest is BaseTest { // Fast forward to vote end vm.roll(governor.proposalDeadline(proposalId) + 1); - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); - vm.warp(governor.proposalEta(proposalId) + 1); // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); @@ -243,9 +229,6 @@ contract ZeroExTreasuryGovernorTest is BaseTest { // Fast forward to vote end vm.roll(governor.proposalDeadline(proposalId) + 1); - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18"))); - vm.warp(governor.proposalEta(proposalId) + 1); // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); From 5fbfcb7172f69f993fffd2a706e8143a7904aebb Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 8 Feb 2023 12:00:05 +0200 Subject: [PATCH 032/106] Refactor _checkpointsLookup to return entire Checkpoint instad of just number of votes --- contracts/governance/src/IZeroExVotes.sol | 33 +++- contracts/governance/src/ZRXWrappedToken.sol | 11 +- contracts/governance/src/ZeroExVotes.sol | 141 +++++++++++++----- .../governance/test/ZRXWrappedTokenTest.t.sol | 10 ++ 4 files changed, 157 insertions(+), 38 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 8167886111..9575ba113e 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -23,13 +23,28 @@ interface IZeroExVotes { struct Checkpoint { uint32 fromBlock; uint224 votes; + uint224 quadraticVotes; + } + + enum VotingType { + OneTokenOneVote, + Quadratic } /** * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. */ - event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + event DelegateVotesChanged( + address indexed delegate, + uint256 previousLinearBalance, + uint256 newLinearBalance, + uint256 previousQuadraticBalance, + uint256 newQuadraticBalance + ); + /** + * @dev Get the `pos`-th checkpoint for `account`. + */ function checkpoints(address account, uint32 pos) external view returns (Checkpoint memory); /** @@ -42,6 +57,11 @@ interface IZeroExVotes { */ function getVotes(address account) external view returns (uint256); + /** + * @dev Gets the current quadratic votes balance for `account` + */ + function getQuadraticVotes(address account) external view returns (uint256); + /** * @dev Retrieve the number of votes for `account` at the end of `blockNumber`. * @@ -51,6 +71,15 @@ interface IZeroExVotes { */ function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); + /** + * @dev Retrieve the number of quadratic votes for `account` at the end of `blockNumber`. + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastQuadraticVotes(address account, uint256 blockNumber) external view returns (uint256); + /** * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. * It is but NOT the sum of all the delegated votes! @@ -61,7 +90,7 @@ interface IZeroExVotes { */ function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); - function moveVotingPower(address src, address dst, uint256 amount) external; + function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; function writeCheckpointAddTotalSupply(uint256 delta) external returns (uint256 oldWeight, uint256 newWeight); diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index ff104f486f..2ccc5cb78a 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -53,7 +53,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { super._afterTokenTransfer(from, to, amount); // Move voting power when tokens are transferred. - zeroExVotes.moveVotingPower(delegates(from), delegates(to), amount); + zeroExVotes.moveVotingPower(delegates(from), delegates(to), balanceOf(from), balanceOf(to), amount); } /** @@ -120,6 +120,13 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { emit DelegateChanged(delegator, currentDelegate, delegatee); - zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance); + // TODO this is a case where the entire delegatee's balance is being moved accross i.e. balanceOf(delegator) == delegatorBalance + zeroExVotes.moveVotingPower( + currentDelegate, + delegatee, + balanceOf(delegator), + balanceOf(delegatee), + delegatorBalance + ); } } diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 3031f2b70e..644e1b9eaf 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -24,7 +24,6 @@ import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/governance/utils/IVotes.sol"; import "./IZeroExVotes.sol"; -/// @custom:security-contact security@0xproject.com contract ZeroExVotes is IZeroExVotes { address public token; @@ -41,88 +40,146 @@ contract ZeroExVotes is IZeroExVotes { } /** - * @dev Get the `pos`-th checkpoint for `account`. + * @inheritdoc IZeroExVotes */ function checkpoints(address account, uint32 pos) public view override returns (Checkpoint memory) { return _checkpoints[account][pos]; } /** - * @dev Get number of checkpoints for `account`. + * @inheritdoc IZeroExVotes */ function numCheckpoints(address account) public view override returns (uint32) { return SafeCast.toUint32(_checkpoints[account].length); } /** - * @dev Gets the current votes balance for `account` + * @inheritdoc IZeroExVotes */ function getVotes(address account) public view override returns (uint256) { uint256 pos = _checkpoints[account].length; return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; } + /** + * @inheritdoc IZeroExVotes + */ + function getQuadraticVotes(address account) public view override returns (uint256) { + uint256 pos = _checkpoints[account].length; + return pos == 0 ? 0 : _checkpoints[account][pos - 1].quadraticVotes; + } + /** * @inheritdoc IZeroExVotes */ function getPastVotes(address account, uint256 blockNumber) public view override returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); - return _checkpointsLookup(_checkpoints[account], blockNumber); + + Checkpoint memory checkpoint = _checkpointsLookup(_checkpoints[account], blockNumber); + return checkpoint.votes; } /** - * @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances. - * It is but NOT the sum of all the delegated votes! - * - * Requirements: - * - * - `blockNumber` must have been already mined + * @inheritdoc IZeroExVotes */ - function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { + function getPastQuadraticVotes(address account, uint256 blockNumber) public view override returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); - return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); - } - function moveVotingPower(address src, address dst, uint256 amount) public override onlyToken { - return _moveVotingPower(src, dst, amount); + Checkpoint memory checkpoint = _checkpointsLookup(_checkpoints[account], blockNumber); + return checkpoint.quadraticVotes; } - function writeCheckpointAddTotalSupply( - uint256 delta - ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { - _writeCheckpoint(_totalSupplyCheckpoints, _add, delta); - } + /** + * @inheritdoc IZeroExVotes + */ + function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { + require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); - function writeCheckpointSubTotalSupply( - uint256 delta - ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, delta); + Checkpoint memory checkpoint = _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + return checkpoint.votes; } - function _moveVotingPower(address src, address dst, uint256 amount) private { + /** + * @inheritdoc IZeroExVotes + */ + function moveVotingPower( + address src, + address dst, + uint256 srcDelegatorBalance, + uint256 dstDelegatorBalance, + uint256 amount + ) public override onlyToken { if (src != dst && amount > 0) { if (src != address(0)) { + // Linear vote weight update (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); - emit DelegateVotesChanged(src, oldWeight, newWeight); + + // Quadratic vote weight update + uint256 quadraticSrcDelegatorBalanceBeforeTransfer = Math.sqrt(srcDelegatorBalance + amount); // sqrt of balance of sender before transfer + //todo if (_checkpoints[src] > 0) _writeCheckpoint(_checkpoints[src], _subtract, quadraticSrcDelegatorBalanceBeforeTransfer); + + uint256 quadraticSrcDelegatorBalanceAfterTransfer = Math.sqrt(srcDelegatorBalance); // sqrt of balance of sender before transfer + (uint256 oldQuadraticWeight, uint256 newQuadraticWeight) = _writeCheckpoint( + _checkpoints[src], + _add, + quadraticSrcDelegatorBalanceAfterTransfer + ); + + emit DelegateVotesChanged(src, oldWeight, newWeight, oldQuadraticWeight, newQuadraticWeight); } if (dst != address(0)) { + // Linear vote weight update (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); - emit DelegateVotesChanged(dst, oldWeight, newWeight); + + // Quadratic vote weight update + uint256 quadraticDstDelegatorBalanceBeforeTransfer = Math.sqrt(dstDelegatorBalance - amount); // sqrt of balance of recipient before transfer + _writeCheckpoint(_checkpoints[dst], _subtract, quadraticDstDelegatorBalanceBeforeTransfer); + + uint256 quadraticDstDelegatorBalanceAfterTransfer = Math.sqrt(dstDelegatorBalance); + (uint256 oldQuadraticWeight, uint256 newQuadraticWeight) = _writeCheckpoint( + _checkpoints[dst], + _add, + quadraticDstDelegatorBalanceAfterTransfer + ); + + emit DelegateVotesChanged(dst, oldWeight, newWeight, oldQuadraticWeight, newQuadraticWeight); } } } + /** + * @inheritdoc IZeroExVotes + */ + function writeCheckpointAddTotalSupply( + uint256 delta + ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { + _writeCheckpoint(_totalSupplyCheckpoints, _add, delta); + } + + /** + * @inheritdoc IZeroExVotes + */ + function writeCheckpointSubTotalSupply( + uint256 delta + ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { + _writeCheckpoint(_totalSupplyCheckpoints, _subtract, delta); + } + /** * @dev Lookup a value in a list of (sorted) checkpoints. + * Implementation as in openzeppelin/token/ERC20/extensions/ERC20Votes.sol except here we return the entire + * checkpoint rather than part of it */ - function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber) private view returns (uint256) { + function _checkpointsLookup( + Checkpoint[] storage ckpts, + uint256 blockNumber + ) private view returns (Checkpoint memory) { // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. // // Initially we check if the block is recent to narrow the search range. // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). - // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the - // invariant. + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not @@ -153,7 +210,10 @@ contract ZeroExVotes is IZeroExVotes { } } - return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + // Leaving here for posterity this is the original OZ implementation which we've replaced + // return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + Checkpoint memory checkpoint = high > 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, high - 1); + return checkpoint; } function _writeCheckpoint( @@ -163,15 +223,26 @@ contract ZeroExVotes is IZeroExVotes { ) private returns (uint256 oldWeight, uint256 newWeight) { uint256 pos = ckpts.length; - Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0) : _unsafeAccess(ckpts, pos - 1); + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); oldWeight = oldCkpt.votes; newWeight = op(oldWeight, delta); + // uint256 oldQuadraticWeight = oldCkpt.quadraticVotes; + // uint256 newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); + if (pos > 0 && oldCkpt.fromBlock == block.number) { _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(votes); + // _unsafeAccess(ckpts, pos - 1).quadraticVotes = SafeCast.toUint224(quadraticVotes); } else { - ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + ckpts.push( + Checkpoint({ + fromBlock: SafeCast.toUint32(block.number), + votes: SafeCast.toUint224(newWeight), + quadraticVotes: 0 // TODO SafeCast.toUint224(quadraticVotes) + }) + ); } } @@ -185,6 +256,8 @@ contract ZeroExVotes is IZeroExVotes { /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. + * Implementation as in openzeppelin/token/ERC20/extensions/ERC20Votes.sol + * https://github.com/ethereum/solidity/issues/9117 */ function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { assembly { diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index eaede0ef07..6c7af128dd 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -106,7 +106,9 @@ contract ZRXWrappedTokenTest is BaseTest { function testShouldBeAbleToSelfDelegateVotingPower() public { // Check voting power initially is 0 uint256 votingPowerAccount2 = votes.getVotes(account2); + uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); assertEq(votingPowerAccount2, 0); + assertEq(votingQuadraticPowerAccount2, 0); // Wrap ZRX and delegate voting power to themselves vm.startPrank(account2); @@ -117,12 +119,17 @@ contract ZRXWrappedTokenTest is BaseTest { // Check voting power is now = token balance votingPowerAccount2 = votes.getVotes(account2); assertEq(votingPowerAccount2, 100e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingQuadraticPowerAccount2, Math.sqrt(100e18)); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { // Check voting power initially is 0 uint256 votingPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getVotes(account3); assertEq(votingPowerAccount3, 0); + assertEq(votingQuadraticPowerAccount3, 0); // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); @@ -133,5 +140,8 @@ contract ZRXWrappedTokenTest is BaseTest { // Check voting power is now = token balance votingPowerAccount3 = votes.getVotes(account3); assertEq(votingPowerAccount3, 10e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount3 = votes.getVotes(account3); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); } } From 1522fff3778b244d3e599e06d7cff41d0c31e628 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 8 Feb 2023 12:36:15 +0200 Subject: [PATCH 033/106] Update the totalSupply checkpoints updating logic --- contracts/governance/src/IZeroExVotes.sol | 9 +-- contracts/governance/src/ZRXWrappedToken.sol | 4 +- contracts/governance/src/ZeroExVotes.sol | 81 ++++++++++---------- 3 files changed, 42 insertions(+), 52 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 9575ba113e..7223978c4e 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -26,11 +26,6 @@ interface IZeroExVotes { uint224 quadraticVotes; } - enum VotingType { - OneTokenOneVote, - Quadratic - } - /** * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. */ @@ -92,7 +87,5 @@ interface IZeroExVotes { function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; - function writeCheckpointAddTotalSupply(uint256 delta) external returns (uint256 oldWeight, uint256 newWeight); - - function writeCheckpointSubTotalSupply(uint256 delta) external returns (uint256 oldWeight, uint256 newWeight); + function writeCheckpointTotalSupply(uint256 totalSupply) external; } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 2ccc5cb78a..0064fe2afc 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -69,14 +69,14 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { // Snapshots the totalSupply after it has been increased. require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - zeroExVotes.writeCheckpointAddTotalSupply(amount); + zeroExVotes.writeCheckpointTotalSupply(totalSupply()); } function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); // Snapshots the totalSupply after it has been decreased. - zeroExVotes.writeCheckpointSubTotalSupply(amount); + zeroExVotes.writeCheckpointTotalSupply(totalSupply()); } /** diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 644e1b9eaf..cecfd04c08 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -151,19 +151,8 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function writeCheckpointAddTotalSupply( - uint256 delta - ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { - _writeCheckpoint(_totalSupplyCheckpoints, _add, delta); - } - - /** - * @inheritdoc IZeroExVotes - */ - function writeCheckpointSubTotalSupply( - uint256 delta - ) public override onlyToken returns (uint256 oldWeight, uint256 newWeight) { - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, delta); + function writeCheckpointTotalSupply(uint256 totalSupply) public override onlyToken { + _writeCheckpoint(_totalSupplyCheckpoints, totalSupply); } /** @@ -216,44 +205,52 @@ contract ZeroExVotes is IZeroExVotes { return checkpoint; } - function _writeCheckpoint( - Checkpoint[] storage ckpts, - function(uint256, uint256) view returns (uint256) op, - uint256 delta - ) private returns (uint256 oldWeight, uint256 newWeight) { - uint256 pos = ckpts.length; + // function _writeCheckpoint( + // Checkpoint[] storage ckpts, + // function(uint256, uint256) view returns (uint256) op, + // uint256 delta + // ) private returns (uint256 oldWeight, uint256 newWeight) { + // uint256 pos = ckpts.length; + + // Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); + + // oldWeight = oldCkpt.votes; + // newWeight = op(oldWeight, delta); + + // // uint256 oldQuadraticWeight = oldCkpt.quadraticVotes; + // // uint256 newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); + + // if (pos > 0 && oldCkpt.fromBlock == block.number) { + // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + // // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(votes); + // // _unsafeAccess(ckpts, pos - 1).quadraticVotes = SafeCast.toUint224(quadraticVotes); + // } else { + // ckpts.push( + // Checkpoint({ + // fromBlock: SafeCast.toUint32(block.number), + // votes: SafeCast.toUint224(newWeight), + // quadraticVotes: 0 // TODO SafeCast.toUint224(quadraticVotes) + // }) + // ); + // } + // } + /** + * Alternative version of openzeppelin/token/ERC20/extensions/ERC20Votes.sol implementation + * which accepts the new voting weight value directly as opposed to calculating in within the function + * based on a sub/add operation required. + */ + function _writeCheckpoint(Checkpoint[] storage ckpts, uint256 newWeight) private { + uint256 pos = ckpts.length; Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); - oldWeight = oldCkpt.votes; - newWeight = op(oldWeight, delta); - - // uint256 oldQuadraticWeight = oldCkpt.quadraticVotes; - // uint256 newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); - if (pos > 0 && oldCkpt.fromBlock == block.number) { _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); - // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(votes); - // _unsafeAccess(ckpts, pos - 1).quadraticVotes = SafeCast.toUint224(quadraticVotes); } else { - ckpts.push( - Checkpoint({ - fromBlock: SafeCast.toUint32(block.number), - votes: SafeCast.toUint224(newWeight), - quadraticVotes: 0 // TODO SafeCast.toUint224(quadraticVotes) - }) - ); + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); } } - function _add(uint256 a, uint256 b) public pure returns (uint256) { - return a + b; - } - - function _subtract(uint256 a, uint256 b) public pure returns (uint256) { - return a - b; - } - /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. * Implementation as in openzeppelin/token/ERC20/extensions/ERC20Votes.sol From 204b2ee047477e949418e0d04e2dfb19ebd7195d Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 8 Feb 2023 14:02:45 +0200 Subject: [PATCH 034/106] Quadratic voting power transfers and delegations --- contracts/governance/src/IZeroExVotes.sol | 25 ++- contracts/governance/src/ZRXWrappedToken.sol | 11 +- .../governance/src/ZeroExTreasuryGovernor.sol | 18 +- contracts/governance/src/ZeroExVotes.sol | 194 +++++++++++------- .../governance/test/ZRXWrappedTokenTest.t.sol | 141 ++++++++++++- .../test/ZeroExTreasuryGovernor.t.sol | 12 +- 6 files changed, 307 insertions(+), 94 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 7223978c4e..9bd87d65f4 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -85,7 +85,30 @@ interface IZeroExVotes { */ function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); - function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; + /** + * @dev Moves the voting power for an account with balance `delegateBalance` from `srcDelegatee` to `dstDelegatee`. + * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) + */ + function moveEntireVotingPower(address srcDelegatee, address dstDelegatee, uint256 delegateBalance) external; + + /** + * @dev Moves the voting power corresponding to `amount` number of tokens from `srcDelegatee` to `dstDelegatee`. + * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) + * @param srcDelegatee the delegatee we are moving voting power away from + * @param dstDelegatee the delegatee we are moving voting power to + * @param srcDelegateBalance balance of the delegate whose delegatee is `srcDelegatee`. + * This is value _after_ the transfer. + * @param dstDelegateBalance balance of the delegate whose delegatee is `dstDelegatee`. + * This is value _after_ the transfer. + * @param amount The amount of tokens transferred from the source delegate to destination delegate. + */ + function movePartialVotingPower( + address srcDelegatee, + address dstDelegatee, + uint256 srcDelegateBalance, + uint256 dstDelegateBalance, + uint256 amount + ) external; function writeCheckpointTotalSupply(uint256 totalSupply) external; } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 0064fe2afc..82d14575f0 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -53,7 +53,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { super._afterTokenTransfer(from, to, amount); // Move voting power when tokens are transferred. - zeroExVotes.moveVotingPower(delegates(from), delegates(to), balanceOf(from), balanceOf(to), amount); + zeroExVotes.movePartialVotingPower(delegates(from), delegates(to), balanceOf(from), balanceOf(to), amount); } /** @@ -120,13 +120,6 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { emit DelegateChanged(delegator, currentDelegate, delegatee); - // TODO this is a case where the entire delegatee's balance is being moved accross i.e. balanceOf(delegator) == delegatorBalance - zeroExVotes.moveVotingPower( - currentDelegate, - delegatee, - balanceOf(delegator), - balanceOf(delegatee), - delegatorBalance - ); + zeroExVotes.moveEntireVotingPower(currentDelegate, delegatee, delegatorBalance); } } diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index fb3ef1b480..342e7e41d6 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -23,17 +23,19 @@ import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import "./IZeroExVotes.sol"; + contract ZeroExTreasuryGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes { constructor( IVotes _token ) Governor("ZeroExTreasuryGovernor") - GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 100000e18) + GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 5e11) GovernorVotes(_token) {} function quorum(uint256 blockNumber) public pure override returns (uint256) { - return 5000000e18; + return 23e11; } // The following functions are overrides required by Solidity. @@ -49,4 +51,16 @@ contract ZeroExTreasuryGovernor is Governor, GovernorSettings, GovernorCountingS function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) { return super.proposalThreshold(); } + + /** + * Overwritten GovernorVotes implementation + * Read the quadratic voting weight from the token's built in snapshot mechanism (see {Governor-_getVotes}). + */ + function _getVotes( + address account, + uint256 blockNumber, + bytes memory /*params*/ + ) internal view virtual override(Governor, GovernorVotes) returns (uint256) { + return IZeroExVotes(address(token)).getPastQuadraticVotes(account, blockNumber); + } } diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index cecfd04c08..92ff52e947 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -92,6 +92,7 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ + // TODO we probably don't need to keep track of total supply checkpoints as all governance values are fixed numbers function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); @@ -102,48 +103,113 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function moveVotingPower( - address src, - address dst, - uint256 srcDelegatorBalance, - uint256 dstDelegatorBalance, - uint256 amount + function moveEntireVotingPower( + address srcDelegatee, + address dstDelegatee, + uint256 delegateBalance ) public override onlyToken { - if (src != dst && amount > 0) { - if (src != address(0)) { - // Linear vote weight update - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); - - // Quadratic vote weight update - uint256 quadraticSrcDelegatorBalanceBeforeTransfer = Math.sqrt(srcDelegatorBalance + amount); // sqrt of balance of sender before transfer - //todo if (_checkpoints[src] > 0) _writeCheckpoint(_checkpoints[src], _subtract, quadraticSrcDelegatorBalanceBeforeTransfer); - - uint256 quadraticSrcDelegatorBalanceAfterTransfer = Math.sqrt(srcDelegatorBalance); // sqrt of balance of sender before transfer - (uint256 oldQuadraticWeight, uint256 newQuadraticWeight) = _writeCheckpoint( - _checkpoints[src], - _add, - quadraticSrcDelegatorBalanceAfterTransfer + if (srcDelegatee != dstDelegatee && delegateBalance > 0) { + if (srcDelegatee != address(0)) { + uint256 pos = _checkpoints[srcDelegatee].length; + Checkpoint memory oldCkptSrcDelegate = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_checkpoints[srcDelegatee], pos - 1); + + uint256 newLinearBalance = oldCkptSrcDelegate.votes - delegateBalance; + uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes - Math.sqrt(delegateBalance); + + _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); + + emit DelegateVotesChanged( + srcDelegatee, + oldCkptSrcDelegate.votes, + newLinearBalance, + oldCkptSrcDelegate.quadraticVotes, + newQuadraticBalance ); - - emit DelegateVotesChanged(src, oldWeight, newWeight, oldQuadraticWeight, newQuadraticWeight); } - if (dst != address(0)) { - // Linear vote weight update - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + if (dstDelegatee != address(0)) { + uint256 pos = _checkpoints[dstDelegatee].length; + Checkpoint memory oldCkptDstDelegate = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_checkpoints[dstDelegatee], pos - 1); + + uint256 newLinearBalance = oldCkptDstDelegate.votes + delegateBalance; + uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(delegateBalance); - // Quadratic vote weight update - uint256 quadraticDstDelegatorBalanceBeforeTransfer = Math.sqrt(dstDelegatorBalance - amount); // sqrt of balance of recipient before transfer - _writeCheckpoint(_checkpoints[dst], _subtract, quadraticDstDelegatorBalanceBeforeTransfer); + _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); + + emit DelegateVotesChanged( + dstDelegatee, + oldCkptDstDelegate.votes, + newLinearBalance, + oldCkptDstDelegate.quadraticVotes, + newQuadraticBalance + ); + } + } + } - uint256 quadraticDstDelegatorBalanceAfterTransfer = Math.sqrt(dstDelegatorBalance); - (uint256 oldQuadraticWeight, uint256 newQuadraticWeight) = _writeCheckpoint( - _checkpoints[dst], - _add, - quadraticDstDelegatorBalanceAfterTransfer + /** + * @inheritdoc IZeroExVotes + */ + function movePartialVotingPower( + address srcDelegatee, + address dstDelegatee, + uint256 srcDelegateBalance, + uint256 dstDelegateBalance, + uint256 amount + ) public override onlyToken { + if (srcDelegatee != dstDelegatee && amount > 0) { + if (srcDelegatee != address(0)) { + uint256 pos = _checkpoints[srcDelegatee].length; + Checkpoint memory oldCkptSrcDelegate = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_checkpoints[srcDelegatee], pos - 1); + + // Remove the entire source delegator's sqrt balance from delegatee's voting power. + // `srcDelegateBalance` is value _after_ transfer so add the amount that was transferred. + if (pos > 0) + oldCkptSrcDelegate.quadraticVotes -= SafeCast.toUint224(Math.sqrt(srcDelegateBalance + amount)); + + uint256 newLinearBalance = oldCkptSrcDelegate.votes - amount; + uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes + Math.sqrt(srcDelegateBalance); + + _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); + + emit DelegateVotesChanged( + srcDelegatee, + oldCkptSrcDelegate.votes, + newLinearBalance, + oldCkptSrcDelegate.quadraticVotes, + newQuadraticBalance ); + } + + if (dstDelegatee != address(0)) { + uint256 pos = _checkpoints[dstDelegatee].length; + Checkpoint memory oldCkptDstDelegate = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_checkpoints[dstDelegatee], pos - 1); - emit DelegateVotesChanged(dst, oldWeight, newWeight, oldQuadraticWeight, newQuadraticWeight); + // Remove the entire destination delegator's sqrt balance from delegatee's voting power. + // `dstDelegateBalance` is value _after_ transfer so remove the amount that was transferred. + if (pos > 0) + oldCkptDstDelegate.quadraticVotes -= SafeCast.toUint224(Math.sqrt(dstDelegateBalance - amount)); + + uint256 newLinearBalance = oldCkptDstDelegate.votes + amount; + uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(dstDelegateBalance); + + _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); + + emit DelegateVotesChanged( + dstDelegatee, + oldCkptDstDelegate.votes, + newLinearBalance, + oldCkptDstDelegate.quadraticVotes, + newQuadraticBalance + ); } } } @@ -152,7 +218,7 @@ contract ZeroExVotes is IZeroExVotes { * @inheritdoc IZeroExVotes */ function writeCheckpointTotalSupply(uint256 totalSupply) public override onlyToken { - _writeCheckpoint(_totalSupplyCheckpoints, totalSupply); + _writeCheckpoint(_totalSupplyCheckpoints, totalSupply, Math.sqrt(totalSupply)); } /** @@ -168,7 +234,8 @@ contract ZeroExVotes is IZeroExVotes { // // Initially we check if the block is recent to narrow the search range. // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). - // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant. + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the + // invariant. // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not @@ -201,59 +268,36 @@ contract ZeroExVotes is IZeroExVotes { // Leaving here for posterity this is the original OZ implementation which we've replaced // return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; - Checkpoint memory checkpoint = high > 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, high - 1); + Checkpoint memory checkpoint = high == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, high - 1); return checkpoint; } - // function _writeCheckpoint( - // Checkpoint[] storage ckpts, - // function(uint256, uint256) view returns (uint256) op, - // uint256 delta - // ) private returns (uint256 oldWeight, uint256 newWeight) { - // uint256 pos = ckpts.length; - - // Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); - - // oldWeight = oldCkpt.votes; - // newWeight = op(oldWeight, delta); - - // // uint256 oldQuadraticWeight = oldCkpt.quadraticVotes; - // // uint256 newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); - - // if (pos > 0 && oldCkpt.fromBlock == block.number) { - // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); - // // _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(votes); - // // _unsafeAccess(ckpts, pos - 1).quadraticVotes = SafeCast.toUint224(quadraticVotes); - // } else { - // ckpts.push( - // Checkpoint({ - // fromBlock: SafeCast.toUint32(block.number), - // votes: SafeCast.toUint224(newWeight), - // quadraticVotes: 0 // TODO SafeCast.toUint224(quadraticVotes) - // }) - // ); - // } - // } - /** * Alternative version of openzeppelin/token/ERC20/extensions/ERC20Votes.sol implementation - * which accepts the new voting weight value directly as opposed to calculating in within the function - * based on a sub/add operation required. + * which accepts the new voting weight values directly as opposed to calculating these within the function + * based on a addition/subtraction operation. */ - function _writeCheckpoint(Checkpoint[] storage ckpts, uint256 newWeight) private { + function _writeCheckpoint(Checkpoint[] storage ckpts, uint256 voteWeight, uint256 quadraticVoteWeight) private { uint256 pos = ckpts.length; - Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); - if (pos > 0 && oldCkpt.fromBlock == block.number) { - _unsafeAccess(ckpts, pos - 1).votes = SafeCast.toUint224(newWeight); + if (pos > 0 && _unsafeAccess(ckpts, pos - 1).fromBlock == block.number) { + Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); + chpt.votes = SafeCast.toUint224(voteWeight); + chpt.quadraticVotes = SafeCast.toUint224(quadraticVoteWeight); } else { - ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + ckpts.push( + Checkpoint({ + fromBlock: SafeCast.toUint32(block.number), + votes: SafeCast.toUint224(voteWeight), + quadraticVotes: SafeCast.toUint224(quadraticVoteWeight) + }) + ); } } /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. - * Implementation as in openzeppelin/token/ERC20/extensions/ERC20Votes.sol + * Implementation from openzeppelin/token/ERC20/extensions/ERC20Votes.sol * https://github.com/ethereum/solidity/issues/9117 */ function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 6c7af128dd..111d0c4029 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -31,7 +31,7 @@ contract ZRXWrappedTokenTest is BaseTest { vm.startPrank(account1); (token, wToken, votes, , , ) = setupGovernance(); token.transfer(account2, 100e18); - token.transfer(account3, 100e18); + token.transfer(account3, 200e18); vm.stopPrank(); } @@ -141,7 +141,144 @@ contract ZRXWrappedTokenTest is BaseTest { votingPowerAccount3 = votes.getVotes(account3); assertEq(votingPowerAccount3, 10e18); // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount3 = votes.getVotes(account3); + votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); } + + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { + // Check account4 voting power initially is 0 + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + + // Account 2 wraps ZRX and delegates voting power to account4 + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Account 3 also wraps ZRX and delegates voting power to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account4); + vm.stopPrank(); + + IZeroExVotes.Checkpoint memory lastChpt = votes.checkpoints(account4, 0); + console.log(lastChpt.votes); + console.log(lastChpt.quadraticVotes); + + // Check voting power is now = token balance + votingPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 300e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + } + + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnDifferentBlock() public { + // Check account4 voting power initially is 0 + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + + // Account 2 wraps ZRX and delegates voting power to account4 + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Different block height + vm.roll(2); + // Account 3 also wraps ZRX and delegates voting power to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account4); + vm.stopPrank(); + + IZeroExVotes.Checkpoint memory lastChpt = votes.checkpoints(account4, 0); + console.log(lastChpt.votes); + console.log(lastChpt.quadraticVotes); + + // Check voting power is now = token balance + votingPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 300e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + } + + function testComplexDelegationScenario() public { + // Account 2 wraps ZRX and delegates to itself + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 90e18); + wToken.delegate(account2); + vm.stopPrank(); + + uint256 votingPowerAccount2 = votes.getVotes(account2); + uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 90e18); + assertEq(votingQuadraticPowerAccount2, Math.sqrt(90e18)); // 9486832980 + + // Account 3 wraps ZRX and delegates to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingPowerAccount4, 100e18); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2 -> account2 90e18 | 9486832980 + // account3 -> account4 100e18 | 10e9 + + // Account 2 deposits the remaining 10e18 and delegates to account3 + vm.startPrank(account2); + wToken.depositFor(account2, 10e18); + wToken.delegate(account3); + vm.stopPrank(); + + uint256 votingPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 100e18); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2 -> account3 100e18 | 10e18 + // account3 -> account4 100e18 | 10e9 + + // Account 3 delegates to itself + vm.startPrank(account3); + wToken.delegate(account3); + vm.stopPrank(); + + votingPowerAccount3 = votes.getVotes(account3); + votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 200e18); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18) + Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2, account3 -> account3 100e18 | 20e18 + + // Check account2 and account4 no longer have voting power + votingPowerAccount2 = votes.getVotes(account2); + votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 0); + assertEq(votingQuadraticPowerAccount2, 0); + + votingPowerAccount4 = votes.getVotes(account4); + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 4f3596f098..3898275e12 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -36,6 +36,8 @@ contract ZeroExTreasuryGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); (token, wToken, votes, timelock, , governor) = setupGovernance(); + // quorum 5000000e18 + // proposal threshold 250000e18 = sqrt(6.25e46) 5e11 token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); @@ -76,11 +78,11 @@ contract ZeroExTreasuryGovernorTest is BaseTest { } function testShouldReturnCorrectProposalThreshold() public { - assertEq(governor.proposalThreshold(), 100000e18); + assertEq(governor.proposalThreshold(), 5e11); } function testShouldReturnCorrectQuorum() public { - assertEq(governor.quorum(block.number), 5000000e18); + assertEq(governor.quorum(block.number), 23e11); } function testShouldReturnCorrectToken() public { @@ -122,9 +124,9 @@ contract ZeroExTreasuryGovernorTest is BaseTest { // Get vote results (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); - assertEq(votesFor, 10000000e18); - assertEq(votesAgainst, 2000000e18); - assertEq(votesAbstain, 3000000e18); + assertEq(votesFor, Math.sqrt(10000000e18)); + assertEq(votesAgainst, Math.sqrt(2000000e18)); + assertEq(votesAbstain, Math.sqrt(3000000e18)); IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); From c704869b19eab071944c43afb124fa522f2452b6 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 10 Feb 2023 08:42:14 +0200 Subject: [PATCH 035/106] Fix workflow yaml --- .github/workflows/ci.yml | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f678069818..e80d499d98 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,27 +137,24 @@ jobs: exclude: '**/tests' - name: Run Forge build on governance contracts - working-directory: contracts/governance - run: | - forge --version - forge build --sizes + working-directory: ./contracts/governance + run: | + forge --version + forge build --sizes - name: Run Forge tests on governance contracts - working-directory: contracts/governance - run: | - forge test -vvv --gas-report + working-directory: ./contracts/governance + run: | + forge test -vvv --gas-report - name: Run Forge coverage on governance contracts - working-directory: contracts/governance - run: | - forge coverage --report lcov - - - name: Upload the coverage report to Coveralls - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - base-path: ./contracts/governance/ - path-to-lcov: ./contracts/governance/lcov.info - - + working-directory: ./contracts/governance + run: | + forge coverage --report lcov + - name: Upload the coverage report to Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + base-path: ./contracts/governance/ + path-to-lcov: ./contracts/governance/lcov.info From 3ae3b0d7ed163e17b34fda8959ca28b7eeb1c809 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 10 Feb 2023 14:49:13 +0200 Subject: [PATCH 036/106] Initialise ZeroExVotes behind a ERC1967Proxy Test it cannot be reinitialised --- contracts/governance/package.json | 4 ++-- contracts/governance/src/ZeroExVotes.sol | 1 + contracts/governance/test/BaseTest.t.sol | 12 ++++++++++-- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 5 +++++ 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/contracts/governance/package.json b/contracts/governance/package.json index f3bb61ce78..4906739f40 100644 --- a/contracts/governance/package.json +++ b/contracts/governance/package.json @@ -11,8 +11,8 @@ "test": "forge test" }, "repository": { - "type": "git", - "url": "https://github.com/0xProject/protocol.git" + "type": "git", + "url": "https://github.com/0xProject/protocol.git" }, "license": "Apache-2.0", "dependencies": {} diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 92ff52e947..cdf93a7f03 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -36,6 +36,7 @@ contract ZeroExVotes is IZeroExVotes { } function initialize(address _token) public { + require(token == address(0), "ZeroExVotes: Already initialized"); token = _token; } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 0129ff5145..d24491043d 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -22,6 +22,7 @@ pragma solidity ^0.8.17; import "forge-std/Test.sol"; import "forge-std/console.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol"; import "./ZRXMock.sol"; import "../src/ZRXWrappedToken.sol"; import "../src/ZeroExVotes.sol"; @@ -55,15 +56,22 @@ contract BaseTest is Test { // return address(_address); ZRXMock mockZRX = new ZRXMock(); + ZeroExVotes votes = new ZeroExVotes(); - ZRXWrappedToken token = new ZRXWrappedToken(mockZRX, votes); + ERC1967Proxy votesProxy = new ERC1967Proxy(address(votes), new bytes(0)); + votes = ZeroExVotes(address(votesProxy)); + + ZRXWrappedToken token = new ZRXWrappedToken(mockZRX, IZeroExVotes(address(votesProxy))); votes.initialize(address(token)); address[] memory proposers = new address[](0); address[] memory executors = new address[](0); ZeroExTimelock protocolTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); - ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); + ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor( + IVotes(address(votesProxy)), + protocolTimelock + ); protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 111d0c4029..826740ab68 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -103,6 +103,11 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(tokenBalance2, 100e18 - wTokenBalance2 - tokenBalance4); } + function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { + vm.expectRevert("ZeroExVotes: Already initialized"); + votes.initialize(account2); + } + function testShouldBeAbleToSelfDelegateVotingPower() public { // Check voting power initially is 0 uint256 votingPowerAccount2 = votes.getVotes(account2); From e128c5b277ed7f4fde984ed3f1716e6742319e7c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Feb 2023 16:47:13 +0200 Subject: [PATCH 037/106] Remove obsoleted console.logs from test --- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 8 -------- 1 file changed, 8 deletions(-) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 826740ab68..392ba03700 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -171,10 +171,6 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.delegate(account4); vm.stopPrank(); - IZeroExVotes.Checkpoint memory lastChpt = votes.checkpoints(account4, 0); - console.log(lastChpt.votes); - console.log(lastChpt.quadraticVotes); - // Check voting power is now = token balance votingPowerAccount4 = votes.getVotes(account4); assertEq(votingPowerAccount4, 300e18); @@ -206,10 +202,6 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.delegate(account4); vm.stopPrank(); - IZeroExVotes.Checkpoint memory lastChpt = votes.checkpoints(account4, 0); - console.log(lastChpt.votes); - console.log(lastChpt.quadraticVotes); - // Check voting power is now = token balance votingPowerAccount4 = votes.getVotes(account4); assertEq(votingPowerAccount4, 300e18); From 7359a64a0420775d50a92f5564716cc231b2ce33 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Feb 2023 16:53:00 +0200 Subject: [PATCH 038/106] Storage pack Checkpoint enum --- contracts/governance/src/IZeroExVotes.sol | 4 ++-- contracts/governance/src/ZeroExVotes.sol | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 9bd87d65f4..4dc9847d79 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -22,8 +22,8 @@ pragma solidity ^0.8.17; interface IZeroExVotes { struct Checkpoint { uint32 fromBlock; - uint224 votes; - uint224 quadraticVotes; + uint96 votes; + uint48 quadraticVotes; } /** diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index cdf93a7f03..1126c461c1 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -172,7 +172,7 @@ contract ZeroExVotes is IZeroExVotes { // Remove the entire source delegator's sqrt balance from delegatee's voting power. // `srcDelegateBalance` is value _after_ transfer so add the amount that was transferred. if (pos > 0) - oldCkptSrcDelegate.quadraticVotes -= SafeCast.toUint224(Math.sqrt(srcDelegateBalance + amount)); + oldCkptSrcDelegate.quadraticVotes -= SafeCast.toUint48(Math.sqrt(srcDelegateBalance + amount)); uint256 newLinearBalance = oldCkptSrcDelegate.votes - amount; uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes + Math.sqrt(srcDelegateBalance); @@ -197,7 +197,7 @@ contract ZeroExVotes is IZeroExVotes { // Remove the entire destination delegator's sqrt balance from delegatee's voting power. // `dstDelegateBalance` is value _after_ transfer so remove the amount that was transferred. if (pos > 0) - oldCkptDstDelegate.quadraticVotes -= SafeCast.toUint224(Math.sqrt(dstDelegateBalance - amount)); + oldCkptDstDelegate.quadraticVotes -= SafeCast.toUint48(Math.sqrt(dstDelegateBalance - amount)); uint256 newLinearBalance = oldCkptDstDelegate.votes + amount; uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(dstDelegateBalance); @@ -283,14 +283,14 @@ contract ZeroExVotes is IZeroExVotes { if (pos > 0 && _unsafeAccess(ckpts, pos - 1).fromBlock == block.number) { Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); - chpt.votes = SafeCast.toUint224(voteWeight); - chpt.quadraticVotes = SafeCast.toUint224(quadraticVoteWeight); + chpt.votes = SafeCast.toUint96(voteWeight); + chpt.quadraticVotes = SafeCast.toUint48(quadraticVoteWeight); } else { ckpts.push( Checkpoint({ fromBlock: SafeCast.toUint32(block.number), - votes: SafeCast.toUint224(voteWeight), - quadraticVotes: SafeCast.toUint224(quadraticVoteWeight) + votes: SafeCast.toUint96(voteWeight), + quadraticVotes: SafeCast.toUint48(quadraticVoteWeight) }) ); } From a381b82248f439b68f94c743baa1d5d0e0e35043 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Feb 2023 19:15:02 +0200 Subject: [PATCH 039/106] Remove keeping track of total balances for voting --- contracts/governance/src/IZeroExVotes.sol | 11 ++++++ contracts/governance/src/ZeroExVotes.sol | 19 ++++++++-- .../governance/test/ZRXWrappedTokenTest.t.sol | 36 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 4dc9847d79..146ab821bf 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -85,6 +85,17 @@ interface IZeroExVotes { */ function getPastTotalSupply(uint256 blockNumber) external view returns (uint256); + /** + * @dev Retrieve the sqrt of `totalSupply` at the end of `blockNumber`. Note, this value is the square root of the + * sum of all balances. + * It is but NOT the sum of all the sqrt of the delegated votes! + * + * Requirements: + * + * - `blockNumber` must have been already mined + */ + function getPastQuadraticTotalSupply(uint256 blockNumber) external view returns (uint256); + /** * @dev Moves the voting power for an account with balance `delegateBalance` from `srcDelegatee` to `dstDelegatee`. * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 1126c461c1..da2d6b87c3 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -93,14 +93,27 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - // TODO we probably don't need to keep track of total supply checkpoints as all governance values are fixed numbers function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); + // Note that due to the disabled updates of `_totalSupplyCheckpoints` in `writeCheckpointTotalSupply` function + // this always returns 0. Checkpoint memory checkpoint = _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); return checkpoint.votes; } + /** + * @inheritdoc IZeroExVotes + */ + function getPastQuadraticTotalSupply(uint256 blockNumber) public view override returns (uint256) { + require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); + + // Note that due to the disabled updates of `_totalSupplyCheckpoints` in `writeCheckpointTotalSupply` function + // this always returns 0. + Checkpoint memory checkpoint = _checkpointsLookup(_totalSupplyCheckpoints, blockNumber); + return checkpoint.quadraticVotes; + } + /** * @inheritdoc IZeroExVotes */ @@ -219,7 +232,9 @@ contract ZeroExVotes is IZeroExVotes { * @inheritdoc IZeroExVotes */ function writeCheckpointTotalSupply(uint256 totalSupply) public override onlyToken { - _writeCheckpoint(_totalSupplyCheckpoints, totalSupply, Math.sqrt(totalSupply)); + // Currently we don't keep track of total supply checkpoints as all governance settings are fixed numbers + // i.e. governance quorum is not a percentage of total + // _writeCheckpoint(_totalSupplyCheckpoints, totalSupply, Math.sqrt(totalSupply)); } /** diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 392ba03700..5ab9eb8cca 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -103,6 +103,42 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(tokenBalance2, 100e18 - wTokenBalance2 - tokenBalance4); } + function testWrappedZRXTotalsAreCorrect() public { + // Wrap 1e18 and check total supply is correct + vm.startPrank(account2); + token.approve(address(wToken), 1e18); + wToken.depositFor(account2, 1e18); + vm.stopPrank(); + uint256 wTokenBalance = wToken.totalSupply(); + assertEq(wTokenBalance, 1e18); + + // Wrap 2e18 more and check total supply is correct + vm.startPrank(account3); + token.approve(address(wToken), 2e18); + wToken.depositFor(account3, 2e18); + vm.stopPrank(); + wTokenBalance = wToken.totalSupply(); + assertEq(wTokenBalance, 1e18 + 2e18); + + // Unwrap 1e7 and check total supply is correct + vm.startPrank(account2); + wToken.withdrawTo(account2, 1e7); + vm.stopPrank(); + wTokenBalance = wToken.totalSupply(); + assertEq(wTokenBalance, 3e18 - 1e7); + + // Unwrap 8e17 and check total supply is correct + vm.startPrank(account2); + wToken.withdrawTo(account2, 8e17); + vm.stopPrank(); + wTokenBalance = wToken.totalSupply(); + assertEq(wTokenBalance, 3e18 - 1e7 - 8e17); + + // We are not keeping record of total balances so check they are zero + assertEq(votes.getPastTotalSupply(0), 0); + assertEq(votes.getPastQuadraticTotalSupply(0), 0); + } + function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { vm.expectRevert("ZeroExVotes: Already initialized"); votes.initialize(account2); From 9fd66a1bde30a7aedc459b0820478ca5d00ddf14 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 19 Feb 2023 20:57:47 +0200 Subject: [PATCH 040/106] Switch to using the foundry artifact in test --- contracts/governance/ZRXToken.json | 6667 ++-------------------- contracts/governance/foundry.toml | 2 +- contracts/governance/test/BaseTest.t.sol | 18 +- 3 files changed, 493 insertions(+), 6194 deletions(-) diff --git a/contracts/governance/ZRXToken.json b/contracts/governance/ZRXToken.json index 1c29022118..c115c40cef 100644 --- a/contracts/governance/ZRXToken.json +++ b/contracts/governance/ZRXToken.json @@ -1,6202 +1,505 @@ { - "schemaVersion": "2.0.0", - "contractName": "ZRXToken", - "compilerOutput": { - "abi": [ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_spender", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "inputs": [], - "payable": false, - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_from", - "type": "address" - }, - { - "indexed": true, - "name": "_to", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_owner", - "type": "address" - }, - { - "indexed": true, - "name": "_spender", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - } - ], - "devdoc": { - "methods": { - "transferFrom(address,address,uint256)": { - "details": "ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.", - "params": { - "_from": "Address to transfer from.", - "_to": "Address to transfer to.", - "_value": "Amount to transfer." - }, - "return": "Success of transfer." + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" } - } + ], + "payable": false, + "type": "function" }, - "evm": { - "assembly": " /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\n mstore(0x40, 0x60)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5056:5064 10 ** 27 */\n 0x33b2e3c9fd0803ce8000000\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n 0x3\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5208:5286 function ZRXToken() public {... */\n jumpi(tag_1, iszero(callvalue))\n invalid\ntag_1:\ntag_2:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5268:5279 totalSupply */\n sload(0x3)\n sub(exp(0x2, 0xa0), 0x1)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5254:5264 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5265 balances[msg.sender] */\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5253 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5265 balances[msg.sender] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5245:5279 balances[msg.sender] = totalSupply */\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5208:5286 function ZRXToken() public {... */\ntag_3:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\ntag_4:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n return\nstop\n\nsub_0: assembly {\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4935:5288 contract ZRXToken is UnlimitedAllowanceToken {... */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(calldatasize))\n and(div(calldataload(0x0), 0x100000000000000000000000000000000000000000000000000000000), 0xffffffff)\n 0x6fdde03\n dup2\n eq\n tag_2\n jumpi\n dup1\n 0x95ea7b3\n eq\n tag_3\n jumpi\n dup1\n 0x18160ddd\n eq\n tag_4\n jumpi\n dup1\n 0x23b872dd\n eq\n tag_5\n jumpi\n dup1\n 0x313ce567\n eq\n tag_6\n jumpi\n dup1\n 0x70a08231\n eq\n tag_7\n jumpi\n dup1\n 0x95d89b41\n eq\n tag_8\n jumpi\n dup1\n 0xa9059cbb\n eq\n tag_9\n jumpi\n dup1\n 0xdd62ed3e\n eq\n tag_10\n jumpi\n tag_1:\n invalid\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5109:5158 string public constant name = \"0x Protocol Token\" */\n tag_2:\n jumpi(tag_11, iszero(callvalue))\n invalid\n tag_11:\n tag_12\n jump(tag_13)\n tag_12:\n 0x40\n dup1\n mload\n 0x20\n dup1\n dup3\n mstore\n dup4\n mload\n dup2\n dup4\n add\n mstore\n dup4\n mload\n swap2\n swap3\n dup4\n swap3\n swap1\n dup4\n add\n swap2\n dup6\n add\n swap1\n dup1\n dup4\n dup4\n /* \"--CODEGEN--\":18:20 */\n dup3\n iszero\n /* \"--CODEGEN--\":13:16 */\n tag_14\n /* \"--CODEGEN--\":7:12 */\n jumpi\n /* \"--CODEGEN--\":32:37 */\n tag_15:\n /* \"--CODEGEN--\":59:62 */\n dup1\n /* \"--CODEGEN--\":53:58 */\n mload\n /* \"--CODEGEN--\":48:51 */\n dup3\n /* \"--CODEGEN--\":41:47 */\n mstore\n /* \"--CODEGEN--\":93:95 */\n 0x20\n /* \"--CODEGEN--\":88:91 */\n dup4\n /* \"--CODEGEN--\":85:87 */\n gt\n /* \"--CODEGEN--\":78:84 */\n iszero\n /* \"--CODEGEN--\":73:76 */\n tag_14\n /* \"--CODEGEN--\":67:72 */\n jumpi\n /* \"--CODEGEN--\":152:155 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0\n swap1\n swap3\n add\n swap2\n /* \"--CODEGEN--\":117:119 */\n 0x20\n /* \"--CODEGEN--\":108:111 */\n swap2\n dup3\n add\n swap2\n /* \"--CODEGEN--\":130:133 */\n add\n /* \"--CODEGEN--\":172:177 */\n tag_15\n /* \"--CODEGEN--\":167:171 */\n jump\n /* \"--CODEGEN--\":181:184 */\n tag_14:\n /* \"--CODEGEN--\":3:189 */\n pop\n pop\n pop\n swap1\n pop\n swap1\n dup2\n add\n swap1\n 0x1f\n and\n dup1\n iszero\n tag_16\n jumpi\n dup1\n dup3\n sub\n dup1\n mload\n 0x1\n dup4\n 0x20\n sub\n 0x100\n exp\n sub\n not\n and\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n tag_16:\n pop\n swap3\n pop\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_3:\n jumpi(tag_17, iszero(callvalue))\n invalid\n tag_17:\n tag_18\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n calldataload(0x24)\n jump(tag_19)\n tag_18:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n tag_4:\n jumpi(tag_20, iszero(callvalue))\n invalid\n tag_20:\n tag_21\n jump(tag_22)\n tag_21:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_5:\n jumpi(tag_23, iszero(callvalue))\n invalid\n tag_23:\n tag_18\n 0xffffffffffffffffffffffffffffffffffffffff\n calldataload(0x4)\n dup2\n and\n swap1\n calldataload(0x24)\n and\n calldataload(0x44)\n jump(tag_25)\n tag_24:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n tag_6:\n jumpi(tag_26, iszero(callvalue))\n invalid\n tag_26:\n tag_27\n jump(tag_28)\n tag_27:\n 0x40\n dup1\n mload\n 0xff\n swap1\n swap3\n and\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_7:\n jumpi(tag_29, iszero(callvalue))\n invalid\n tag_29:\n tag_21\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n jump(tag_31)\n tag_30:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5164:5201 string public constant symbol = \"ZRX\" */\n tag_8:\n jumpi(tag_32, iszero(callvalue))\n invalid\n tag_32:\n tag_12\n jump(tag_34)\n tag_33:\n 0x40\n dup1\n mload\n 0x20\n dup1\n dup3\n mstore\n dup4\n mload\n dup2\n dup4\n add\n mstore\n dup4\n mload\n swap2\n swap3\n dup4\n swap3\n swap1\n dup4\n add\n swap2\n dup6\n add\n swap1\n dup1\n dup4\n dup4\n /* \"--CODEGEN--\":18:20 */\n dup3\n iszero\n /* \"--CODEGEN--\":13:16 */\n tag_14\n /* \"--CODEGEN--\":7:12 */\n jumpi\n /* \"--CODEGEN--\":32:37 */\n tag_36:\n /* \"--CODEGEN--\":59:62 */\n dup1\n /* \"--CODEGEN--\":53:58 */\n mload\n /* \"--CODEGEN--\":48:51 */\n dup3\n /* \"--CODEGEN--\":41:47 */\n mstore\n /* \"--CODEGEN--\":93:95 */\n 0x20\n /* \"--CODEGEN--\":88:91 */\n dup4\n /* \"--CODEGEN--\":85:87 */\n gt\n /* \"--CODEGEN--\":78:84 */\n iszero\n /* \"--CODEGEN--\":73:76 */\n tag_14\n /* \"--CODEGEN--\":67:72 */\n jumpi\n /* \"--CODEGEN--\":152:155 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0\n swap1\n swap3\n add\n swap2\n /* \"--CODEGEN--\":117:119 */\n 0x20\n /* \"--CODEGEN--\":108:111 */\n swap2\n dup3\n add\n swap2\n /* \"--CODEGEN--\":130:133 */\n add\n /* \"--CODEGEN--\":172:177 */\n tag_15\n /* \"--CODEGEN--\":167:171 */\n jump\n /* \"--CODEGEN--\":181:184 */\n tag_35:\n /* \"--CODEGEN--\":3:189 */\n pop\n pop\n pop\n swap1\n pop\n swap1\n dup2\n add\n swap1\n 0x1f\n and\n dup1\n iszero\n tag_16\n jumpi\n dup1\n dup3\n sub\n dup1\n mload\n 0x1\n dup4\n 0x20\n sub\n 0x100\n exp\n sub\n not\n and\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n tag_37:\n pop\n swap3\n pop\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_9:\n jumpi(tag_38, iszero(callvalue))\n invalid\n tag_38:\n tag_18\n and(calldataload(0x4), 0xffffffffffffffffffffffffffffffffffffffff)\n calldataload(0x24)\n jump(tag_40)\n tag_39:\n 0x40\n dup1\n mload\n swap2\n iszero\n iszero\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_10:\n jumpi(tag_41, iszero(callvalue))\n invalid\n tag_41:\n tag_21\n 0xffffffffffffffffffffffffffffffffffffffff\n calldataload(0x4)\n dup2\n and\n swap1\n calldataload(0x24)\n and\n jump(tag_43)\n tag_42:\n 0x40\n dup1\n mload\n swap2\n dup3\n mstore\n mload\n swap1\n dup2\n swap1\n sub\n 0x20\n add\n swap1\n return\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5109:5158 string public constant name = \"0x Protocol Token\" */\n tag_13:\n 0x40\n dup1\n mload\n dup1\n dup3\n add\n swap1\n swap2\n mstore\n 0x11\n dup2\n mstore\n 0x30782050726f746f636f6c20546f6b656e000000000000000000000000000000\n 0x20\n dup3\n add\n mstore\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_19:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3607:3617 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3583:3587 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n dup2\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3606 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3628 allowed[msg.sender][_spender] */\n swap5\n dup8\n and\n dup1\n dup5\n mstore\n swap5\n dup3\n mstore\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3637 allowed[msg.sender][_spender] = _value */\n dup7\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3647:3685 Approval(msg.sender, _spender, _value) */\n dup1\n mload\n dup7\n dup2\n mstore\n swap1\n mload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3583:3587 bool */\n swap3\n swap5\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3628 allowed[msg.sender][_spender] */\n swap4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3599:3618 allowed[msg.sender] */\n swap3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3647:3685 Approval(msg.sender, _spender, _value) */\n 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925\n swap3\n swap2\n dup2\n swap1\n sub\n swap1\n swap2\n add\n swap1\n log3\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3702:3706 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3523:3713 function approve(address _spender, uint256 _value) returns (bool) {... */\n tag_44:\n swap3\n swap2\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5027:5064 uint256 public totalSupply = 10 ** 27 */\n tag_22:\n sload(0x3)\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_25:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup5\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4451:4455 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n dup2\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4494 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4501 allowed[_from] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4502:4512 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4487:4513 allowed[_from][msg.sender] */\n swap1\n swap6\n and\n dup4\n mstore\n swap4\n dup2\n mstore\n dup4\n dup3\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4542 balances[_from] */\n swap3\n dup3\n mstore\n dup2\n swap1\n mstore\n swap2\n dup3\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4552 balances[_from] >= _value */\n dup4\n swap1\n lt\n dup1\n iszero\n swap1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4575 balances[_from] >= _value && allowance >= _value */\n tag_46\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4569:4575 _value */\n dup3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4556:4565 allowance */\n dup2\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4556:4575 allowance >= _value */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4575 balances[_from] >= _value && allowance >= _value */\n tag_46:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4618 balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to] */\n dup1\n iszero\n tag_47\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4618 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup5\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4613 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4605:4618 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4579:4601 balances[_to] + _value */\n dup4\n dup2\n add\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4579:4618 balances[_to] + _value >= balances[_to] */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4527:4618 balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to] */\n tag_47:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n iszero\n tag_48\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4647 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup6\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4642 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4647 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n dup1\n dup3\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4634:4657 balances[_to] += _value */\n dup1\n sload\n dup8\n add\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4671:4686 balances[_from] */\n swap2\n dup8\n and\n dup2\n mstore\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4671:4696 balances[_from] -= _value */\n dup1\n sload\n dup5\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4069:4081 2 ** 256 - 1 */\n 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4714:4734 allowance < MAX_UINT */\n dup2\n lt\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4710:4805 if (allowance < MAX_UINT) {... */\n iszero\n tag_49\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4768 allowed[_from] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup7\n and\n 0x0\n swap1\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4761 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4768 allowed[_from] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4769:4779 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4780 allowed[_from][msg.sender] */\n swap1\n swap5\n and\n dup4\n mstore\n swap3\n swap1\n mstore\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4754:4790 allowed[_from][msg.sender] -= _value */\n dup1\n sload\n dup5\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4710:4805 if (allowance < MAX_UINT) {... */\n tag_49:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4834:4837 _to */\n dup4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n 0xffffffffffffffffffffffffffffffffffffffff\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4827:4832 _from */\n dup6\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n 0xffffffffffffffffffffffffffffffffffffffff\n and\n 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4839:4845 _value */\n dup6\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4818:4846 Transfer(_from, _to, _value) */\n mload(0x40)\n dup1\n dup3\n dup2\n mstore\n 0x20\n add\n swap2\n pop\n pop\n mload(0x40)\n dup1\n swap2\n sub\n swap1\n log3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4867:4871 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4860:4871 return true */\n swap2\n pop\n jump(tag_50)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_48:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4909:4914 false */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4902:4914 return false */\n swap2\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4523:4925 if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_50:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4369:4931 function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {... */\n tag_45:\n pop\n swap4\n swap3\n pop\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n tag_28:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5019:5021 18 */\n 0x12\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":4986:5021 uint8 public constant decimals = 18 */\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_31:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3494:3510 balances[_owner] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3468:3475 uint256 */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3494:3510 balances[_owner] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3415:3517 function balanceOf(address _owner) constant returns (uint256) {... */\n tag_51:\n swap2\n swap1\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":5164:5201 string public constant symbol = \"ZRX\" */\n tag_34:\n 0x40\n dup1\n mload\n dup1\n dup3\n add\n swap1\n swap2\n mstore\n 0x3\n dup2\n mstore\n 0x5a52580000000000000000000000000000000000000000000000000000000000\n 0x20\n dup3\n add\n mstore\n dup2\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_40:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2644:2654 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2546:2550 bool */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2655 balances[msg.sender] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n dup2\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2665 balances[msg.sender] >= _value */\n dup3\n swap1\n lt\n dup1\n iszero\n swap1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2708 balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to] */\n tag_53\n jumpi\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2708 balances[_to] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup4\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2703 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2695:2708 balances[_to] */\n swap1\n dup2\n mstore\n 0x20\n dup2\n swap1\n mstore\n 0x40\n swap1\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2669:2691 balances[_to] + _value */\n dup3\n dup2\n add\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2669:2708 balances[_to] + _value >= balances[_to] */\n lt\n iszero\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2635:2708 balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to] */\n tag_53:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n iszero\n tag_54\n jumpi\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n 0xffffffffffffffffffffffffffffffffffffffff\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2733:2743 msg.sender */\n caller\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n dup2\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2732 balances */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2744 balances[msg.sender] */\n dup2\n dup2\n mstore\n 0x20\n dup2\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2724:2754 balances[msg.sender] -= _value */\n dup1\n sload\n dup9\n swap1\n sub\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2781 balances[_to] */\n swap4\n dup8\n and\n dup1\n dup4\n mstore\n swap2\n dup5\n swap1\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2791 balances[_to] += _value */\n dup1\n sload\n dup8\n add\n swap1\n sstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2805:2838 Transfer(msg.sender, _to, _value) */\n dup4\n mload\n dup7\n dup2\n mstore\n swap4\n mload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2768:2781 balances[_to] */\n swap2\n swap4\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2805:2838 Transfer(msg.sender, _to, _value) */\n 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\n swap3\n swap1\n dup2\n swap1\n sub\n swap1\n swap2\n add\n swap1\n log3\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2859:2863 true */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2852:2863 return true */\n jump(tag_44)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_54:\n pop\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2901:2906 false */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2894:2906 return false */\n jump(tag_44)\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2631:2917 if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {... */\n tag_55:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":2490:2923 function transfer(address _to, uint256 _value) returns (bool) {... */\n tag_52:\n swap3\n swap2\n pop\n pop\n jump\t// out\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_43:\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n 0xffffffffffffffffffffffffffffffffffffffff\n dup1\n dup4\n and\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3790:3797 uint256 */\n 0x0\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n swap1\n dup2\n mstore\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3823 allowed */\n 0x1\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3831 allowed[_owner] */\n 0x20\n swap1\n dup2\n mstore\n 0x40\n dup1\n dup4\n sha3\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3816:3841 allowed[_owner][_spender] */\n swap4\n dup6\n and\n dup4\n mstore\n swap3\n swap1\n mstore\n sha3\n sload\n /* \"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":3719:3848 function allowance(address _owner, address _spender) constant returns (uint256) {... */\n tag_56:\n swap3\n swap2\n pop\n pop\n jump\t// out\n}\n", - "bytecode": { - "linkReferences": {}, - "object": "0x60606040526b033b2e3c9fd0803ce8000000600355341561001c57fe5b5b600354600160a060020a0333166000908152602081905260409020555b5b61078d8061004a6000396000f300606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a72305820b706acfbca681a492299535a7c37132e2b3dd130311b59423d04596c21a624170029", - "opcodes": "PUSH1 0x60 PUSH1 0x40 MSTORE PUSH12 0x33B2E3C9FD0803CE8000000 PUSH1 0x3 SSTORE CALLVALUE ISZERO PUSH2 0x1C JUMPI INVALID JUMPDEST JUMPDEST PUSH1 0x3 SLOAD PUSH1 0x1 PUSH1 0xA0 PUSH1 0x2 EXP SUB CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SSTORE JUMPDEST JUMPDEST PUSH2 0x78D DUP1 PUSH2 0x4A PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE CALLDATASIZE ISZERO PUSH2 0x96 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x98 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x146 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x186 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x1A8 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1EE JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x214 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x24F JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x2FD JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x33D JUMPI JUMPDEST INVALID JUMPDEST CALLVALUE ISZERO PUSH2 0xA0 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x37E JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x14E JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x3B5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x18E JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH2 0x42D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1B0 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1F6 JUMPI INVALID JUMPDEST PUSH2 0x1FE PUSH2 0x5D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x21C JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x5D9 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x257 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x305 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x63C JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x345 JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x727 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE DUP1 DUP4 SHA3 DUP7 SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP5 SWAP4 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP6 AND DUP4 MSTORE SWAP4 DUP2 MSTORE DUP4 DUP3 SHA3 SLOAD SWAP3 DUP3 MSTORE DUP2 SWAP1 MSTORE SWAP2 DUP3 SHA3 SLOAD DUP4 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x483 JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x4B6 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x5C6 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x558 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x5CB JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 SHA3 SLOAD DUP3 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x699 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x718 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 DUP1 SLOAD DUP9 SWAP1 SUB SWAP1 SSTORE SWAP4 DUP8 AND DUP1 DUP4 MSTORE SWAP2 DUP5 SWAP1 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP4 MLOAD DUP7 DUP2 MSTORE SWAP4 MLOAD SWAP2 SWAP4 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x427 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x427 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 SLOAD JUMPDEST SWAP3 SWAP2 POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 SHA3 0xb7 MOD 0xac 0xfb 0xca PUSH9 0x1A492299535A7C3713 0x2e 0x2b 0x3d 0xd1 ADDRESS BALANCE 0x1b MSIZE TIMESTAMP 0x3d DIV MSIZE PUSH13 0x21A62417002900000000000000 ", - "sourceMap": "4935:353:0:-;;;5056:8;5027:37;;5208:78;;;;;;;5268:11;;-1:-1:-1;;;;;5254:10:0;5245:20;:8;:20;;;;;;;;;;:34;5208:78;4935:353;;;;;;;" - }, - "deployedBytecode": { - "linkReferences": {}, - "object": "0x606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a72305820b706acfbca681a492299535a7c37132e2b3dd130311b59423d04596c21a624170029", - "opcodes": "PUSH1 0x60 PUSH1 0x40 MSTORE CALLDATASIZE ISZERO PUSH2 0x96 JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0x6FDDE03 DUP2 EQ PUSH2 0x98 JUMPI DUP1 PUSH4 0x95EA7B3 EQ PUSH2 0x146 JUMPI DUP1 PUSH4 0x18160DDD EQ PUSH2 0x186 JUMPI DUP1 PUSH4 0x23B872DD EQ PUSH2 0x1A8 JUMPI DUP1 PUSH4 0x313CE567 EQ PUSH2 0x1EE JUMPI DUP1 PUSH4 0x70A08231 EQ PUSH2 0x214 JUMPI DUP1 PUSH4 0x95D89B41 EQ PUSH2 0x24F JUMPI DUP1 PUSH4 0xA9059CBB EQ PUSH2 0x2FD JUMPI DUP1 PUSH4 0xDD62ED3E EQ PUSH2 0x33D JUMPI JUMPDEST INVALID JUMPDEST CALLVALUE ISZERO PUSH2 0xA0 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x37E JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x14E JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x3B5 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x18E JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH2 0x42D JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1B0 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH1 0x44 CALLDATALOAD PUSH2 0x433 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x1F6 JUMPI INVALID JUMPDEST PUSH2 0x1FE PUSH2 0x5D4 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0xFF SWAP1 SWAP3 AND DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x21C JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH2 0x5D9 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x257 JUMPI INVALID JUMPDEST PUSH2 0xA8 PUSH2 0x605 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 DUP3 ISZERO PUSH2 0x10C JUMPI JUMPDEST DUP1 MLOAD DUP3 MSTORE PUSH1 0x20 DUP4 GT ISZERO PUSH2 0x10C JUMPI PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0 SWAP1 SWAP3 ADD SWAP2 PUSH1 0x20 SWAP2 DUP3 ADD SWAP2 ADD PUSH2 0xCE JUMP JUMPDEST POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x138 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x305 JUMPI INVALID JUMPDEST PUSH2 0x172 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD AND PUSH1 0x24 CALLDATALOAD PUSH2 0x63C JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 ISZERO ISZERO DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST CALLVALUE ISZERO PUSH2 0x345 JUMPI INVALID JUMPDEST PUSH2 0x196 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF PUSH1 0x4 CALLDATALOAD DUP2 AND SWAP1 PUSH1 0x24 CALLDATALOAD AND PUSH2 0x727 JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD SWAP2 DUP3 MSTORE MLOAD SWAP1 DUP2 SWAP1 SUB PUSH1 0x20 ADD SWAP1 RETURN JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x11 DUP2 MSTORE PUSH32 0x30782050726F746F636F6C20546F6B656E000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP5 DUP8 AND DUP1 DUP5 MSTORE SWAP5 DUP3 MSTORE DUP1 DUP4 SHA3 DUP7 SWAP1 SSTORE DUP1 MLOAD DUP7 DUP2 MSTORE SWAP1 MLOAD SWAP3 SWAP5 SWAP4 SWAP3 PUSH32 0x8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925 SWAP3 SWAP2 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH1 0x3 SLOAD DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP5 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP6 AND DUP4 MSTORE SWAP4 DUP2 MSTORE DUP4 DUP3 SHA3 SLOAD SWAP3 DUP3 MSTORE DUP2 SWAP1 MSTORE SWAP2 DUP3 SHA3 SLOAD DUP4 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x483 JUMPI POP DUP3 DUP2 LT ISZERO JUMPDEST DUP1 ISZERO PUSH2 0x4B6 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP5 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP4 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x5C6 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP6 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP1 DUP3 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE SWAP2 DUP8 AND DUP2 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 LT ISZERO PUSH2 0x558 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP7 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 CALLER SWAP1 SWAP5 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 DUP1 SLOAD DUP5 SWAP1 SUB SWAP1 SSTORE JUMPDEST DUP4 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND DUP6 PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF AND PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF DUP6 PUSH1 0x40 MLOAD DUP1 DUP3 DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 LOG3 PUSH1 0x1 SWAP2 POP PUSH2 0x5CB JUMP JUMPDEST PUSH1 0x0 SWAP2 POP JUMPDEST JUMPDEST POP SWAP4 SWAP3 POP POP POP JUMP JUMPDEST PUSH1 0x12 DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP2 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD JUMPDEST SWAP2 SWAP1 POP JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0x3 DUP2 MSTORE PUSH32 0x5A52580000000000000000000000000000000000000000000000000000000000 PUSH1 0x20 DUP3 ADD MSTORE DUP2 JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 DUP2 SHA3 SLOAD DUP3 SWAP1 LT DUP1 ISZERO SWAP1 PUSH2 0x699 JUMPI POP PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x20 DUP2 SWAP1 MSTORE PUSH1 0x40 SWAP1 SHA3 SLOAD DUP3 DUP2 ADD LT ISZERO JUMPDEST ISZERO PUSH2 0x718 JUMPI PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF CALLER DUP2 AND PUSH1 0x0 DUP2 DUP2 MSTORE PUSH1 0x20 DUP2 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 DUP1 SLOAD DUP9 SWAP1 SUB SWAP1 SSTORE SWAP4 DUP8 AND DUP1 DUP4 MSTORE SWAP2 DUP5 SWAP1 SHA3 DUP1 SLOAD DUP8 ADD SWAP1 SSTORE DUP4 MLOAD DUP7 DUP2 MSTORE SWAP4 MLOAD SWAP2 SWAP4 PUSH32 0xDDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF SWAP3 SWAP1 DUP2 SWAP1 SUB SWAP1 SWAP2 ADD SWAP1 LOG3 POP PUSH1 0x1 PUSH2 0x427 JUMP JUMPDEST POP PUSH1 0x0 PUSH2 0x427 JUMP JUMPDEST JUMPDEST SWAP3 SWAP2 POP POP JUMP JUMPDEST PUSH20 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF DUP1 DUP4 AND PUSH1 0x0 SWAP1 DUP2 MSTORE PUSH1 0x1 PUSH1 0x20 SWAP1 DUP2 MSTORE PUSH1 0x40 DUP1 DUP4 SHA3 SWAP4 DUP6 AND DUP4 MSTORE SWAP3 SWAP1 MSTORE SHA3 SLOAD JUMPDEST SWAP3 SWAP2 POP POP JUMP STOP LOG1 PUSH6 0x627A7A723058 SHA3 0xb7 MOD 0xac 0xfb 0xca PUSH9 0x1A492299535A7C3713 0x2e 0x2b 0x3d 0xd1 ADDRESS BALANCE 0x1b MSIZE TIMESTAMP 0x3d DIV MSIZE PUSH13 0x21A62417002900000000000000 ", - "sourceMap": "4935:353:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3523:190:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5027:37;;;;;;;;;;;;;;;;;;;;;;;;;;4369:562;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4986:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3415:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5164:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2490:433:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3719:129;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;:::o;3523:190::-;3599:19;3607:10;3599:19;;3583:4;3599:19;;;:7;:19;;;;;;;;:29;;;;;;;;;;;;:38;;;3647;;;;;;;3583:4;;3599:29;:19;3647:38;;;;;;;;;;;-1:-1:-1;3702:4:0;3523:190;;;;;:::o;5027:37::-;;;;:::o;4369:562::-;4487:14;;;;4451:4;4487:14;;;:7;:14;;;;;;;;4502:10;4487:26;;;;;;;;;;;;4527:15;;;;;;;;;;:25;;;;;;:48;;;4569:6;4556:9;:19;;4527:48;:91;;;;-1:-1:-1;4605:13:0;;;:8;:13;;;;;;;;;;;4579:22;;;:39;;4527:91;4523:402;;;4634:13;;;;:8;:13;;;;;;;;;;;:23;;;;;;4671:15;;;;;;:25;;;;;;;4069:12;4714:20;;4710:95;;;4754:14;;;;;;;;:7;:14;;;;;;;;4769:10;4754:26;;;;;;;;;:36;;;;;;;4710:95;4834:3;4818:28;;4827:5;4818:28;;;4839:6;4818:28;;;;;;;;;;;;;;;;;;4867:4;4860:11;;;;4523:402;4909:5;4902:12;;4523:402;4369:562;;;;;;;:::o;4986:35::-;5019:2;4986:35;:::o;3415:102::-;3494:16;;;3468:7;3494:16;;;;;;;;;;;3415:102;;;;:::o;5164:37::-;;;;;;;;;;;;;;;;;;;:::o;2490:433::-;2635:20;2644:10;2635:20;2546:4;2635:20;;;;;;;;;;;:30;;;;;;:73;;-1:-1:-1;2695:13:0;;;:8;:13;;;;;;;;;;;2669:22;;;:39;;2635:73;2631:286;;;2724:20;2733:10;2724:20;;:8;:20;;;;;;;;;;;:30;;;;;;;2768:13;;;;;;;;;;:23;;;;;;2805:33;;;;;;;2768:13;;2805:33;;;;;;;;;;;-1:-1:-1;2859:4:0;2852:11;;2631:286;-1:-1:-1;2901:5:0;2894:12;;2631:286;2490:433;;;;;:::o;3719:129::-;3816:15;;;;3790:7;3816:15;;;:7;:15;;;;;;;;:25;;;;;;;;;;3719:129;;;;;:::o" - }, - "gasEstimates": { - "creation": { - "codeDepositCost": "386600", - "executionCost": "40780", - "totalCost": "427380" + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" }, - "external": { - "allowance(address,address)": "737", - "approve(address,uint256)": "22218", - "balanceOf(address)": "579", - "decimals()": "270", - "name()": "530", - "symbol()": "662", - "totalSupply()": "417", - "transfer(address,uint256)": "43393", - "transferFrom(address,address,uint256)": "64116" + { + "name": "_value", + "type": "uint256" } - }, - "legacyAssembly": { - ".code": [ - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "60" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4935, - "end": 5288, - "name": "MSTORE" - }, - { - "begin": 5056, - "end": 5064, - "name": "PUSH", - "value": "33B2E3C9FD0803CE8000000" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH", - "value": "3" - }, - { - "begin": 5027, - "end": 5064, - "name": "SSTORE" - }, - { - "begin": 5208, - "end": 5286, - "name": "CALLVALUE" - }, - { - "begin": 5208, - "end": 5286, - "name": "ISZERO" - }, - { - "begin": 5208, - "end": 5286, - "name": "PUSH [tag]", - "value": "1" - }, - { - "begin": 5208, - "end": 5286, - "name": "JUMPI" - }, - { - "begin": 5208, - "end": 5286, - "name": "INVALID" - }, - { - "begin": 5208, - "end": 5286, - "name": "tag", - "value": "1" - }, - { - "begin": 5208, - "end": 5286, - "name": "JUMPDEST" - }, - { - "begin": 5208, - "end": 5286, - "name": "tag", - "value": "2" - }, - { - "begin": 5208, - "end": 5286, - "name": "JUMPDEST" - }, - { - "begin": 5268, - "end": 5279, - "name": "PUSH", - "value": "3" - }, - { - "begin": 5268, - "end": 5279, - "name": "SLOAD" - }, - { - "begin": -1, - "end": -1, - "name": "PUSH", - "value": "1" - }, - { - "begin": -1, - "end": -1, - "name": "PUSH", - "value": "A0" - }, - { - "begin": -1, - "end": -1, - "name": "PUSH", - "value": "2" - }, - { - "begin": -1, - "end": -1, - "name": "EXP" - }, - { - "begin": -1, - "end": -1, - "name": "SUB" - }, - { - "begin": 5254, - "end": 5264, - "name": "CALLER" - }, - { - "begin": 5245, - "end": 5265, - "name": "AND" - }, - { - "begin": 5245, - "end": 5253, - "name": "PUSH", - "value": "0" - }, - { - "begin": 5245, - "end": 5265, - "name": "SWAP1" - }, - { - "begin": 5245, - "end": 5265, - "name": "DUP2" - }, - { - "begin": 5245, - "end": 5265, - "name": "MSTORE" - }, - { - "begin": 5245, - "end": 5265, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5245, - "end": 5265, - "name": "DUP2" - }, - { - "begin": 5245, - "end": 5265, - "name": "SWAP1" - }, - { - "begin": 5245, - "end": 5265, - "name": "MSTORE" - }, - { - "begin": 5245, - "end": 5265, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5245, - "end": 5265, - "name": "SWAP1" - }, - { - "begin": 5245, - "end": 5265, - "name": "SHA3" - }, - { - "begin": 5245, - "end": 5279, - "name": "SSTORE" - }, - { - "begin": 5208, - "end": 5286, - "name": "tag", - "value": "3" - }, - { - "begin": 5208, - "end": 5286, - "name": "JUMPDEST" - }, - { - "begin": 4935, - "end": 5288, - "name": "tag", - "value": "4" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPDEST" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH #[$]", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [$]", - "value": "0000000000000000000000000000000000000000000000000000000000000000" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4935, - "end": 5288, - "name": "CODECOPY" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4935, - "end": 5288, - "name": "RETURN" - } - ], - ".data": { - "0": { - ".code": [ - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "60" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4935, - "end": 5288, - "name": "MSTORE" - }, - { - "begin": 4935, - "end": 5288, - "name": "CALLDATASIZE" - }, - { - "begin": 4935, - "end": 5288, - "name": "ISZERO" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "1" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "FFFFFFFF" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "100000000000000000000000000000000000000000000000000000000" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4935, - "end": 5288, - "name": "CALLDATALOAD" - }, - { - "begin": 4935, - "end": 5288, - "name": "DIV" - }, - { - "begin": 4935, - "end": 5288, - "name": "AND" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "6FDDE03" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP2" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "2" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "95EA7B3" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "3" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "18160DDD" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "4" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "23B872DD" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "5" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "313CE567" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "6" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "70A08231" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "7" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "95D89B41" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "8" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "A9059CBB" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "9" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "DUP1" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH", - "value": "DD62ED3E" - }, - { - "begin": 4935, - "end": 5288, - "name": "EQ" - }, - { - "begin": 4935, - "end": 5288, - "name": "PUSH [tag]", - "value": "10" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPI" - }, - { - "begin": 4935, - "end": 5288, - "name": "tag", - "value": "1" - }, - { - "begin": 4935, - "end": 5288, - "name": "JUMPDEST" - }, - { - "begin": 4935, - "end": 5288, - "name": "INVALID" - }, - { - "begin": 5109, - "end": 5158, - "name": "tag", - "value": "2" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMPDEST" - }, - { - "begin": 5109, - "end": 5158, - "name": "CALLVALUE" - }, - { - "begin": 5109, - "end": 5158, - "name": "ISZERO" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH [tag]", - "value": "11" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMPI" - }, - { - "begin": 5109, - "end": 5158, - "name": "INVALID" - }, - { - "begin": 5109, - "end": 5158, - "name": "tag", - "value": "11" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMPDEST" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH [tag]", - "value": "12" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH [tag]", - "value": "13" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMP" - }, - { - "begin": 5109, - "end": 5158, - "name": "tag", - "value": "12" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMPDEST" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "MLOAD" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP3" - }, - { - "begin": 5109, - "end": 5158, - "name": "MSTORE" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "MLOAD" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "ADD" - }, - { - "begin": 5109, - "end": 5158, - "name": "MSTORE" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "MLOAD" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP3" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP3" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "ADD" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP6" - }, - { - "begin": 5109, - "end": 5158, - "name": "ADD" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP4" - }, - { - "begin": 18, - "end": 20, - "name": "DUP3" - }, - { - "begin": 18, - "end": 20, - "name": "ISZERO" - }, - { - "begin": 13, - "end": 16, - "name": "PUSH [tag]", - "value": "14" - }, - { - "begin": 7, - "end": 12, - "name": "JUMPI" - }, - { - "begin": 32, - "end": 37, - "name": "tag", - "value": "15" - }, - { - "begin": 32, - "end": 37, - "name": "JUMPDEST" - }, - { - "begin": 59, - "end": 62, - "name": "DUP1" - }, - { - "begin": 53, - "end": 58, - "name": "MLOAD" - }, - { - "begin": 48, - "end": 51, - "name": "DUP3" - }, - { - "begin": 41, - "end": 47, - "name": "MSTORE" - }, - { - "begin": 93, - "end": 95, - "name": "PUSH", - "value": "20" - }, - { - "begin": 88, - "end": 91, - "name": "DUP4" - }, - { - "begin": 85, - "end": 87, - "name": "GT" - }, - { - "begin": 78, - "end": 84, - "name": "ISZERO" - }, - { - "begin": 73, - "end": 76, - "name": "PUSH [tag]", - "value": "14" - }, - { - "begin": 67, - "end": 72, - "name": "JUMPI" - }, - { - "begin": 152, - "end": 155, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP1" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP3" - }, - { - "begin": 152, - "end": 155, - "name": "ADD" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP2" - }, - { - "begin": 117, - "end": 119, - "name": "PUSH", - "value": "20" - }, - { - "begin": 108, - "end": 111, - "name": "SWAP2" - }, - { - "begin": 108, - "end": 111, - "name": "DUP3" - }, - { - "begin": 108, - "end": 111, - "name": "ADD" - }, - { - "begin": 108, - "end": 111, - "name": "SWAP2" - }, - { - "begin": 130, - "end": 133, - "name": "ADD" - }, - { - "begin": 172, - "end": 177, - "name": "PUSH [tag]", - "value": "15" - }, - { - "begin": 167, - "end": 171, - "name": "JUMP" - }, - { - "begin": 181, - "end": 184, - "name": "tag", - "value": "14" - }, - { - "begin": 181, - "end": 184, - "name": "JUMPDEST" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP2" - }, - { - "begin": 3, - "end": 189, - "name": "ADD" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "1F" - }, - { - "begin": 3, - "end": 189, - "name": "AND" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "ISZERO" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH [tag]", - "value": "16" - }, - { - "begin": 3, - "end": 189, - "name": "JUMPI" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP3" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "MLOAD" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP4" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "100" - }, - { - "begin": 3, - "end": 189, - "name": "EXP" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "NOT" - }, - { - "begin": 3, - "end": 189, - "name": "AND" - }, - { - "begin": 3, - "end": 189, - "name": "DUP2" - }, - { - "begin": 3, - "end": 189, - "name": "MSTORE" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3, - "end": 189, - "name": "ADD" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP2" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "tag", - "value": "16" - }, - { - "begin": 3, - "end": 189, - "name": "JUMPDEST" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP3" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3, - "end": 189, - "name": "MLOAD" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP2" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "RETURN" - }, - { - "begin": 3523, - "end": 3713, - "name": "tag", - "value": "3" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPDEST" - }, - { - "begin": 3523, - "end": 3713, - "name": "CALLVALUE" - }, - { - "begin": 3523, - "end": 3713, - "name": "ISZERO" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH [tag]", - "value": "17" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPI" - }, - { - "begin": 3523, - "end": 3713, - "name": "INVALID" - }, - { - "begin": 3523, - "end": 3713, - "name": "tag", - "value": "17" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPDEST" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH [tag]", - "value": "18" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH", - "value": "4" - }, - { - "begin": 3523, - "end": 3713, - "name": "CALLDATALOAD" - }, - { - "begin": 3523, - "end": 3713, - "name": "AND" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH", - "value": "24" - }, - { - "begin": 3523, - "end": 3713, - "name": "CALLDATALOAD" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH [tag]", - "value": "19" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMP" - }, - { - "begin": 3523, - "end": 3713, - "name": "tag", - "value": "18" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPDEST" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3523, - "end": 3713, - "name": "DUP1" - }, - { - "begin": 3523, - "end": 3713, - "name": "MLOAD" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP2" - }, - { - "begin": 3523, - "end": 3713, - "name": "ISZERO" - }, - { - "begin": 3523, - "end": 3713, - "name": "ISZERO" - }, - { - "begin": 3523, - "end": 3713, - "name": "DUP3" - }, - { - "begin": 3523, - "end": 3713, - "name": "MSTORE" - }, - { - "begin": 3523, - "end": 3713, - "name": "MLOAD" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP1" - }, - { - "begin": 3523, - "end": 3713, - "name": "DUP2" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP1" - }, - { - "begin": 3523, - "end": 3713, - "name": "SUB" - }, - { - "begin": 3523, - "end": 3713, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3523, - "end": 3713, - "name": "ADD" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP1" - }, - { - "begin": 3523, - "end": 3713, - "name": "RETURN" - }, - { - "begin": 5027, - "end": 5064, - "name": "tag", - "value": "4" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMPDEST" - }, - { - "begin": 5027, - "end": 5064, - "name": "CALLVALUE" - }, - { - "begin": 5027, - "end": 5064, - "name": "ISZERO" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH [tag]", - "value": "20" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMPI" - }, - { - "begin": 5027, - "end": 5064, - "name": "INVALID" - }, - { - "begin": 5027, - "end": 5064, - "name": "tag", - "value": "20" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMPDEST" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH [tag]", - "value": "21" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH [tag]", - "value": "22" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMP" - }, - { - "begin": 5027, - "end": 5064, - "name": "tag", - "value": "21" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMPDEST" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5027, - "end": 5064, - "name": "DUP1" - }, - { - "begin": 5027, - "end": 5064, - "name": "MLOAD" - }, - { - "begin": 5027, - "end": 5064, - "name": "SWAP2" - }, - { - "begin": 5027, - "end": 5064, - "name": "DUP3" - }, - { - "begin": 5027, - "end": 5064, - "name": "MSTORE" - }, - { - "begin": 5027, - "end": 5064, - "name": "MLOAD" - }, - { - "begin": 5027, - "end": 5064, - "name": "SWAP1" - }, - { - "begin": 5027, - "end": 5064, - "name": "DUP2" - }, - { - "begin": 5027, - "end": 5064, - "name": "SWAP1" - }, - { - "begin": 5027, - "end": 5064, - "name": "SUB" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5027, - "end": 5064, - "name": "ADD" - }, - { - "begin": 5027, - "end": 5064, - "name": "SWAP1" - }, - { - "begin": 5027, - "end": 5064, - "name": "RETURN" - }, - { - "begin": 4369, - "end": 4931, - "name": "tag", - "value": "5" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPDEST" - }, - { - "begin": 4369, - "end": 4931, - "name": "CALLVALUE" - }, - { - "begin": 4369, - "end": 4931, - "name": "ISZERO" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH [tag]", - "value": "23" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPI" - }, - { - "begin": 4369, - "end": 4931, - "name": "INVALID" - }, - { - "begin": 4369, - "end": 4931, - "name": "tag", - "value": "23" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPDEST" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH [tag]", - "value": "18" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "4" - }, - { - "begin": 4369, - "end": 4931, - "name": "CALLDATALOAD" - }, - { - "begin": 4369, - "end": 4931, - "name": "DUP2" - }, - { - "begin": 4369, - "end": 4931, - "name": "AND" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP1" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "24" - }, - { - "begin": 4369, - "end": 4931, - "name": "CALLDATALOAD" - }, - { - "begin": 4369, - "end": 4931, - "name": "AND" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "44" - }, - { - "begin": 4369, - "end": 4931, - "name": "CALLDATALOAD" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH [tag]", - "value": "25" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMP" - }, - { - "begin": 4369, - "end": 4931, - "name": "tag", - "value": "24" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPDEST" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4369, - "end": 4931, - "name": "DUP1" - }, - { - "begin": 4369, - "end": 4931, - "name": "MLOAD" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP2" - }, - { - "begin": 4369, - "end": 4931, - "name": "ISZERO" - }, - { - "begin": 4369, - "end": 4931, - "name": "ISZERO" - }, - { - "begin": 4369, - "end": 4931, - "name": "DUP3" - }, - { - "begin": 4369, - "end": 4931, - "name": "MSTORE" - }, - { - "begin": 4369, - "end": 4931, - "name": "MLOAD" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP1" - }, - { - "begin": 4369, - "end": 4931, - "name": "DUP2" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP1" - }, - { - "begin": 4369, - "end": 4931, - "name": "SUB" - }, - { - "begin": 4369, - "end": 4931, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4369, - "end": 4931, - "name": "ADD" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP1" - }, - { - "begin": 4369, - "end": 4931, - "name": "RETURN" - }, - { - "begin": 4986, - "end": 5021, - "name": "tag", - "value": "6" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMPDEST" - }, - { - "begin": 4986, - "end": 5021, - "name": "CALLVALUE" - }, - { - "begin": 4986, - "end": 5021, - "name": "ISZERO" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH [tag]", - "value": "26" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMPI" - }, - { - "begin": 4986, - "end": 5021, - "name": "INVALID" - }, - { - "begin": 4986, - "end": 5021, - "name": "tag", - "value": "26" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMPDEST" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH [tag]", - "value": "27" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH [tag]", - "value": "28" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMP" - }, - { - "begin": 4986, - "end": 5021, - "name": "tag", - "value": "27" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMPDEST" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4986, - "end": 5021, - "name": "DUP1" - }, - { - "begin": 4986, - "end": 5021, - "name": "MLOAD" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH", - "value": "FF" - }, - { - "begin": 4986, - "end": 5021, - "name": "SWAP1" - }, - { - "begin": 4986, - "end": 5021, - "name": "SWAP3" - }, - { - "begin": 4986, - "end": 5021, - "name": "AND" - }, - { - "begin": 4986, - "end": 5021, - "name": "DUP3" - }, - { - "begin": 4986, - "end": 5021, - "name": "MSTORE" - }, - { - "begin": 4986, - "end": 5021, - "name": "MLOAD" - }, - { - "begin": 4986, - "end": 5021, - "name": "SWAP1" - }, - { - "begin": 4986, - "end": 5021, - "name": "DUP2" - }, - { - "begin": 4986, - "end": 5021, - "name": "SWAP1" - }, - { - "begin": 4986, - "end": 5021, - "name": "SUB" - }, - { - "begin": 4986, - "end": 5021, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4986, - "end": 5021, - "name": "ADD" - }, - { - "begin": 4986, - "end": 5021, - "name": "SWAP1" - }, - { - "begin": 4986, - "end": 5021, - "name": "RETURN" - }, - { - "begin": 3415, - "end": 3517, - "name": "tag", - "value": "7" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPDEST" - }, - { - "begin": 3415, - "end": 3517, - "name": "CALLVALUE" - }, - { - "begin": 3415, - "end": 3517, - "name": "ISZERO" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH [tag]", - "value": "29" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPI" - }, - { - "begin": 3415, - "end": 3517, - "name": "INVALID" - }, - { - "begin": 3415, - "end": 3517, - "name": "tag", - "value": "29" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPDEST" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH [tag]", - "value": "21" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH", - "value": "4" - }, - { - "begin": 3415, - "end": 3517, - "name": "CALLDATALOAD" - }, - { - "begin": 3415, - "end": 3517, - "name": "AND" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH [tag]", - "value": "31" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMP" - }, - { - "begin": 3415, - "end": 3517, - "name": "tag", - "value": "30" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPDEST" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3415, - "end": 3517, - "name": "DUP1" - }, - { - "begin": 3415, - "end": 3517, - "name": "MLOAD" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP2" - }, - { - "begin": 3415, - "end": 3517, - "name": "DUP3" - }, - { - "begin": 3415, - "end": 3517, - "name": "MSTORE" - }, - { - "begin": 3415, - "end": 3517, - "name": "MLOAD" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP1" - }, - { - "begin": 3415, - "end": 3517, - "name": "DUP2" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP1" - }, - { - "begin": 3415, - "end": 3517, - "name": "SUB" - }, - { - "begin": 3415, - "end": 3517, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3415, - "end": 3517, - "name": "ADD" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP1" - }, - { - "begin": 3415, - "end": 3517, - "name": "RETURN" - }, - { - "begin": 5164, - "end": 5201, - "name": "tag", - "value": "8" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMPDEST" - }, - { - "begin": 5164, - "end": 5201, - "name": "CALLVALUE" - }, - { - "begin": 5164, - "end": 5201, - "name": "ISZERO" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH [tag]", - "value": "32" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMPI" - }, - { - "begin": 5164, - "end": 5201, - "name": "INVALID" - }, - { - "begin": 5164, - "end": 5201, - "name": "tag", - "value": "32" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMPDEST" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH [tag]", - "value": "12" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH [tag]", - "value": "34" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMP" - }, - { - "begin": 5164, - "end": 5201, - "name": "tag", - "value": "33" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMPDEST" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "MLOAD" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP3" - }, - { - "begin": 5164, - "end": 5201, - "name": "MSTORE" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "MLOAD" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "ADD" - }, - { - "begin": 5164, - "end": 5201, - "name": "MSTORE" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "MLOAD" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP3" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP3" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "ADD" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP6" - }, - { - "begin": 5164, - "end": 5201, - "name": "ADD" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP4" - }, - { - "begin": 18, - "end": 20, - "name": "DUP3" - }, - { - "begin": 18, - "end": 20, - "name": "ISZERO" - }, - { - "begin": 13, - "end": 16, - "name": "PUSH [tag]", - "value": "14" - }, - { - "begin": 7, - "end": 12, - "name": "JUMPI" - }, - { - "begin": 32, - "end": 37, - "name": "tag", - "value": "36" - }, - { - "begin": 32, - "end": 37, - "name": "JUMPDEST" - }, - { - "begin": 59, - "end": 62, - "name": "DUP1" - }, - { - "begin": 53, - "end": 58, - "name": "MLOAD" - }, - { - "begin": 48, - "end": 51, - "name": "DUP3" - }, - { - "begin": 41, - "end": 47, - "name": "MSTORE" - }, - { - "begin": 93, - "end": 95, - "name": "PUSH", - "value": "20" - }, - { - "begin": 88, - "end": 91, - "name": "DUP4" - }, - { - "begin": 85, - "end": 87, - "name": "GT" - }, - { - "begin": 78, - "end": 84, - "name": "ISZERO" - }, - { - "begin": 73, - "end": 76, - "name": "PUSH [tag]", - "value": "14" - }, - { - "begin": 67, - "end": 72, - "name": "JUMPI" - }, - { - "begin": 152, - "end": 155, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP1" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP3" - }, - { - "begin": 152, - "end": 155, - "name": "ADD" - }, - { - "begin": 152, - "end": 155, - "name": "SWAP2" - }, - { - "begin": 117, - "end": 119, - "name": "PUSH", - "value": "20" - }, - { - "begin": 108, - "end": 111, - "name": "SWAP2" - }, - { - "begin": 108, - "end": 111, - "name": "DUP3" - }, - { - "begin": 108, - "end": 111, - "name": "ADD" - }, - { - "begin": 108, - "end": 111, - "name": "SWAP2" - }, - { - "begin": 130, - "end": 133, - "name": "ADD" - }, - { - "begin": 172, - "end": 177, - "name": "PUSH [tag]", - "value": "15" - }, - { - "begin": 167, - "end": 171, - "name": "JUMP" - }, - { - "begin": 181, - "end": 184, - "name": "tag", - "value": "35" - }, - { - "begin": 181, - "end": 184, - "name": "JUMPDEST" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP2" - }, - { - "begin": 3, - "end": 189, - "name": "ADD" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "1F" - }, - { - "begin": 3, - "end": 189, - "name": "AND" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "ISZERO" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH [tag]", - "value": "16" - }, - { - "begin": 3, - "end": 189, - "name": "JUMPI" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP3" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "MLOAD" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "1" - }, - { - "begin": 3, - "end": 189, - "name": "DUP4" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "100" - }, - { - "begin": 3, - "end": 189, - "name": "EXP" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "NOT" - }, - { - "begin": 3, - "end": 189, - "name": "AND" - }, - { - "begin": 3, - "end": 189, - "name": "DUP2" - }, - { - "begin": 3, - "end": 189, - "name": "MSTORE" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3, - "end": 189, - "name": "ADD" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP2" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "tag", - "value": "37" - }, - { - "begin": 3, - "end": 189, - "name": "JUMPDEST" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP3" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "POP" - }, - { - "begin": 3, - "end": 189, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3, - "end": 189, - "name": "MLOAD" - }, - { - "begin": 3, - "end": 189, - "name": "DUP1" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP2" - }, - { - "begin": 3, - "end": 189, - "name": "SUB" - }, - { - "begin": 3, - "end": 189, - "name": "SWAP1" - }, - { - "begin": 3, - "end": 189, - "name": "RETURN" - }, - { - "begin": 2490, - "end": 2923, - "name": "tag", - "value": "9" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPDEST" - }, - { - "begin": 2490, - "end": 2923, - "name": "CALLVALUE" - }, - { - "begin": 2490, - "end": 2923, - "name": "ISZERO" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH [tag]", - "value": "38" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPI" - }, - { - "begin": 2490, - "end": 2923, - "name": "INVALID" - }, - { - "begin": 2490, - "end": 2923, - "name": "tag", - "value": "38" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPDEST" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH [tag]", - "value": "18" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH", - "value": "4" - }, - { - "begin": 2490, - "end": 2923, - "name": "CALLDATALOAD" - }, - { - "begin": 2490, - "end": 2923, - "name": "AND" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH", - "value": "24" - }, - { - "begin": 2490, - "end": 2923, - "name": "CALLDATALOAD" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH [tag]", - "value": "40" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMP" - }, - { - "begin": 2490, - "end": 2923, - "name": "tag", - "value": "39" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPDEST" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH", - "value": "40" - }, - { - "begin": 2490, - "end": 2923, - "name": "DUP1" - }, - { - "begin": 2490, - "end": 2923, - "name": "MLOAD" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP2" - }, - { - "begin": 2490, - "end": 2923, - "name": "ISZERO" - }, - { - "begin": 2490, - "end": 2923, - "name": "ISZERO" - }, - { - "begin": 2490, - "end": 2923, - "name": "DUP3" - }, - { - "begin": 2490, - "end": 2923, - "name": "MSTORE" - }, - { - "begin": 2490, - "end": 2923, - "name": "MLOAD" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP1" - }, - { - "begin": 2490, - "end": 2923, - "name": "DUP2" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP1" - }, - { - "begin": 2490, - "end": 2923, - "name": "SUB" - }, - { - "begin": 2490, - "end": 2923, - "name": "PUSH", - "value": "20" - }, - { - "begin": 2490, - "end": 2923, - "name": "ADD" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP1" - }, - { - "begin": 2490, - "end": 2923, - "name": "RETURN" - }, - { - "begin": 3719, - "end": 3848, - "name": "tag", - "value": "10" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPDEST" - }, - { - "begin": 3719, - "end": 3848, - "name": "CALLVALUE" - }, - { - "begin": 3719, - "end": 3848, - "name": "ISZERO" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH [tag]", - "value": "41" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPI" - }, - { - "begin": 3719, - "end": 3848, - "name": "INVALID" - }, - { - "begin": 3719, - "end": 3848, - "name": "tag", - "value": "41" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPDEST" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH [tag]", - "value": "21" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH", - "value": "4" - }, - { - "begin": 3719, - "end": 3848, - "name": "CALLDATALOAD" - }, - { - "begin": 3719, - "end": 3848, - "name": "DUP2" - }, - { - "begin": 3719, - "end": 3848, - "name": "AND" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP1" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH", - "value": "24" - }, - { - "begin": 3719, - "end": 3848, - "name": "CALLDATALOAD" - }, - { - "begin": 3719, - "end": 3848, - "name": "AND" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH [tag]", - "value": "43" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMP" - }, - { - "begin": 3719, - "end": 3848, - "name": "tag", - "value": "42" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPDEST" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3719, - "end": 3848, - "name": "DUP1" - }, - { - "begin": 3719, - "end": 3848, - "name": "MLOAD" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP2" - }, - { - "begin": 3719, - "end": 3848, - "name": "DUP3" - }, - { - "begin": 3719, - "end": 3848, - "name": "MSTORE" - }, - { - "begin": 3719, - "end": 3848, - "name": "MLOAD" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP1" - }, - { - "begin": 3719, - "end": 3848, - "name": "DUP2" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP1" - }, - { - "begin": 3719, - "end": 3848, - "name": "SUB" - }, - { - "begin": 3719, - "end": 3848, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3719, - "end": 3848, - "name": "ADD" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP1" - }, - { - "begin": 3719, - "end": 3848, - "name": "RETURN" - }, - { - "begin": 5109, - "end": 5158, - "name": "tag", - "value": "13" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMPDEST" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "MLOAD" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP3" - }, - { - "begin": 5109, - "end": 5158, - "name": "ADD" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP1" - }, - { - "begin": 5109, - "end": 5158, - "name": "SWAP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "MSTORE" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "11" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "MSTORE" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "30782050726F746F636F6C20546F6B656E000000000000000000000000000000" - }, - { - "begin": 5109, - "end": 5158, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP3" - }, - { - "begin": 5109, - "end": 5158, - "name": "ADD" - }, - { - "begin": 5109, - "end": 5158, - "name": "MSTORE" - }, - { - "begin": 5109, - "end": 5158, - "name": "DUP2" - }, - { - "begin": 5109, - "end": 5158, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 3523, - "end": 3713, - "name": "tag", - "value": "19" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPDEST" - }, - { - "begin": 3599, - "end": 3618, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3607, - "end": 3617, - "name": "CALLER" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP2" - }, - { - "begin": 3599, - "end": 3618, - "name": "AND" - }, - { - "begin": 3583, - "end": 3587, - "name": "PUSH", - "value": "0" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP2" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP2" - }, - { - "begin": 3599, - "end": 3618, - "name": "MSTORE" - }, - { - "begin": 3599, - "end": 3606, - "name": "PUSH", - "value": "1" - }, - { - "begin": 3599, - "end": 3618, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3599, - "end": 3618, - "name": "SWAP1" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP2" - }, - { - "begin": 3599, - "end": 3618, - "name": "MSTORE" - }, - { - "begin": 3599, - "end": 3618, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP1" - }, - { - "begin": 3599, - "end": 3618, - "name": "DUP4" - }, - { - "begin": 3599, - "end": 3618, - "name": "SHA3" - }, - { - "begin": 3599, - "end": 3628, - "name": "SWAP5" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP8" - }, - { - "begin": 3599, - "end": 3628, - "name": "AND" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP1" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP5" - }, - { - "begin": 3599, - "end": 3628, - "name": "MSTORE" - }, - { - "begin": 3599, - "end": 3628, - "name": "SWAP5" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP3" - }, - { - "begin": 3599, - "end": 3628, - "name": "MSTORE" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP1" - }, - { - "begin": 3599, - "end": 3628, - "name": "DUP4" - }, - { - "begin": 3599, - "end": 3628, - "name": "SHA3" - }, - { - "begin": 3599, - "end": 3637, - "name": "DUP7" - }, - { - "begin": 3599, - "end": 3637, - "name": "SWAP1" - }, - { - "begin": 3599, - "end": 3637, - "name": "SSTORE" - }, - { - "begin": 3647, - "end": 3685, - "name": "DUP1" - }, - { - "begin": 3647, - "end": 3685, - "name": "MLOAD" - }, - { - "begin": 3647, - "end": 3685, - "name": "DUP7" - }, - { - "begin": 3647, - "end": 3685, - "name": "DUP2" - }, - { - "begin": 3647, - "end": 3685, - "name": "MSTORE" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP1" - }, - { - "begin": 3647, - "end": 3685, - "name": "MLOAD" - }, - { - "begin": 3583, - "end": 3587, - "name": "SWAP3" - }, - { - "begin": 3583, - "end": 3587, - "name": "SWAP5" - }, - { - "begin": 3599, - "end": 3628, - "name": "SWAP4" - }, - { - "begin": 3599, - "end": 3618, - "name": "SWAP3" - }, - { - "begin": 3647, - "end": 3685, - "name": "PUSH", - "value": "8C5BE1E5EBEC7D5BD14F71427D1E84F3DD0314C0F7B2291E5B200AC8C7C3B925" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP3" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP2" - }, - { - "begin": 3647, - "end": 3685, - "name": "DUP2" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP1" - }, - { - "begin": 3647, - "end": 3685, - "name": "SUB" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP1" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP2" - }, - { - "begin": 3647, - "end": 3685, - "name": "ADD" - }, - { - "begin": 3647, - "end": 3685, - "name": "SWAP1" - }, - { - "begin": 3647, - "end": 3685, - "name": "LOG3" - }, - { - "begin": -1, - "end": -1, - "name": "POP" - }, - { - "begin": 3702, - "end": 3706, - "name": "PUSH", - "value": "1" - }, - { - "begin": 3523, - "end": 3713, - "name": "tag", - "value": "44" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMPDEST" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP3" - }, - { - "begin": 3523, - "end": 3713, - "name": "SWAP2" - }, - { - "begin": 3523, - "end": 3713, - "name": "POP" - }, - { - "begin": 3523, - "end": 3713, - "name": "POP" - }, - { - "begin": 3523, - "end": 3713, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 5027, - "end": 5064, - "name": "tag", - "value": "22" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMPDEST" - }, - { - "begin": 5027, - "end": 5064, - "name": "PUSH", - "value": "3" - }, - { - "begin": 5027, - "end": 5064, - "name": "SLOAD" - }, - { - "begin": 5027, - "end": 5064, - "name": "DUP2" - }, - { - "begin": 5027, - "end": 5064, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 4369, - "end": 4931, - "name": "tag", - "value": "25" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPDEST" - }, - { - "begin": 4487, - "end": 4501, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP1" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP5" - }, - { - "begin": 4487, - "end": 4501, - "name": "AND" - }, - { - "begin": 4451, - "end": 4455, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP2" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP2" - }, - { - "begin": 4487, - "end": 4501, - "name": "MSTORE" - }, - { - "begin": 4487, - "end": 4494, - "name": "PUSH", - "value": "1" - }, - { - "begin": 4487, - "end": 4501, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4487, - "end": 4501, - "name": "SWAP1" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP2" - }, - { - "begin": 4487, - "end": 4501, - "name": "MSTORE" - }, - { - "begin": 4487, - "end": 4501, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP1" - }, - { - "begin": 4487, - "end": 4501, - "name": "DUP4" - }, - { - "begin": 4487, - "end": 4501, - "name": "SHA3" - }, - { - "begin": 4502, - "end": 4512, - "name": "CALLER" - }, - { - "begin": 4487, - "end": 4513, - "name": "SWAP1" - }, - { - "begin": 4487, - "end": 4513, - "name": "SWAP6" - }, - { - "begin": 4487, - "end": 4513, - "name": "AND" - }, - { - "begin": 4487, - "end": 4513, - "name": "DUP4" - }, - { - "begin": 4487, - "end": 4513, - "name": "MSTORE" - }, - { - "begin": 4487, - "end": 4513, - "name": "SWAP4" - }, - { - "begin": 4487, - "end": 4513, - "name": "DUP2" - }, - { - "begin": 4487, - "end": 4513, - "name": "MSTORE" - }, - { - "begin": 4487, - "end": 4513, - "name": "DUP4" - }, - { - "begin": 4487, - "end": 4513, - "name": "DUP3" - }, - { - "begin": 4487, - "end": 4513, - "name": "SHA3" - }, - { - "begin": 4487, - "end": 4513, - "name": "SLOAD" - }, - { - "begin": 4527, - "end": 4542, - "name": "SWAP3" - }, - { - "begin": 4527, - "end": 4542, - "name": "DUP3" - }, - { - "begin": 4527, - "end": 4542, - "name": "MSTORE" - }, - { - "begin": 4527, - "end": 4542, - "name": "DUP2" - }, - { - "begin": 4527, - "end": 4542, - "name": "SWAP1" - }, - { - "begin": 4527, - "end": 4542, - "name": "MSTORE" - }, - { - "begin": 4527, - "end": 4542, - "name": "SWAP2" - }, - { - "begin": 4527, - "end": 4542, - "name": "DUP3" - }, - { - "begin": 4527, - "end": 4542, - "name": "SHA3" - }, - { - "begin": 4527, - "end": 4542, - "name": "SLOAD" - }, - { - "begin": 4527, - "end": 4552, - "name": "DUP4" - }, - { - "begin": 4527, - "end": 4552, - "name": "SWAP1" - }, - { - "begin": 4527, - "end": 4552, - "name": "LT" - }, - { - "begin": 4527, - "end": 4552, - "name": "DUP1" - }, - { - "begin": 4527, - "end": 4552, - "name": "ISZERO" - }, - { - "begin": 4527, - "end": 4552, - "name": "SWAP1" - }, - { - "begin": 4527, - "end": 4575, - "name": "PUSH [tag]", - "value": "46" - }, - { - "begin": 4527, - "end": 4575, - "name": "JUMPI" - }, - { - "begin": 4527, - "end": 4575, - "name": "POP" - }, - { - "begin": 4569, - "end": 4575, - "name": "DUP3" - }, - { - "begin": 4556, - "end": 4565, - "name": "DUP2" - }, - { - "begin": 4556, - "end": 4575, - "name": "LT" - }, - { - "begin": 4556, - "end": 4575, - "name": "ISZERO" - }, - { - "begin": 4527, - "end": 4575, - "name": "tag", - "value": "46" - }, - { - "begin": 4527, - "end": 4575, - "name": "JUMPDEST" - }, - { - "begin": 4527, - "end": 4618, - "name": "DUP1" - }, - { - "begin": 4527, - "end": 4618, - "name": "ISZERO" - }, - { - "begin": 4527, - "end": 4618, - "name": "PUSH [tag]", - "value": "47" - }, - { - "begin": 4527, - "end": 4618, - "name": "JUMPI" - }, - { - "begin": -1, - "end": -1, - "name": "POP" - }, - { - "begin": 4605, - "end": 4618, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4605, - "end": 4618, - "name": "DUP5" - }, - { - "begin": 4605, - "end": 4618, - "name": "AND" - }, - { - "begin": 4605, - "end": 4613, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4605, - "end": 4618, - "name": "SWAP1" - }, - { - "begin": 4605, - "end": 4618, - "name": "DUP2" - }, - { - "begin": 4605, - "end": 4618, - "name": "MSTORE" - }, - { - "begin": 4605, - "end": 4618, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4605, - "end": 4618, - "name": "DUP2" - }, - { - "begin": 4605, - "end": 4618, - "name": "SWAP1" - }, - { - "begin": 4605, - "end": 4618, - "name": "MSTORE" - }, - { - "begin": 4605, - "end": 4618, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4605, - "end": 4618, - "name": "SWAP1" - }, - { - "begin": 4605, - "end": 4618, - "name": "SHA3" - }, - { - "begin": 4605, - "end": 4618, - "name": "SLOAD" - }, - { - "begin": 4579, - "end": 4601, - "name": "DUP4" - }, - { - "begin": 4579, - "end": 4601, - "name": "DUP2" - }, - { - "begin": 4579, - "end": 4601, - "name": "ADD" - }, - { - "begin": 4579, - "end": 4618, - "name": "LT" - }, - { - "begin": 4579, - "end": 4618, - "name": "ISZERO" - }, - { - "begin": 4527, - "end": 4618, - "name": "tag", - "value": "47" - }, - { - "begin": 4527, - "end": 4618, - "name": "JUMPDEST" - }, - { - "begin": 4523, - "end": 4925, - "name": "ISZERO" - }, - { - "begin": 4523, - "end": 4925, - "name": "PUSH [tag]", - "value": "48" - }, - { - "begin": 4523, - "end": 4925, - "name": "JUMPI" - }, - { - "begin": 4634, - "end": 4647, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP1" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP6" - }, - { - "begin": 4634, - "end": 4647, - "name": "AND" - }, - { - "begin": 4634, - "end": 4642, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4634, - "end": 4647, - "name": "SWAP1" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP2" - }, - { - "begin": 4634, - "end": 4647, - "name": "MSTORE" - }, - { - "begin": 4634, - "end": 4647, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP2" - }, - { - "begin": 4634, - "end": 4647, - "name": "SWAP1" - }, - { - "begin": 4634, - "end": 4647, - "name": "MSTORE" - }, - { - "begin": 4634, - "end": 4647, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP1" - }, - { - "begin": 4634, - "end": 4647, - "name": "DUP3" - }, - { - "begin": 4634, - "end": 4647, - "name": "SHA3" - }, - { - "begin": 4634, - "end": 4657, - "name": "DUP1" - }, - { - "begin": 4634, - "end": 4657, - "name": "SLOAD" - }, - { - "begin": 4634, - "end": 4657, - "name": "DUP8" - }, - { - "begin": 4634, - "end": 4657, - "name": "ADD" - }, - { - "begin": 4634, - "end": 4657, - "name": "SWAP1" - }, - { - "begin": 4634, - "end": 4657, - "name": "SSTORE" - }, - { - "begin": 4671, - "end": 4686, - "name": "SWAP2" - }, - { - "begin": 4671, - "end": 4686, - "name": "DUP8" - }, - { - "begin": 4671, - "end": 4686, - "name": "AND" - }, - { - "begin": 4671, - "end": 4686, - "name": "DUP2" - }, - { - "begin": 4671, - "end": 4686, - "name": "MSTORE" - }, - { - "begin": 4671, - "end": 4686, - "name": "SHA3" - }, - { - "begin": 4671, - "end": 4696, - "name": "DUP1" - }, - { - "begin": 4671, - "end": 4696, - "name": "SLOAD" - }, - { - "begin": 4671, - "end": 4696, - "name": "DUP5" - }, - { - "begin": 4671, - "end": 4696, - "name": "SWAP1" - }, - { - "begin": 4671, - "end": 4696, - "name": "SUB" - }, - { - "begin": 4671, - "end": 4696, - "name": "SWAP1" - }, - { - "begin": 4671, - "end": 4696, - "name": "SSTORE" - }, - { - "begin": 4069, - "end": 4081, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4714, - "end": 4734, - "name": "DUP2" - }, - { - "begin": 4714, - "end": 4734, - "name": "LT" - }, - { - "begin": 4710, - "end": 4805, - "name": "ISZERO" - }, - { - "begin": 4710, - "end": 4805, - "name": "PUSH [tag]", - "value": "49" - }, - { - "begin": 4710, - "end": 4805, - "name": "JUMPI" - }, - { - "begin": 4754, - "end": 4768, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP1" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP7" - }, - { - "begin": 4754, - "end": 4768, - "name": "AND" - }, - { - "begin": 4754, - "end": 4768, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4754, - "end": 4768, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP2" - }, - { - "begin": 4754, - "end": 4768, - "name": "MSTORE" - }, - { - "begin": 4754, - "end": 4761, - "name": "PUSH", - "value": "1" - }, - { - "begin": 4754, - "end": 4768, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4754, - "end": 4768, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP2" - }, - { - "begin": 4754, - "end": 4768, - "name": "MSTORE" - }, - { - "begin": 4754, - "end": 4768, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP1" - }, - { - "begin": 4754, - "end": 4768, - "name": "DUP4" - }, - { - "begin": 4754, - "end": 4768, - "name": "SHA3" - }, - { - "begin": 4769, - "end": 4779, - "name": "CALLER" - }, - { - "begin": 4754, - "end": 4780, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4780, - "name": "SWAP5" - }, - { - "begin": 4754, - "end": 4780, - "name": "AND" - }, - { - "begin": 4754, - "end": 4780, - "name": "DUP4" - }, - { - "begin": 4754, - "end": 4780, - "name": "MSTORE" - }, - { - "begin": 4754, - "end": 4780, - "name": "SWAP3" - }, - { - "begin": 4754, - "end": 4780, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4780, - "name": "MSTORE" - }, - { - "begin": 4754, - "end": 4780, - "name": "SHA3" - }, - { - "begin": 4754, - "end": 4790, - "name": "DUP1" - }, - { - "begin": 4754, - "end": 4790, - "name": "SLOAD" - }, - { - "begin": 4754, - "end": 4790, - "name": "DUP5" - }, - { - "begin": 4754, - "end": 4790, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4790, - "name": "SUB" - }, - { - "begin": 4754, - "end": 4790, - "name": "SWAP1" - }, - { - "begin": 4754, - "end": 4790, - "name": "SSTORE" - }, - { - "begin": 4710, - "end": 4805, - "name": "tag", - "value": "49" - }, - { - "begin": 4710, - "end": 4805, - "name": "JUMPDEST" - }, - { - "begin": 4834, - "end": 4837, - "name": "DUP4" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4818, - "end": 4846, - "name": "AND" - }, - { - "begin": 4827, - "end": 4832, - "name": "DUP6" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 4818, - "end": 4846, - "name": "AND" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF" - }, - { - "begin": 4839, - "end": 4845, - "name": "DUP6" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4818, - "end": 4846, - "name": "MLOAD" - }, - { - "begin": 4818, - "end": 4846, - "name": "DUP1" - }, - { - "begin": 4818, - "end": 4846, - "name": "DUP3" - }, - { - "begin": 4818, - "end": 4846, - "name": "DUP2" - }, - { - "begin": 4818, - "end": 4846, - "name": "MSTORE" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "20" - }, - { - "begin": 4818, - "end": 4846, - "name": "ADD" - }, - { - "begin": 4818, - "end": 4846, - "name": "SWAP2" - }, - { - "begin": 4818, - "end": 4846, - "name": "POP" - }, - { - "begin": 4818, - "end": 4846, - "name": "POP" - }, - { - "begin": 4818, - "end": 4846, - "name": "PUSH", - "value": "40" - }, - { - "begin": 4818, - "end": 4846, - "name": "MLOAD" - }, - { - "begin": 4818, - "end": 4846, - "name": "DUP1" - }, - { - "begin": 4818, - "end": 4846, - "name": "SWAP2" - }, - { - "begin": 4818, - "end": 4846, - "name": "SUB" - }, - { - "begin": 4818, - "end": 4846, - "name": "SWAP1" - }, - { - "begin": 4818, - "end": 4846, - "name": "LOG3" - }, - { - "begin": 4867, - "end": 4871, - "name": "PUSH", - "value": "1" - }, - { - "begin": 4860, - "end": 4871, - "name": "SWAP2" - }, - { - "begin": 4860, - "end": 4871, - "name": "POP" - }, - { - "begin": 4860, - "end": 4871, - "name": "PUSH [tag]", - "value": "50" - }, - { - "begin": 4860, - "end": 4871, - "name": "JUMP" - }, - { - "begin": 4523, - "end": 4925, - "name": "tag", - "value": "48" - }, - { - "begin": 4523, - "end": 4925, - "name": "JUMPDEST" - }, - { - "begin": 4909, - "end": 4914, - "name": "PUSH", - "value": "0" - }, - { - "begin": 4902, - "end": 4914, - "name": "SWAP2" - }, - { - "begin": 4902, - "end": 4914, - "name": "POP" - }, - { - "begin": 4523, - "end": 4925, - "name": "tag", - "value": "50" - }, - { - "begin": 4523, - "end": 4925, - "name": "JUMPDEST" - }, - { - "begin": 4369, - "end": 4931, - "name": "tag", - "value": "45" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMPDEST" - }, - { - "begin": 4369, - "end": 4931, - "name": "POP" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP4" - }, - { - "begin": 4369, - "end": 4931, - "name": "SWAP3" - }, - { - "begin": 4369, - "end": 4931, - "name": "POP" - }, - { - "begin": 4369, - "end": 4931, - "name": "POP" - }, - { - "begin": 4369, - "end": 4931, - "name": "POP" - }, - { - "begin": 4369, - "end": 4931, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 4986, - "end": 5021, - "name": "tag", - "value": "28" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMPDEST" - }, - { - "begin": 5019, - "end": 5021, - "name": "PUSH", - "value": "12" - }, - { - "begin": 4986, - "end": 5021, - "name": "DUP2" - }, - { - "begin": 4986, - "end": 5021, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 3415, - "end": 3517, - "name": "tag", - "value": "31" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPDEST" - }, - { - "begin": 3494, - "end": 3510, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3494, - "end": 3510, - "name": "DUP2" - }, - { - "begin": 3494, - "end": 3510, - "name": "AND" - }, - { - "begin": 3468, - "end": 3475, - "name": "PUSH", - "value": "0" - }, - { - "begin": 3494, - "end": 3510, - "name": "SWAP1" - }, - { - "begin": 3494, - "end": 3510, - "name": "DUP2" - }, - { - "begin": 3494, - "end": 3510, - "name": "MSTORE" - }, - { - "begin": 3494, - "end": 3510, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3494, - "end": 3510, - "name": "DUP2" - }, - { - "begin": 3494, - "end": 3510, - "name": "SWAP1" - }, - { - "begin": 3494, - "end": 3510, - "name": "MSTORE" - }, - { - "begin": 3494, - "end": 3510, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3494, - "end": 3510, - "name": "SWAP1" - }, - { - "begin": 3494, - "end": 3510, - "name": "SHA3" - }, - { - "begin": 3494, - "end": 3510, - "name": "SLOAD" - }, - { - "begin": 3415, - "end": 3517, - "name": "tag", - "value": "51" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMPDEST" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP2" - }, - { - "begin": 3415, - "end": 3517, - "name": "SWAP1" - }, - { - "begin": 3415, - "end": 3517, - "name": "POP" - }, - { - "begin": 3415, - "end": 3517, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 5164, - "end": 5201, - "name": "tag", - "value": "34" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMPDEST" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "40" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "MLOAD" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP3" - }, - { - "begin": 5164, - "end": 5201, - "name": "ADD" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP1" - }, - { - "begin": 5164, - "end": 5201, - "name": "SWAP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "MSTORE" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "3" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "MSTORE" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "5A52580000000000000000000000000000000000000000000000000000000000" - }, - { - "begin": 5164, - "end": 5201, - "name": "PUSH", - "value": "20" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP3" - }, - { - "begin": 5164, - "end": 5201, - "name": "ADD" - }, - { - "begin": 5164, - "end": 5201, - "name": "MSTORE" - }, - { - "begin": 5164, - "end": 5201, - "name": "DUP2" - }, - { - "begin": 5164, - "end": 5201, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 2490, - "end": 2923, - "name": "tag", - "value": "40" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPDEST" - }, - { - "begin": 2635, - "end": 2655, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 2644, - "end": 2654, - "name": "CALLER" - }, - { - "begin": 2635, - "end": 2655, - "name": "AND" - }, - { - "begin": 2546, - "end": 2550, - "name": "PUSH", - "value": "0" - }, - { - "begin": 2635, - "end": 2655, - "name": "SWAP1" - }, - { - "begin": 2635, - "end": 2655, - "name": "DUP2" - }, - { - "begin": 2635, - "end": 2655, - "name": "MSTORE" - }, - { - "begin": 2635, - "end": 2655, - "name": "PUSH", - "value": "20" - }, - { - "begin": 2635, - "end": 2655, - "name": "DUP2" - }, - { - "begin": 2635, - "end": 2655, - "name": "SWAP1" - }, - { - "begin": 2635, - "end": 2655, - "name": "MSTORE" - }, - { - "begin": 2635, - "end": 2655, - "name": "PUSH", - "value": "40" - }, - { - "begin": 2635, - "end": 2655, - "name": "DUP2" - }, - { - "begin": 2635, - "end": 2655, - "name": "SHA3" - }, - { - "begin": 2635, - "end": 2655, - "name": "SLOAD" - }, - { - "begin": 2635, - "end": 2665, - "name": "DUP3" - }, - { - "begin": 2635, - "end": 2665, - "name": "SWAP1" - }, - { - "begin": 2635, - "end": 2665, - "name": "LT" - }, - { - "begin": 2635, - "end": 2665, - "name": "DUP1" - }, - { - "begin": 2635, - "end": 2665, - "name": "ISZERO" - }, - { - "begin": 2635, - "end": 2665, - "name": "SWAP1" - }, - { - "begin": 2635, - "end": 2708, - "name": "PUSH [tag]", - "value": "53" - }, - { - "begin": 2635, - "end": 2708, - "name": "JUMPI" - }, - { - "begin": -1, - "end": -1, - "name": "POP" - }, - { - "begin": 2695, - "end": 2708, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 2695, - "end": 2708, - "name": "DUP4" - }, - { - "begin": 2695, - "end": 2708, - "name": "AND" - }, - { - "begin": 2695, - "end": 2703, - "name": "PUSH", - "value": "0" - }, - { - "begin": 2695, - "end": 2708, - "name": "SWAP1" - }, - { - "begin": 2695, - "end": 2708, - "name": "DUP2" - }, - { - "begin": 2695, - "end": 2708, - "name": "MSTORE" - }, - { - "begin": 2695, - "end": 2708, - "name": "PUSH", - "value": "20" - }, - { - "begin": 2695, - "end": 2708, - "name": "DUP2" - }, - { - "begin": 2695, - "end": 2708, - "name": "SWAP1" - }, - { - "begin": 2695, - "end": 2708, - "name": "MSTORE" - }, - { - "begin": 2695, - "end": 2708, - "name": "PUSH", - "value": "40" - }, - { - "begin": 2695, - "end": 2708, - "name": "SWAP1" - }, - { - "begin": 2695, - "end": 2708, - "name": "SHA3" - }, - { - "begin": 2695, - "end": 2708, - "name": "SLOAD" - }, - { - "begin": 2669, - "end": 2691, - "name": "DUP3" - }, - { - "begin": 2669, - "end": 2691, - "name": "DUP2" - }, - { - "begin": 2669, - "end": 2691, - "name": "ADD" - }, - { - "begin": 2669, - "end": 2708, - "name": "LT" - }, - { - "begin": 2669, - "end": 2708, - "name": "ISZERO" - }, - { - "begin": 2635, - "end": 2708, - "name": "tag", - "value": "53" - }, - { - "begin": 2635, - "end": 2708, - "name": "JUMPDEST" - }, - { - "begin": 2631, - "end": 2917, - "name": "ISZERO" - }, - { - "begin": 2631, - "end": 2917, - "name": "PUSH [tag]", - "value": "54" - }, - { - "begin": 2631, - "end": 2917, - "name": "JUMPI" - }, - { - "begin": 2724, - "end": 2744, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 2733, - "end": 2743, - "name": "CALLER" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP2" - }, - { - "begin": 2724, - "end": 2744, - "name": "AND" - }, - { - "begin": 2724, - "end": 2732, - "name": "PUSH", - "value": "0" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP2" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP2" - }, - { - "begin": 2724, - "end": 2744, - "name": "MSTORE" - }, - { - "begin": 2724, - "end": 2744, - "name": "PUSH", - "value": "20" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP2" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP2" - }, - { - "begin": 2724, - "end": 2744, - "name": "MSTORE" - }, - { - "begin": 2724, - "end": 2744, - "name": "PUSH", - "value": "40" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP1" - }, - { - "begin": 2724, - "end": 2744, - "name": "DUP4" - }, - { - "begin": 2724, - "end": 2744, - "name": "SHA3" - }, - { - "begin": 2724, - "end": 2754, - "name": "DUP1" - }, - { - "begin": 2724, - "end": 2754, - "name": "SLOAD" - }, - { - "begin": 2724, - "end": 2754, - "name": "DUP9" - }, - { - "begin": 2724, - "end": 2754, - "name": "SWAP1" - }, - { - "begin": 2724, - "end": 2754, - "name": "SUB" - }, - { - "begin": 2724, - "end": 2754, - "name": "SWAP1" - }, - { - "begin": 2724, - "end": 2754, - "name": "SSTORE" - }, - { - "begin": 2768, - "end": 2781, - "name": "SWAP4" - }, - { - "begin": 2768, - "end": 2781, - "name": "DUP8" - }, - { - "begin": 2768, - "end": 2781, - "name": "AND" - }, - { - "begin": 2768, - "end": 2781, - "name": "DUP1" - }, - { - "begin": 2768, - "end": 2781, - "name": "DUP4" - }, - { - "begin": 2768, - "end": 2781, - "name": "MSTORE" - }, - { - "begin": 2768, - "end": 2781, - "name": "SWAP2" - }, - { - "begin": 2768, - "end": 2781, - "name": "DUP5" - }, - { - "begin": 2768, - "end": 2781, - "name": "SWAP1" - }, - { - "begin": 2768, - "end": 2781, - "name": "SHA3" - }, - { - "begin": 2768, - "end": 2791, - "name": "DUP1" - }, - { - "begin": 2768, - "end": 2791, - "name": "SLOAD" - }, - { - "begin": 2768, - "end": 2791, - "name": "DUP8" - }, - { - "begin": 2768, - "end": 2791, - "name": "ADD" - }, - { - "begin": 2768, - "end": 2791, - "name": "SWAP1" - }, - { - "begin": 2768, - "end": 2791, - "name": "SSTORE" - }, - { - "begin": 2805, - "end": 2838, - "name": "DUP4" - }, - { - "begin": 2805, - "end": 2838, - "name": "MLOAD" - }, - { - "begin": 2805, - "end": 2838, - "name": "DUP7" - }, - { - "begin": 2805, - "end": 2838, - "name": "DUP2" - }, - { - "begin": 2805, - "end": 2838, - "name": "MSTORE" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP4" - }, - { - "begin": 2805, - "end": 2838, - "name": "MLOAD" - }, - { - "begin": 2768, - "end": 2781, - "name": "SWAP2" - }, - { - "begin": 2768, - "end": 2781, - "name": "SWAP4" - }, - { - "begin": 2805, - "end": 2838, - "name": "PUSH", - "value": "DDF252AD1BE2C89B69C2B068FC378DAA952BA7F163C4A11628F55A4DF523B3EF" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP3" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP1" - }, - { - "begin": 2805, - "end": 2838, - "name": "DUP2" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP1" - }, - { - "begin": 2805, - "end": 2838, - "name": "SUB" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP1" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP2" - }, - { - "begin": 2805, - "end": 2838, - "name": "ADD" - }, - { - "begin": 2805, - "end": 2838, - "name": "SWAP1" - }, - { - "begin": 2805, - "end": 2838, - "name": "LOG3" - }, - { - "begin": -1, - "end": -1, - "name": "POP" - }, - { - "begin": 2859, - "end": 2863, - "name": "PUSH", - "value": "1" - }, - { - "begin": 2852, - "end": 2863, - "name": "PUSH [tag]", - "value": "44" - }, - { - "begin": 2852, - "end": 2863, - "name": "JUMP" - }, - { - "begin": 2631, - "end": 2917, - "name": "tag", - "value": "54" - }, - { - "begin": 2631, - "end": 2917, - "name": "JUMPDEST" - }, - { - "begin": -1, - "end": -1, - "name": "POP" - }, - { - "begin": 2901, - "end": 2906, - "name": "PUSH", - "value": "0" - }, - { - "begin": 2894, - "end": 2906, - "name": "PUSH [tag]", - "value": "44" - }, - { - "begin": 2894, - "end": 2906, - "name": "JUMP" - }, - { - "begin": 2631, - "end": 2917, - "name": "tag", - "value": "55" - }, - { - "begin": 2631, - "end": 2917, - "name": "JUMPDEST" - }, - { - "begin": 2490, - "end": 2923, - "name": "tag", - "value": "52" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMPDEST" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP3" - }, - { - "begin": 2490, - "end": 2923, - "name": "SWAP2" - }, - { - "begin": 2490, - "end": 2923, - "name": "POP" - }, - { - "begin": 2490, - "end": 2923, - "name": "POP" - }, - { - "begin": 2490, - "end": 2923, - "name": "JUMP", - "value": "[out]" - }, - { - "begin": 3719, - "end": 3848, - "name": "tag", - "value": "43" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPDEST" - }, - { - "begin": 3816, - "end": 3831, - "name": "PUSH", - "value": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP1" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP4" - }, - { - "begin": 3816, - "end": 3831, - "name": "AND" - }, - { - "begin": 3790, - "end": 3797, - "name": "PUSH", - "value": "0" - }, - { - "begin": 3816, - "end": 3831, - "name": "SWAP1" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP2" - }, - { - "begin": 3816, - "end": 3831, - "name": "MSTORE" - }, - { - "begin": 3816, - "end": 3823, - "name": "PUSH", - "value": "1" - }, - { - "begin": 3816, - "end": 3831, - "name": "PUSH", - "value": "20" - }, - { - "begin": 3816, - "end": 3831, - "name": "SWAP1" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP2" - }, - { - "begin": 3816, - "end": 3831, - "name": "MSTORE" - }, - { - "begin": 3816, - "end": 3831, - "name": "PUSH", - "value": "40" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP1" - }, - { - "begin": 3816, - "end": 3831, - "name": "DUP4" - }, - { - "begin": 3816, - "end": 3831, - "name": "SHA3" - }, - { - "begin": 3816, - "end": 3841, - "name": "SWAP4" - }, - { - "begin": 3816, - "end": 3841, - "name": "DUP6" - }, - { - "begin": 3816, - "end": 3841, - "name": "AND" - }, - { - "begin": 3816, - "end": 3841, - "name": "DUP4" - }, - { - "begin": 3816, - "end": 3841, - "name": "MSTORE" - }, - { - "begin": 3816, - "end": 3841, - "name": "SWAP3" - }, - { - "begin": 3816, - "end": 3841, - "name": "SWAP1" - }, - { - "begin": 3816, - "end": 3841, - "name": "MSTORE" - }, - { - "begin": 3816, - "end": 3841, - "name": "SHA3" - }, - { - "begin": 3816, - "end": 3841, - "name": "SLOAD" - }, - { - "begin": 3719, - "end": 3848, - "name": "tag", - "value": "56" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMPDEST" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP3" - }, - { - "begin": 3719, - "end": 3848, - "name": "SWAP2" - }, - { - "begin": 3719, - "end": 3848, - "name": "POP" - }, - { - "begin": 3719, - "end": 3848, - "name": "POP" - }, - { - "begin": 3719, - "end": 3848, - "name": "JUMP", - "value": "[out]" - } - ] - } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" } - }, - "methodIdentifiers": { - "allowance(address,address)": "dd62ed3e", - "approve(address,uint256)": "095ea7b3", - "balanceOf(address)": "70a08231", - "decimals()": "313ce567", - "name()": "06fdde03", - "symbol()": "95d89b41", - "totalSupply()": "18160ddd", - "transfer(address,uint256)": "a9059cbb", - "transferFrom(address,address,uint256)": "23b872dd" - } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "inputs": [], + "payable": false, + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" }, - "metadata": "{\"compiler\":{\"version\":\"0.4.11+commit.68ef5810\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}],\"devdoc\":{\"methods\":{\"transferFrom(address,address,uint256)\":{\"details\":\"ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\",\"params\":{\"_from\":\"Address to transfer from.\",\"_to\":\"Address to transfer to.\",\"_value\":\"Amount to transfer.\"},\"return\":\"Success of transfer.\"}}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":\"ZRXToken\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":@0x/contracts-utils=/Users/Elena/Source/protocol/contracts/erc20/node_modules/@0x/contracts-utils\"]},\"sources\":{\"/Users/Elena/Source/protocol/contracts/erc20/contracts/src/ZRXToken.sol\":{\"keccak256\":\"0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47\",\"urls\":[\"bzzr://ef728dddbaa1e26baa6cc9fe0f83de5055bc0b17dfe488018f4ee59d68ccb5dd\"]}},\"version\":1}", - "userdoc": { - "methods": {} + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" } + ], + "bytecode": { + "object": "0x60606040526b033b2e3c9fd0803ce8000000600355341561001c57fe5b5b600354600160a060020a0333166000908152602081905260409020555b5b61078d8061004a6000396000f300606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a723058202dbef854545f38e5b78ec251d65db5fa0f12b6f2f0a0039063735c2dc416d6310029", + "sourceMap": "4935:353:0:-;;;5056:8;5027:37;;5208:78;;;;;;;5268:11;;-1:-1:-1;;;;;5254:10:0;5245:20;:8;:20;;;;;;;;;;:34;5208:78;4935:353;;;;;;;", + "linkReferences": {} }, - "sourceTreeHashHex": "0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47", - "sources": { - "./ZRXToken.sol": { - "id": 0, - "content": "/*\n\n Copyright 2019 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.11;\n\ncontract Token {\n /// @return total amount of tokens\n function totalSupply() constant returns (uint256 supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint256 balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint256 _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint256 _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event Approval(address indexed _owner, address indexed _spender, uint256 _value);\n}\n\ncontract ERC20Token is Token {\n function transfer(address _to, uint256 _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function transferFrom(address _from, address _to, uint256 _value) returns (bool) {\n if (\n balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function balanceOf(address _owner) constant returns (uint256) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint256 _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint256) {\n return allowed[_owner][_spender];\n }\n\n mapping(address => uint256) balances;\n mapping(address => mapping(address => uint256)) allowed;\n uint256 public totalSupply;\n}\n\ncontract UnlimitedAllowanceToken is ERC20Token {\n uint256 constant MAX_UINT = 2 ** 256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {\n uint256 allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n uint8 public constant decimals = 18;\n uint256 public totalSupply = 10 ** 27; // 1 billion tokens, 18 decimal places\n string public constant name = \"0x Protocol Token\";\n string public constant symbol = \"ZRX\";\n\n function ZRXToken() public {\n balances[msg.sender] = totalSupply;\n }\n}\n" - } + "deployedBytecode": { + "object": "0x606060405236156100965763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde038114610098578063095ea7b31461014657806318160ddd1461018657806323b872dd146101a8578063313ce567146101ee57806370a082311461021457806395d89b411461024f578063a9059cbb146102fd578063dd62ed3e1461033d575bfe5b34156100a057fe5b6100a861037e565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014e57fe5b61017273ffffffffffffffffffffffffffffffffffffffff600435166024356103b5565b604080519115158252519081900360200190f35b341561018e57fe5b61019661042d565b60408051918252519081900360200190f35b34156101b057fe5b61017273ffffffffffffffffffffffffffffffffffffffff60043581169060243516604435610433565b604080519115158252519081900360200190f35b34156101f657fe5b6101fe6105d4565b6040805160ff9092168252519081900360200190f35b341561021c57fe5b61019673ffffffffffffffffffffffffffffffffffffffff600435166105d9565b60408051918252519081900360200190f35b341561025757fe5b6100a8610605565b60408051602080825283518183015283519192839290830191850190808383821561010c575b80518252602083111561010c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016100ce565b505050905090810190601f1680156101385780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561030557fe5b61017273ffffffffffffffffffffffffffffffffffffffff6004351660243561063c565b604080519115158252519081900360200190f35b341561034557fe5b61019673ffffffffffffffffffffffffffffffffffffffff60043581169060243516610727565b60408051918252519081900360200190f35b60408051808201909152601181527f30782050726f746f636f6c20546f6b656e000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff338116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b60035481565b73ffffffffffffffffffffffffffffffffffffffff808416600081815260016020908152604080832033909516835293815283822054928252819052918220548390108015906104835750828110155b80156104b6575073ffffffffffffffffffffffffffffffffffffffff841660009081526020819052604090205483810110155b156105c65773ffffffffffffffffffffffffffffffffffffffff808516600090815260208190526040808220805487019055918716815220805484900390557fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156105585773ffffffffffffffffffffffffffffffffffffffff808616600090815260016020908152604080832033909416835292905220805484900390555b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a3600191506105cb565b600091505b5b509392505050565b601281565b73ffffffffffffffffffffffffffffffffffffffff81166000908152602081905260409020545b919050565b60408051808201909152600381527f5a52580000000000000000000000000000000000000000000000000000000000602082015281565b73ffffffffffffffffffffffffffffffffffffffff3316600090815260208190526040812054829010801590610699575073ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110155b156107185773ffffffffffffffffffffffffffffffffffffffff33811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610427565b506000610427565b5b92915050565b73ffffffffffffffffffffffffffffffffffffffff8083166000908152600160209081526040808320938516835292905220545b929150505600a165627a7a723058202dbef854545f38e5b78ec251d65db5fa0f12b6f2f0a0039063735c2dc416d6310029", + "sourceMap": "4935:353:0:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3523:190:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5027:37;;;;;;;;;;;;;;;;;;;;;;;;;;4369:562;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4986:35;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3415:102;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5164:37;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18:2:-1;;13:3;7:5;32;59:3;53:5;48:3;41:6;93:2;88:3;85:2;78:6;73:3;67:5;152:3;;;;;117:2;108:3;;;;130;172:5;167:4;181:3;3:186;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2490:433:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3719:129;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5109:49;;;;;;;;;;;;;;;;;;;:::o;3523:190::-;3599:19;3607:10;3599:19;;3583:4;3599:19;;;:7;:19;;;;;;;;:29;;;;;;;;;;;;:38;;;3647;;;;;;;3583:4;;3599:29;:19;3647:38;;;;;;;;;;;-1:-1:-1;3702:4:0;3523:190;;;;;:::o;5027:37::-;;;;:::o;4369:562::-;4487:14;;;;4451:4;4487:14;;;:7;:14;;;;;;;;4502:10;4487:26;;;;;;;;;;;;4527:15;;;;;;;;;;:25;;;;;;:48;;;4569:6;4556:9;:19;;4527:48;:91;;;;-1:-1:-1;4605:13:0;;;:8;:13;;;;;;;;;;;4579:22;;;:39;;4527:91;4523:402;;;4634:13;;;;:8;:13;;;;;;;;;;;:23;;;;;;4671:15;;;;;;:25;;;;;;;4069:12;4714:20;;4710:95;;;4754:14;;;;;;;;:7;:14;;;;;;;;4769:10;4754:26;;;;;;;;;:36;;;;;;;4710:95;4834:3;4818:28;;4827:5;4818:28;;;4839:6;4818:28;;;;;;;;;;;;;;;;;;4867:4;4860:11;;;;4523:402;4909:5;4902:12;;4523:402;4369:562;;;;;;;:::o;4986:35::-;5019:2;4986:35;:::o;3415:102::-;3494:16;;;3468:7;3494:16;;;;;;;;;;;3415:102;;;;:::o;5164:37::-;;;;;;;;;;;;;;;;;;;:::o;2490:433::-;2635:20;2644:10;2635:20;2546:4;2635:20;;;;;;;;;;;:30;;;;;;:73;;-1:-1:-1;2695:13:0;;;:8;:13;;;;;;;;;;;2669:22;;;:39;;2635:73;2631:286;;;2724:20;2733:10;2724:20;;:8;:20;;;;;;;;;;;:30;;;;;;;2768:13;;;;;;;;;;:23;;;;;;2805:33;;;;;;;2768:13;;2805:33;;;;;;;;;;;-1:-1:-1;2859:4:0;2852:11;;2631:286;-1:-1:-1;2901:5:0;2894:12;;2631:286;2490:433;;;;;:::o;3719:129::-;3816:15;;;;3790:7;3816:15;;;:7;:15;;;;;;;;:25;;;;;;;;;;3719:129;;;;;:::o", + "linkReferences": {} }, - "sourceCodes": { - "./ZRXToken.sol": "/*\n\n Copyright 2019 ZeroEx Intl.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n\n*/\n\npragma solidity 0.4.11;\n\ncontract Token {\n /// @return total amount of tokens\n function totalSupply() constant returns (uint256 supply) {}\n\n /// @param _owner The address from which the balance will be retrieved\n /// @return The balance\n function balanceOf(address _owner) constant returns (uint256 balance) {}\n\n /// @notice send `_value` token to `_to` from `msg.sender`\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transfer(address _to, uint256 _value) returns (bool success) {}\n\n /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`\n /// @param _from The address of the sender\n /// @param _to The address of the recipient\n /// @param _value The amount of token to be transferred\n /// @return Whether the transfer was successful or not\n function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}\n\n /// @notice `msg.sender` approves `_addr` to spend `_value` tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @param _value The amount of wei to be approved for transfer\n /// @return Whether the approval was successful or not\n function approve(address _spender, uint256 _value) returns (bool success) {}\n\n /// @param _owner The address of the account owning tokens\n /// @param _spender The address of the account able to transfer the tokens\n /// @return Amount of remaining tokens allowed to spent\n function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}\n\n event Transfer(address indexed _from, address indexed _to, uint256 _value);\n event Approval(address indexed _owner, address indexed _spender, uint256 _value);\n}\n\ncontract ERC20Token is Token {\n function transfer(address _to, uint256 _value) returns (bool) {\n //Default assumes totalSupply can't be over max (2^256 - 1).\n if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {\n balances[msg.sender] -= _value;\n balances[_to] += _value;\n Transfer(msg.sender, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function transferFrom(address _from, address _to, uint256 _value) returns (bool) {\n if (\n balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]\n ) {\n balances[_to] += _value;\n balances[_from] -= _value;\n allowed[_from][msg.sender] -= _value;\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n\n function balanceOf(address _owner) constant returns (uint256) {\n return balances[_owner];\n }\n\n function approve(address _spender, uint256 _value) returns (bool) {\n allowed[msg.sender][_spender] = _value;\n Approval(msg.sender, _spender, _value);\n return true;\n }\n\n function allowance(address _owner, address _spender) constant returns (uint256) {\n return allowed[_owner][_spender];\n }\n\n mapping(address => uint256) balances;\n mapping(address => mapping(address => uint256)) allowed;\n uint256 public totalSupply;\n}\n\ncontract UnlimitedAllowanceToken is ERC20Token {\n uint256 constant MAX_UINT = 2 ** 256 - 1;\n\n /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\n /// @param _from Address to transfer from.\n /// @param _to Address to transfer to.\n /// @param _value Amount to transfer.\n /// @return Success of transfer.\n function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {\n uint256 allowance = allowed[_from][msg.sender];\n if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {\n balances[_to] += _value;\n balances[_from] -= _value;\n if (allowance < MAX_UINT) {\n allowed[_from][msg.sender] -= _value;\n }\n Transfer(_from, _to, _value);\n return true;\n } else {\n return false;\n }\n }\n}\n\ncontract ZRXToken is UnlimitedAllowanceToken {\n uint8 public constant decimals = 18;\n uint256 public totalSupply = 10 ** 27; // 1 billion tokens, 18 decimal places\n string public constant name = \"0x Protocol Token\";\n string public constant symbol = \"ZRX\";\n\n function ZRXToken() public {\n balances[msg.sender] = totalSupply;\n }\n}\n" + "methodIdentifiers": { + "allowance(address,address)": "dd62ed3e", + "approve(address,uint256)": "095ea7b3", + "balanceOf(address)": "70a08231", + "decimals()": "313ce567", + "name()": "06fdde03", + "symbol()": "95d89b41", + "totalSupply()": "18160ddd", + "transfer(address,uint256)": "a9059cbb", + "transferFrom(address,address,uint256)": "23b872dd" }, - "compiler": { - "name": "solc", - "version": "0.4.11+commit.68ef5810", + "rawMetadata": "{\"compiler\":{\"version\":\"0.4.11+commit.68ef5810\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"_owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"_spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}],\"devdoc\":{\"methods\":{\"transferFrom(address,address,uint256)\":{\"details\":\"ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.\",\"params\":{\"_from\":\"Address to transfer from.\",\"_to\":\"Address to transfer to.\",\"_value\":\"Amount to transfer.\"},\"return\":\"Success of transfer.\"}}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"contracts/erc20/src/ZRXToken.sol\":\"ZRXToken\"},\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":1000000},\"remappings\":[\":@0x/contracts-erc20/=contracts/erc20/\",\":@0x/contracts-utils/=contracts/utils/\",\":ds-test/=contracts/erc20/lib/forge-std/lib/ds-test/src/\",\":forge-std/=contracts/erc20/lib/forge-std/src/\"]},\"sources\":{\"contracts/erc20/src/ZRXToken.sol\":{\"keccak256\":\"0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47\",\"urls\":[\"bzzr://ef728dddbaa1e26baa6cc9fe0f83de5055bc0b17dfe488018f4ee59d68ccb5dd\"]}},\"version\":1}", + "metadata": { + "compiler": { + "version": "0.4.11+commit.68ef5810" + }, + "language": "Solidity", + "output": { + "abi": [ + { + "inputs": [], + "type": "function", + "name": "name", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "string" + } + ] + }, + { + "inputs": [ + { + "internalType": null, + "name": "_spender", + "type": "address" + }, + { + "internalType": null, + "name": "_value", + "type": "uint256" + } + ], + "type": "function", + "name": "approve", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "type": "function", + "name": "totalSupply", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [ + { + "internalType": null, + "name": "_from", + "type": "address" + }, + { + "internalType": null, + "name": "_to", + "type": "address" + }, + { + "internalType": null, + "name": "_value", + "type": "uint256" + } + ], + "type": "function", + "name": "transferFrom", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [], + "type": "function", + "name": "decimals", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "uint8" + } + ] + }, + { + "inputs": [ + { + "internalType": null, + "name": "_owner", + "type": "address" + } + ], + "type": "function", + "name": "balanceOf", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "type": "function", + "name": "symbol", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "string" + } + ] + }, + { + "inputs": [ + { + "internalType": null, + "name": "_to", + "type": "address" + }, + { + "internalType": null, + "name": "_value", + "type": "uint256" + } + ], + "type": "function", + "name": "transfer", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "bool" + } + ] + }, + { + "inputs": [ + { + "internalType": null, + "name": "_owner", + "type": "address" + }, + { + "internalType": null, + "name": "_spender", + "type": "address" + } + ], + "type": "function", + "name": "allowance", + "outputs": [ + { + "internalType": null, + "name": "", + "type": "uint256" + } + ] + }, + { + "inputs": [], + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": null, + "name": "_from", + "type": "address", + "indexed": true + }, + { + "internalType": null, + "name": "_to", + "type": "address", + "indexed": true + }, + { + "internalType": null, + "name": "_value", + "type": "uint256", + "indexed": false + } + ], + "type": "event", + "name": "Transfer", + "anonymous": false + }, + { + "inputs": [ + { + "internalType": null, + "name": "_owner", + "type": "address", + "indexed": true + }, + { + "internalType": null, + "name": "_spender", + "type": "address", + "indexed": true + }, + { + "internalType": null, + "name": "_value", + "type": "uint256", + "indexed": false + } + ], + "type": "event", + "name": "Approval", + "anonymous": false + } + ], + "devdoc": { + "methods": { + "transferFrom(address,address,uint256)": { + "details": "ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.", + "params": { + "_from": "Address to transfer from.", + "_to": "Address to transfer to.", + "_value": "Amount to transfer." + }, + "return": "Success of transfer." + } + } + }, + "userdoc": { + "methods": {} + } + }, "settings": { "remappings": [ - "@0x/contracts-utils=/Users/Elena/Source/protocol/contracts/erc20/node_modules/@0x/contracts-utils" + ":@0x/contracts-erc20/=contracts/erc20/", + ":@0x/contracts-utils/=contracts/utils/", + ":ds-test/=contracts/erc20/lib/forge-std/lib/ds-test/src/", + ":forge-std/=contracts/erc20/lib/forge-std/src/" ], "optimizer": { "enabled": true, - "runs": 1000000, - "details": { - "yul": true, - "deduplicate": true, - "cse": true, - "constantOptimizer": true - } + "runs": 1000000 }, - "outputSelection": { - "*": { - "*": [ - "abi", - "devdoc", - "evm.bytecode.object", - "evm.bytecode.sourceMap", - "evm.deployedBytecode.object", - "evm.deployedBytecode.sourceMap", - "devdoc" - ] - } + "compilationTarget": { + "contracts/erc20/src/ZRXToken.sol": "ZRXToken" }, - "evmVersion": "istanbul" - } + "libraries": {} + }, + "sources": { + "contracts/erc20/src/ZRXToken.sol": { + "keccak256": "0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47", + "urls": ["bzzr://ef728dddbaa1e26baa6cc9fe0f83de5055bc0b17dfe488018f4ee59d68ccb5dd"], + "license": null + } + }, + "version": 1 }, - "chains": {} + "id": 0 } diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index 3660d4075e..b9e3277b8e 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -2,6 +2,6 @@ src = 'src' out = 'out' libs = ['lib'] -fs_permissions = [{ access = "read", path = "./"}] +fs_permissions = [{ access = "read", path = "./ZRXToken.json"}] remappings = ['@openzeppelin/=./lib/openzeppelin-contracts/contracts/'] optimizer_runs = 20_000 diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index d24491043d..4f0b4e004e 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -47,21 +47,17 @@ contract BaseTest is Test { internal returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor, ZeroExTreasuryGovernor) { - // Use this once https://linear.app/0xproject/issue/PRO-44/zrx-artifact-is-incompatible-with-foundry is resolved - // bytes memory _bytecode = abi.encodePacked(vm.getCode("./ZRXToken.json")); - // address _address; - // assembly { - // _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) - // } - // return address(_address); - - ZRXMock mockZRX = new ZRXMock(); + bytes memory _bytecode = vm.getCode("./ZRXToken.json"); + address _address; + assembly { + _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) + } ZeroExVotes votes = new ZeroExVotes(); ERC1967Proxy votesProxy = new ERC1967Proxy(address(votes), new bytes(0)); votes = ZeroExVotes(address(votesProxy)); - ZRXWrappedToken token = new ZRXWrappedToken(mockZRX, IZeroExVotes(address(votesProxy))); + ZRXWrappedToken token = new ZRXWrappedToken(IERC20(address(_address)), IZeroExVotes(address(votesProxy))); votes.initialize(address(token)); address[] memory proposers = new address[](0); @@ -77,6 +73,6 @@ contract BaseTest is Test { protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes))); - return (mockZRX, token, votes, protocolTimelock, protocolGovernor, treasuryGovernor); + return (IERC20(address(_address)), token, votes, protocolTimelock, protocolGovernor, treasuryGovernor); } } From c1966e0219d7727f4d7290bb26723a54f593f1b5 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 19 Feb 2023 21:03:38 +0200 Subject: [PATCH 041/106] Fix rebase issue --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index d7c1ed2850..b83bfd45ae 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,6 +3,7 @@ url = https://github.com/foundry-rs/forge-std [submodule "contracts/erc20/lib/forge-std"] path = contracts/erc20/lib/forge-std + url = https://github.com/foundry-rs/forge-std [submodule "contracts/governance/lib/forge-std"] path = contracts/governance/lib/forge-std url = https://github.com/foundry-rs/forge-std From f258bb41122ee58aaff9e4f1d3d5bbb453d35cce Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 20 Feb 2023 12:40:24 +0200 Subject: [PATCH 042/106] Add timelock control over the treasury governor --- .../governance/src/ZeroExTreasuryGovernor.sol | 57 ++++++++- contracts/governance/test/BaseTest.t.sol | 49 +++++--- .../governance/test/ZRXWrappedTokenTest.t.sol | 2 +- .../test/ZeroExProtocolGovernor.t.sol | 2 +- .../test/ZeroExTreasuryGovernor.t.sol | 110 +++++++++++------- 5 files changed, 157 insertions(+), 63 deletions(-) diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 342e7e41d6..32a02edee3 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -22,16 +22,25 @@ import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; import "./IZeroExVotes.sol"; -contract ZeroExTreasuryGovernor is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes { +contract ZeroExTreasuryGovernor is + Governor, + GovernorSettings, + GovernorCountingSimple, + GovernorVotes, + GovernorTimelockControl +{ constructor( - IVotes _token + IVotes votes, + TimelockController _timelock ) Governor("ZeroExTreasuryGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 5e11) - GovernorVotes(_token) + GovernorVotes(votes) + GovernorTimelockControl(_timelock) {} function quorum(uint256 blockNumber) public pure override returns (uint256) { @@ -63,4 +72,46 @@ contract ZeroExTreasuryGovernor is Governor, GovernorSettings, GovernorCountingS ) internal view virtual override(Governor, GovernorVotes) returns (uint256) { return IZeroExVotes(address(token)).getPastQuadraticVotes(account, blockNumber); } + + function state(uint256 proposalId) public view override(Governor, GovernorTimelockControl) returns (ProposalState) { + return super.state(proposalId); + } + + function propose( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + string memory description + ) public override(Governor, IGovernor) returns (uint256) { + return super.propose(targets, values, calldatas, description); + } + + function _execute( + uint256 proposalId, + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) { + super._execute(proposalId, targets, values, calldatas, descriptionHash); + } + + function _cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) internal override(Governor, GovernorTimelockControl) returns (uint256) { + return super._cancel(targets, values, calldatas, descriptionHash); + } + + function _executor() internal view override(Governor, GovernorTimelockControl) returns (address) { + return super._executor(); + } + + function supportsInterface( + bytes4 interfaceId + ) public view override(Governor, GovernorTimelockControl) returns (bool) { + return super.supportsInterface(interfaceId); + } } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 4f0b4e004e..8e00ec6fd2 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -45,34 +45,51 @@ contract BaseTest is Test { function setupGovernance() internal - returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExProtocolGovernor, ZeroExTreasuryGovernor) + returns ( + IERC20, + ZRXWrappedToken, + ZeroExVotes, + ZeroExTimelock, + ZeroExTimelock, + ZeroExProtocolGovernor, + ZeroExTreasuryGovernor + ) { + (IERC20 zrxToken, ZRXWrappedToken token, ZeroExVotes votes) = setupZRXWrappedToken(); + + address[] memory proposers = new address[](0); + address[] memory executors = new address[](0); + + ZeroExTimelock protocolTimelock = new ZeroExTimelock(3 days, proposers, executors, account1); + ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); + protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); + protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); + + ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); + ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); + + treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); + treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); + + return (zrxToken, token, votes, protocolTimelock, treasuryTimelock, protocolGovernor, treasuryGovernor); + } + + function setupZRXWrappedToken() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes) { bytes memory _bytecode = vm.getCode("./ZRXToken.json"); address _address; assembly { _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) } + IERC20 zrxToken = IERC20(address(_address)); + ZeroExVotes votes = new ZeroExVotes(); ERC1967Proxy votesProxy = new ERC1967Proxy(address(votes), new bytes(0)); votes = ZeroExVotes(address(votesProxy)); - ZRXWrappedToken token = new ZRXWrappedToken(IERC20(address(_address)), IZeroExVotes(address(votesProxy))); + ZRXWrappedToken token = new ZRXWrappedToken(zrxToken, IZeroExVotes(address(votesProxy))); votes.initialize(address(token)); - address[] memory proposers = new address[](0); - address[] memory executors = new address[](0); - - ZeroExTimelock protocolTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); - ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor( - IVotes(address(votesProxy)), - protocolTimelock - ); - - protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); - protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); - - ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes))); - return (IERC20(address(_address)), token, votes, protocolTimelock, protocolGovernor, treasuryGovernor); + return (zrxToken, token, votes); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 5ab9eb8cca..5e46bd1f8d 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -29,7 +29,7 @@ contract ZRXWrappedTokenTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, , , ) = setupGovernance(); + (token, wToken, votes, , , , ) = setupGovernance(); token.transfer(account2, 100e18); token.transfer(account3, 200e18); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 3411867a4d..b16623d9df 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -35,7 +35,7 @@ contract ZeroExProtocolGovernorTest is BaseTest { function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, governor, ) = setupGovernance(); + (token, wToken, votes, timelock, , governor, ) = setupGovernance(); token.transfer(account2, 10000000e18); token.transfer(account3, 2000000e18); token.transfer(account4, 3000000e18); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 3898275e12..95ca1d805f 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -30,12 +30,12 @@ contract ZeroExTreasuryGovernorTest is BaseTest { ZRXWrappedToken internal wToken; ZeroExVotes internal votes; ZeroExTimelock internal timelock; - ZeroExTreasuryGovernor internal governor; + ZeroExTreasuryGovernor internal treasuryGovernor; CallReceiverMock internal callReceiverMock; function setUp() public { vm.startPrank(account1); - (token, wToken, votes, timelock, , governor) = setupGovernance(); + (token, wToken, votes, , timelock, , treasuryGovernor) = setupGovernance(); // quorum 5000000e18 // proposal threshold 250000e18 = sqrt(6.25e46) 5e11 token.transfer(account2, 10000000e18); @@ -66,27 +66,27 @@ contract ZeroExTreasuryGovernorTest is BaseTest { } function testShouldReturnCorrectName() public { - assertEq(governor.name(), "ZeroExTreasuryGovernor"); + assertEq(treasuryGovernor.name(), "ZeroExTreasuryGovernor"); } function testShouldReturnCorrectVotingDelay() public { - assertEq(governor.votingDelay(), 14400); + assertEq(treasuryGovernor.votingDelay(), 14400); } function testShouldReturnCorrectVotingPeriod() public { - assertEq(governor.votingPeriod(), 50400); + assertEq(treasuryGovernor.votingPeriod(), 50400); } function testShouldReturnCorrectProposalThreshold() public { - assertEq(governor.proposalThreshold(), 5e11); + assertEq(treasuryGovernor.proposalThreshold(), 5e11); } function testShouldReturnCorrectQuorum() public { - assertEq(governor.quorum(block.number), 23e11); + assertEq(treasuryGovernor.quorum(block.number), 23e11); } function testShouldReturnCorrectToken() public { - assertEq(address(governor.token()), address(votes)); + assertEq(address(treasuryGovernor.token()), address(votes)); } function testShouldBeAbleToExecuteASuccessfulProposal() public { @@ -102,139 +102,165 @@ contract ZeroExTreasuryGovernorTest is BaseTest { vm.roll(2); vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Proposal description"); vm.stopPrank(); // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" + treasuryGovernor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); vm.prank(account3); - governor.castVote(proposalId, 0); // Vote "against" + treasuryGovernor.castVote(proposalId, 0); // Vote "against" vm.stopPrank(); vm.prank(account4); - governor.castVote(proposalId, 2); // Vote "abstain" + treasuryGovernor.castVote(proposalId, 2); // Vote "abstain" vm.stopPrank(); // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); // Get vote results - (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); + (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = treasuryGovernor.proposalVotes(proposalId); assertEq(votesFor, Math.sqrt(10000000e18)); assertEq(votesAgainst, Math.sqrt(2000000e18)); assertEq(votesAbstain, Math.sqrt(3000000e18)); - IGovernor.ProposalState state = governor.state(proposalId); + IGovernor.ProposalState state = treasuryGovernor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); - governor.execute(targets, values, calldatas, keccak256("Proposal description")); - state = governor.state(proposalId); + // Queue proposal + treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); + + treasuryGovernor.execute(targets, values, calldatas, keccak256("Proposal description")); + state = treasuryGovernor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } function testCanUpdateVotingDelaySetting() public { // Create a proposal address[] memory targets = new address[](1); - targets[0] = address(governor); + targets[0] = address(treasuryGovernor); uint256[] memory values = new uint256[](1); values[0] = 0; bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setVotingDelay.selector, 3 days); + calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setVotingDelay.selector, 3 days); vm.roll(2); vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); + uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); vm.stopPrank(); // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" + treasuryGovernor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + + // Queue proposal + treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); + vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); + treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); - uint256 votingDelay = governor.votingDelay(); + uint256 votingDelay = treasuryGovernor.votingDelay(); assertEq(votingDelay, 3 days); } function testCanUpdateVotingPeriodSetting() public { // Create a proposal address[] memory targets = new address[](1); - targets[0] = address(governor); + targets[0] = address(treasuryGovernor); uint256[] memory values = new uint256[](1); values[0] = 0; bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setVotingPeriod.selector, 14 days); + calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setVotingPeriod.selector, 14 days); vm.roll(2); vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting period to 14 days"); + uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Increase voting period to 14 days"); vm.stopPrank(); // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" + treasuryGovernor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + + // Queue proposal + treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); + vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); + treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); - uint256 votingPeriod = governor.votingPeriod(); + uint256 votingPeriod = treasuryGovernor.votingPeriod(); assertEq(votingPeriod, 14 days); } function testCanUpdateProposalThresholdSetting() public { // Create a proposal address[] memory targets = new address[](1); - targets[0] = address(governor); + targets[0] = address(treasuryGovernor); uint256[] memory values = new uint256[](1); values[0] = 0; bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setProposalThreshold.selector, 2000000e18); + calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setProposalThreshold.selector, 2000000e18); vm.roll(2); vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase proposal threshold to 2000000e18"); + uint256 proposalId = treasuryGovernor.propose( + targets, + values, + calldatas, + "Increase proposal threshold to 2000000e18" + ); vm.stopPrank(); // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" + treasuryGovernor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + + // Queue proposal + treasuryGovernor.queue( + targets, + values, + calldatas, + keccak256(bytes("Increase proposal threshold to 2000000e18")) + ); + vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); + treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); - uint256 proposalThreshold = governor.proposalThreshold(); + uint256 proposalThreshold = treasuryGovernor.proposalThreshold(); assertEq(proposalThreshold, 2000000e18); } } From 091e0a50ad93ebc7d0f01ea6825fb3ffc90a91d5 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 20 Feb 2023 15:28:57 +0200 Subject: [PATCH 043/106] Add test for wrapped token transfer --- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 5e46bd1f8d..7b43facf34 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -139,6 +139,18 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votes.getPastQuadraticTotalSupply(0), 0); } + function testShouldBeAbleToTransferCorrectly() public { + assertEq(wToken.balanceOf(account4), 0); + + vm.startPrank(account2); + token.approve(address(wToken), 1e18); + wToken.depositFor(account2, 1e18); + wToken.transfer(account4, 1e17); + vm.stopPrank(); + + assertEq(wToken.balanceOf(account4), 1e17); + } + function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { vm.expectRevert("ZeroExVotes: Already initialized"); votes.initialize(account2); From e223ba09ad8de99506b7661bf351faf6d6577a97 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 20 Feb 2023 18:08:18 +0200 Subject: [PATCH 044/106] Emit separate events for changing linear and quadratic voting power --- contracts/governance/src/IZeroExVotes.sol | 12 ++++++++---- contracts/governance/src/ZeroExVotes.sol | 24 +++++++++++------------ 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 146ab821bf..892103d0e4 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -27,16 +27,20 @@ interface IZeroExVotes { } /** - * @dev Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + * @dev Emitted when a token transfer or delegate change, + * results in changes to a delegate's quadratic number of votes. */ - event DelegateVotesChanged( + event DelegateQuadraticVotesChanged( address indexed delegate, - uint256 previousLinearBalance, - uint256 newLinearBalance, uint256 previousQuadraticBalance, uint256 newQuadraticBalance ); + /** + * @dev Emitted when a token transfer or delegate change, results in changes to a delegate's number of votes. + */ + event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + /** * @dev Get the `pos`-th checkpoint for `account`. */ diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index da2d6b87c3..eb49ca6216 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -134,10 +134,10 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); - emit DelegateVotesChanged( + emit DelegateVotesChanged(srcDelegatee, oldCkptSrcDelegate.votes, newLinearBalance); + + emit DelegateQuadraticVotesChanged( srcDelegatee, - oldCkptSrcDelegate.votes, - newLinearBalance, oldCkptSrcDelegate.quadraticVotes, newQuadraticBalance ); @@ -154,10 +154,10 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); - emit DelegateVotesChanged( + emit DelegateVotesChanged(dstDelegatee, oldCkptDstDelegate.votes, newLinearBalance); + + emit DelegateQuadraticVotesChanged( dstDelegatee, - oldCkptDstDelegate.votes, - newLinearBalance, oldCkptDstDelegate.quadraticVotes, newQuadraticBalance ); @@ -192,10 +192,10 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); - emit DelegateVotesChanged( + emit DelegateVotesChanged(srcDelegatee, oldCkptSrcDelegate.votes, newLinearBalance); + + emit DelegateQuadraticVotesChanged( srcDelegatee, - oldCkptSrcDelegate.votes, - newLinearBalance, oldCkptSrcDelegate.quadraticVotes, newQuadraticBalance ); @@ -217,10 +217,10 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); - emit DelegateVotesChanged( + emit DelegateVotesChanged(dstDelegatee, oldCkptDstDelegate.votes, newLinearBalance); + + emit DelegateQuadraticVotesChanged( dstDelegatee, - oldCkptDstDelegate.votes, - newLinearBalance, oldCkptDstDelegate.quadraticVotes, newQuadraticBalance ); From d18c3ca88f9bcf74004b717866a224d7bb95c147 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 21 Feb 2023 15:16:41 +0200 Subject: [PATCH 045/106] Add the ability to cancel a proposal --- .../governance/src/ZeroExProtocolGovernor.sol | 9 ++++ .../governance/src/ZeroExTreasuryGovernor.sol | 9 ++++ contracts/governance/test/BaseTest.t.sol | 4 ++ .../test/ZeroExProtocolGovernor.t.sol | 41 +++++++++++++++++++ .../test/ZeroExTreasuryGovernor.t.sol | 41 +++++++++++++++++++ 5 files changed, 104 insertions(+) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index a210f7d87a..69e75eb17d 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -72,6 +72,15 @@ contract ZeroExProtocolGovernor is return super.proposalThreshold(); } + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public { + _cancel(targets, values, calldatas, descriptionHash); + } + function _execute( uint256 proposalId, address[] memory targets, diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 32a02edee3..48c5f1c93e 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -86,6 +86,15 @@ contract ZeroExTreasuryGovernor is return super.propose(targets, values, calldatas, description); } + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public { + _cancel(targets, values, calldatas, descriptionHash); + } + function _execute( uint256 proposalId, address[] memory targets, diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 8e00ec6fd2..820ca9ebf6 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -35,12 +35,14 @@ contract BaseTest is Test { address payable internal account2 = payable(vm.addr(2)); address payable internal account3 = payable(vm.addr(3)); address payable internal account4 = payable(vm.addr(4)); + address payable internal securityCouncil = payable(vm.addr(5)); constructor() public { vm.deal(account1, 1e20); vm.deal(account2, 1e20); vm.deal(account3, 1e20); vm.deal(account4, 1e20); + vm.deal(securityCouncil, 1e20); } function setupGovernance() @@ -64,12 +66,14 @@ contract BaseTest is Test { ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); + protocolTimelock.grantRole(protocolTimelock.CANCELLER_ROLE(), address(protocolGovernor)); ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); + treasuryTimelock.grantRole(treasuryTimelock.CANCELLER_ROLE(), address(treasuryGovernor)); return (zrxToken, token, votes, protocolTimelock, treasuryTimelock, protocolGovernor, treasuryGovernor); } diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index b16623d9df..bc849738c4 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -289,4 +289,45 @@ contract ZeroExProtocolGovernorTest is BaseTest { uint256 timelockDelay = timelock.getMinDelay(); assertEq(timelockDelay, 7 days); } + + function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + IGovernor.ProposalState state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + // Cancel the proposal + vm.warp(governor.proposalEta(proposalId)); + governor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); + } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 95ca1d805f..6a412e1632 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -263,4 +263,45 @@ contract ZeroExTreasuryGovernorTest is BaseTest { uint256 proposalThreshold = treasuryGovernor.proposalThreshold(); assertEq(proposalThreshold, 2000000e18); } + + function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + treasuryGovernor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + + IGovernor.ProposalState state = treasuryGovernor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + + // Queue proposal + treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + // Cancel the proposal + vm.warp(treasuryGovernor.proposalEta(proposalId)); + treasuryGovernor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + state = treasuryGovernor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); + } } From 94adc8fdfa38a06d75093643279fbb87f0f37a61 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 21 Feb 2023 16:21:00 +0200 Subject: [PATCH 046/106] Limit the governors' cancel function to security council only --- contracts/governance/src/ZeroExProtocolGovernor.sol | 10 ++++++++-- contracts/governance/src/ZeroExTreasuryGovernor.sol | 10 ++++++++-- contracts/governance/test/BaseTest.t.sol | 12 ++++++++++-- .../governance/test/ZeroExProtocolGovernor.t.sol | 3 +++ .../governance/test/ZeroExTreasuryGovernor.t.sol | 2 ++ 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 69e75eb17d..73c2aa5847 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -31,15 +31,20 @@ contract ZeroExProtocolGovernor is GovernorVotes, GovernorTimelockControl { + address public securityCouncil; + constructor( IVotes _token, - TimelockController _timelock + TimelockController _timelock, + address _securityCouncil ) Governor("ZeroExProtocolGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 1000000e18) GovernorVotes(_token) GovernorTimelockControl(_timelock) - {} + { + securityCouncil = _securityCouncil; + } function quorum(uint256 blockNumber) public pure override returns (uint256) { return 10000000e18; @@ -78,6 +83,7 @@ contract ZeroExProtocolGovernor is bytes[] memory calldatas, bytes32 descriptionHash ) public { + require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); _cancel(targets, values, calldatas, descriptionHash); } diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 48c5f1c93e..eaad3c9ed5 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -33,15 +33,20 @@ contract ZeroExTreasuryGovernor is GovernorVotes, GovernorTimelockControl { + address public securityCouncil; + constructor( IVotes votes, - TimelockController _timelock + TimelockController _timelock, + address _securityCouncil ) Governor("ZeroExTreasuryGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 5e11) GovernorVotes(votes) GovernorTimelockControl(_timelock) - {} + { + securityCouncil = _securityCouncil; + } function quorum(uint256 blockNumber) public pure override returns (uint256) { return 23e11; @@ -92,6 +97,7 @@ contract ZeroExTreasuryGovernor is bytes[] memory calldatas, bytes32 descriptionHash ) public { + require(msg.sender == securityCouncil, "ZeroExTreasuryGovernor: Only security council allowed"); _cancel(targets, values, calldatas, descriptionHash); } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 820ca9ebf6..978b86c1fe 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -63,13 +63,21 @@ contract BaseTest is Test { address[] memory executors = new address[](0); ZeroExTimelock protocolTimelock = new ZeroExTimelock(3 days, proposers, executors, account1); - ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(IVotes(address(votes)), protocolTimelock); + ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor( + IVotes(address(votes)), + protocolTimelock, + securityCouncil + ); protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor)); protocolTimelock.grantRole(protocolTimelock.CANCELLER_ROLE(), address(protocolGovernor)); ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, account1); - ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(IVotes(address(votes)), treasuryTimelock); + ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor( + IVotes(address(votes)), + treasuryTimelock, + securityCouncil + ); treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index bc849738c4..08428fc50e 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -325,7 +325,10 @@ contract ZeroExProtocolGovernorTest is BaseTest { // Cancel the proposal vm.warp(governor.proposalEta(proposalId)); + + vm.prank(securityCouncil); governor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.stopPrank(); state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 6a412e1632..a1a3a99d84 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -299,7 +299,9 @@ contract ZeroExTreasuryGovernorTest is BaseTest { // Cancel the proposal vm.warp(treasuryGovernor.proposalEta(proposalId)); + vm.prank(securityCouncil); treasuryGovernor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.stopPrank(); state = treasuryGovernor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); From 9f96dcb72127227092a1dab3e313f554b51473d1 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 21 Feb 2023 16:23:52 +0200 Subject: [PATCH 047/106] Eject security council after a proposal is cancelled --- contracts/governance/src/ZeroExProtocolGovernor.sol | 3 +++ contracts/governance/src/ZeroExTreasuryGovernor.sol | 3 +++ contracts/governance/test/ZeroExProtocolGovernor.t.sol | 2 ++ contracts/governance/test/ZeroExTreasuryGovernor.t.sol | 2 ++ 4 files changed, 10 insertions(+) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 73c2aa5847..49b1e2005e 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -85,6 +85,9 @@ contract ZeroExProtocolGovernor is ) public { require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); _cancel(targets, values, calldatas, descriptionHash); + + // Eject security council + securityCouncil = address(0); } function _execute( diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index eaad3c9ed5..b2e64767d0 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -99,6 +99,9 @@ contract ZeroExTreasuryGovernor is ) public { require(msg.sender == securityCouncil, "ZeroExTreasuryGovernor: Only security council allowed"); _cancel(targets, values, calldatas, descriptionHash); + + // Eject security council + securityCouncil = address(0); } function _execute( diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 08428fc50e..d8446a7586 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -332,5 +332,7 @@ contract ZeroExProtocolGovernorTest is BaseTest { state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); + + assertEq(governor.securityCouncil(), address(0)); } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index a1a3a99d84..d947728512 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -305,5 +305,7 @@ contract ZeroExTreasuryGovernorTest is BaseTest { state = treasuryGovernor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); + + assertEq(treasuryGovernor.securityCouncil(), address(0)); } } From 5afedda753d61ed6740d5375c8c149aa3ecd9bf0 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 22 Feb 2023 08:30:21 +0200 Subject: [PATCH 048/106] Add ability for governance to set the security council --- .../governance/src/ZeroExProtocolGovernor.sol | 4 ++ .../governance/src/ZeroExTreasuryGovernor.sol | 4 ++ .../test/ZeroExProtocolGovernor.t.sol | 47 +++++++++++++++++++ .../test/ZeroExTreasuryGovernor.t.sol | 47 +++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 49b1e2005e..ae8b192340 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -90,6 +90,10 @@ contract ZeroExProtocolGovernor is securityCouncil = address(0); } + function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { + securityCouncil = _securityCouncil; + } + function _execute( uint256 proposalId, address[] memory targets, diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index b2e64767d0..4b8cacb873 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -104,6 +104,10 @@ contract ZeroExTreasuryGovernor is securityCouncil = address(0); } + function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { + securityCouncil = _securityCouncil; + } + function _execute( uint256 proposalId, address[] memory targets, diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index d8446a7586..2ffc2e04c9 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -91,6 +91,10 @@ contract ZeroExProtocolGovernorTest is BaseTest { assertEq(address(governor.timelock()), address(timelock)); } + function testShouldReturnCorrectSecurityCouncil() public { + assertEq(governor.securityCouncil(), securityCouncil); + } + function testShouldBeAbleToExecuteASuccessfulProposal() public { // Create a proposal address[] memory targets = new address[](1); @@ -335,4 +339,47 @@ contract ZeroExProtocolGovernorTest is BaseTest { assertEq(governor.securityCouncil(), address(0)); } + + function testCanAssignSecurityCouncil() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.assignSecurityCouncil.selector, account1); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Assign new security council"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); + vm.warp(governor.proposalEta(proposalId) + 1); + + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Assign new security council")); + + address newSecurityCouncil = governor.securityCouncil(); + assertEq(newSecurityCouncil, account1); + } + + function testCannotAssignSecurityCouncilOutsideOfGovernance() public { + vm.expectRevert("Governor: onlyGovernance"); + governor.assignSecurityCouncil(account1); + } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index d947728512..0b24e4cf70 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -89,6 +89,10 @@ contract ZeroExTreasuryGovernorTest is BaseTest { assertEq(address(treasuryGovernor.token()), address(votes)); } + function testShouldReturnCorrectSecurityCouncil() public { + assertEq(treasuryGovernor.securityCouncil(), securityCouncil); + } + function testShouldBeAbleToExecuteASuccessfulProposal() public { // Create a proposal address[] memory targets = new address[](1); @@ -308,4 +312,47 @@ contract ZeroExTreasuryGovernorTest is BaseTest { assertEq(treasuryGovernor.securityCouncil(), address(0)); } + + function testCanAssignSecurityCouncil() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(treasuryGovernor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(treasuryGovernor.assignSecurityCouncil.selector, account1); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Assign new security council"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + treasuryGovernor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + + // Queue proposal + treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); + vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); + + // Execute proposal + treasuryGovernor.execute(targets, values, calldatas, keccak256("Assign new security council")); + + address newSecurityCouncil = treasuryGovernor.securityCouncil(); + assertEq(newSecurityCouncil, account1); + } + + function testCannotAssignSecurityCouncilOutsideOfGovernance() public { + vm.expectRevert("Governor: onlyGovernance"); + treasuryGovernor.assignSecurityCouncil(account1); + } } From b4301839b45355e4386a605eb0b48e376236fa6f Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 22 Feb 2023 17:34:59 +0200 Subject: [PATCH 049/106] Merge the governors test suites into one reusable set of tests --- contracts/governance/test/BaseTest.t.sol | 22 +- contracts/governance/test/IZeroExGovernor.sol | 49 +++ .../governance/test/ZRXWrappedTokenTest.t.sol | 2 +- .../test/ZeroExGovernorBaseTest.t.sol | 338 ++++++++++++++++++ .../test/ZeroExProtocolGovernor.t.sol | 317 +--------------- .../test/ZeroExTreasuryGovernor.t.sol | 314 ++-------------- 6 files changed, 430 insertions(+), 612 deletions(-) create mode 100644 contracts/governance/test/IZeroExGovernor.sol create mode 100644 contracts/governance/test/ZeroExGovernorBaseTest.t.sol diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 978b86c1fe..3fd1a0afd0 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -47,16 +47,9 @@ contract BaseTest is Test { function setupGovernance() internal - returns ( - IERC20, - ZRXWrappedToken, - ZeroExVotes, - ZeroExTimelock, - ZeroExTimelock, - ZeroExProtocolGovernor, - ZeroExTreasuryGovernor - ) + returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExTimelock, address, address) { + vm.startPrank(account1); (IERC20 zrxToken, ZRXWrappedToken token, ZeroExVotes votes) = setupZRXWrappedToken(); address[] memory proposers = new address[](0); @@ -82,8 +75,17 @@ contract BaseTest is Test { treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor)); treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor)); treasuryTimelock.grantRole(treasuryTimelock.CANCELLER_ROLE(), address(treasuryGovernor)); + vm.stopPrank(); - return (zrxToken, token, votes, protocolTimelock, treasuryTimelock, protocolGovernor, treasuryGovernor); + return ( + zrxToken, + token, + votes, + protocolTimelock, + treasuryTimelock, + address(protocolGovernor), + address(treasuryGovernor) + ); } function setupZRXWrappedToken() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes) { diff --git a/contracts/governance/test/IZeroExGovernor.sol b/contracts/governance/test/IZeroExGovernor.sol new file mode 100644 index 0000000000..8fb853876c --- /dev/null +++ b/contracts/governance/test/IZeroExGovernor.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "@openzeppelin/governance/IGovernor.sol"; +import "@openzeppelin/governance/extensions/IGovernorTimelock.sol"; + +abstract contract IZeroExGovernor is IGovernor, IGovernorTimelock { + function token() public virtual returns (address); + + function securityCouncil() public virtual returns (address); + + function proposalThreshold() public view virtual returns (uint256); + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public virtual; + + function assignSecurityCouncil(address _securityCouncil) public virtual; + + function setVotingDelay(uint256 newVotingDelay) public virtual; + + function setVotingPeriod(uint256 newVotingPeriod) public virtual; + + function setProposalThreshold(uint256 newProposalThreshold) public virtual; + + function proposalVotes( + uint256 proposalId + ) public view virtual returns (uint256 againstVotes, uint256 forVotes, uint256 abstainVotes); +} diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 7b43facf34..41ed0ecd09 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -28,8 +28,8 @@ contract ZRXWrappedTokenTest is BaseTest { ZeroExVotes private votes; function setUp() public { - vm.startPrank(account1); (token, wToken, votes, , , , ) = setupGovernance(); + vm.startPrank(account1); token.transfer(account2, 100e18); token.transfer(account3, 200e18); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol new file mode 100644 index 0000000000..948aca3bc1 --- /dev/null +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF IZeroExGovernorANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.17; + +import "./BaseTest.t.sol"; +import "./IZeroExGovernor.sol"; +import "../src/ZeroExTimelock.sol"; +import "../src/ZeroExProtocolGovernor.sol"; +import "../src/ZRXWrappedToken.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; +import "@openzeppelin/mocks/CallReceiverMock.sol"; + +abstract contract ZeroExGovernorBaseTest is BaseTest { + IERC20 public token; + ZRXWrappedToken internal wToken; + ZeroExVotes internal votes; + ZeroExTimelock internal timelock; + IZeroExGovernor internal governor; + CallReceiverMock internal callReceiverMock; + + string internal governorName; + uint256 internal proposalThreshold; + uint256 internal quorum; + + function initialiseAccounts() public { + vm.startPrank(account1); + token.transfer(account2, 10000000e18); + token.transfer(account3, 2000000e18); + token.transfer(account4, 3000000e18); + vm.stopPrank(); + + // Setup accounts 2,3 and 4 to vote + vm.startPrank(account2); + token.approve(address(wToken), 10000000e18); + wToken.depositFor(account2, 10000000e18); + wToken.delegate(account2); + vm.stopPrank(); + + vm.startPrank(account3); + token.approve(address(wToken), 2000000e18); + wToken.depositFor(account3, 2000000e18); + wToken.delegate(account3); + vm.stopPrank(); + + vm.startPrank(account4); + token.approve(address(wToken), 3000000e18); + wToken.depositFor(account4, 3000000e18); + wToken.delegate(account4); + vm.stopPrank(); + + callReceiverMock = new CallReceiverMock(); + } + + function testShouldReturnCorrectName() public { + assertEq(governor.name(), governorName); + } + + function testShouldReturnCorrectVotingDelay() public { + assertEq(governor.votingDelay(), 14400); + } + + function testShouldReturnCorrectVotingPeriod() public { + assertEq(governor.votingPeriod(), 50400); + } + + function testShouldReturnCorrectProposalThreshold() public { + assertEq(governor.proposalThreshold(), proposalThreshold); + } + + function testShouldReturnCorrectQuorum() public { + assertEq(governor.quorum(block.number), quorum); + } + + function testShouldReturnCorrectToken() public { + assertEq(address(governor.token()), address(votes)); + } + + function testShouldReturnCorrectTimelock() public { + assertEq(address(governor.timelock()), address(timelock)); + } + + function testShouldReturnCorrectSecurityCouncil() public { + assertEq(governor.securityCouncil(), securityCouncil); + } + + function testCanAssignSecurityCouncil() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.assignSecurityCouncil.selector, account1); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Assign new security council"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); + vm.warp(governor.proposalEta(proposalId) + 1); + + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Assign new security council")); + + address newSecurityCouncil = governor.securityCouncil(); + assertEq(newSecurityCouncil, account1); + } + + function testCannotAssignSecurityCouncilOutsideOfGovernance() public { + vm.expectRevert("Governor: onlyGovernance"); + governor.assignSecurityCouncil(account1); + } + + function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + IGovernor.ProposalState state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + // Cancel the proposal + vm.warp(governor.proposalEta(proposalId)); + + vm.prank(securityCouncil); + governor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.stopPrank(); + + state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); + + assertEq(governor.securityCouncil(), address(0)); + } + + function testCanUpdateVotingDelaySetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingDelay.selector, 3 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); + + uint256 votingDelay = governor.votingDelay(); + assertEq(votingDelay, 3 days); + } + + function testCanUpdateVotingPeriodSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setVotingPeriod.selector, 14 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting period to 14 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); + + uint256 votingPeriod = governor.votingPeriod(); + assertEq(votingPeriod, 14 days); + } + + function testCanUpdateProposalThresholdSetting() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.setProposalThreshold.selector, 2000000e18); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase proposal threshold to 2000000e18"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); + + uint256 proposalThreshold = governor.proposalThreshold(); + assertEq(proposalThreshold, 2000000e18); + } + + function testCanUpdateTimelockDelay() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(timelock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(timelock.updateDelay.selector, 7 days); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Increase timelock delay to 7 days"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 7 days"))); + vm.warp(governor.proposalEta(proposalId) + 1); + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 7 days")); + + uint256 timelockDelay = timelock.getMinDelay(); + assertEq(timelockDelay, 7 days); + } +} diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 2ffc2e04c9..965d26548b 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -18,81 +18,19 @@ */ pragma solidity ^0.8.17; -import "./BaseTest.t.sol"; -import "../src/ZeroExTimelock.sol"; -import "../src/ZeroExProtocolGovernor.sol"; -import "../src/ZRXWrappedToken.sol"; -import "@openzeppelin/token/ERC20/ERC20.sol"; -import "@openzeppelin/mocks/CallReceiverMock.sol"; - -contract ZeroExProtocolGovernorTest is BaseTest { - IERC20 public token; - ZRXWrappedToken internal wToken; - ZeroExVotes internal votes; - ZeroExTimelock internal timelock; - ZeroExProtocolGovernor internal governor; - CallReceiverMock internal callReceiverMock; +import "./ZeroExGovernorBaseTest.t.sol"; +contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { function setUp() public { - vm.startPrank(account1); - (token, wToken, votes, timelock, , governor, ) = setupGovernance(); - token.transfer(account2, 10000000e18); - token.transfer(account3, 2000000e18); - token.transfer(account4, 3000000e18); - vm.stopPrank(); - - // Setup accounts 2,3 and 4 to vote - vm.startPrank(account2); - token.approve(address(wToken), 10000000e18); - wToken.depositFor(account2, 10000000e18); - wToken.delegate(account2); - vm.stopPrank(); - - vm.startPrank(account3); - token.approve(address(wToken), 2000000e18); - wToken.depositFor(account3, 2000000e18); - wToken.delegate(account3); - vm.stopPrank(); - - vm.startPrank(account4); - token.approve(address(wToken), 3000000e18); - wToken.depositFor(account4, 3000000e18); - wToken.delegate(account4); - vm.stopPrank(); - - callReceiverMock = new CallReceiverMock(); - } - - function testShouldReturnCorrectName() public { - assertEq(governor.name(), "ZeroExProtocolGovernor"); - } + governorName = "ZeroExProtocolGovernor"; + proposalThreshold = 1000000e18; + quorum = 10000000e18; - function testShouldReturnCorrectVotingDelay() public { - assertEq(governor.votingDelay(), 14400); - } - - function testShouldReturnCorrectVotingPeriod() public { - assertEq(governor.votingPeriod(), 50400); - } - - function testShouldReturnCorrectProposalThreshold() public { - assertEq(governor.proposalThreshold(), 1000000e18); - } - - function testShouldReturnCorrectQuorum() public { - assertEq(governor.quorum(block.number), 10000000e18); - } + address governorAddress; + (token, wToken, votes, timelock, , governorAddress, ) = setupGovernance(); + governor = IZeroExGovernor(governorAddress); - function testShouldReturnCorrectToken() public { - assertEq(address(governor.token()), address(votes)); - } - - function testShouldReturnCorrectTimelock() public { - assertEq(address(governor.timelock()), address(timelock)); - } - - function testShouldReturnCorrectSecurityCouncil() public { - assertEq(governor.securityCouncil(), securityCouncil); + initialiseAccounts(); } function testShouldBeAbleToExecuteASuccessfulProposal() public { @@ -145,241 +83,4 @@ contract ZeroExProtocolGovernorTest is BaseTest { state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } - - function testCanUpdateVotingDelaySetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(governor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setVotingDelay.selector, 3 days); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); - vm.warp(governor.proposalEta(proposalId) + 1); - // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); - - uint256 votingDelay = governor.votingDelay(); - assertEq(votingDelay, 3 days); - } - - function testCanUpdateVotingPeriodSetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(governor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setVotingPeriod.selector, 14 days); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase voting period to 14 days"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); - vm.warp(governor.proposalEta(proposalId) + 1); - // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); - - uint256 votingPeriod = governor.votingPeriod(); - assertEq(votingPeriod, 14 days); - } - - function testCanUpdateProposalThresholdSetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(governor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.setProposalThreshold.selector, 2000000e18); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase proposal threshold to 2000000e18"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18"))); - vm.warp(governor.proposalEta(proposalId) + 1); - // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); - - uint256 proposalThreshold = governor.proposalThreshold(); - assertEq(proposalThreshold, 2000000e18); - } - - function testCanUpdateTimelockDelay() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(timelock); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(timelock.updateDelay.selector, 7 days); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Increase timelock delay to 7 days"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 7 days"))); - vm.warp(governor.proposalEta(proposalId) + 1); - // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 7 days")); - - uint256 timelockDelay = timelock.getMinDelay(); - assertEq(timelockDelay, 7 days); - } - - function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(callReceiverMock); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSignature("mockFunction()"); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - IGovernor.ProposalState state = governor.state(proposalId); - assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); - - // Cancel the proposal - vm.warp(governor.proposalEta(proposalId)); - - vm.prank(securityCouncil); - governor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); - vm.stopPrank(); - - state = governor.state(proposalId); - assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); - - assertEq(governor.securityCouncil(), address(0)); - } - - function testCanAssignSecurityCouncil() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(governor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(governor.assignSecurityCouncil.selector, account1); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = governor.propose(targets, values, calldatas, "Assign new security council"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(governor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - governor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(governor.proposalDeadline(proposalId) + 1); - - // Queue proposal - governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); - vm.warp(governor.proposalEta(proposalId) + 1); - - // Execute proposal - governor.execute(targets, values, calldatas, keccak256("Assign new security council")); - - address newSecurityCouncil = governor.securityCouncil(); - assertEq(newSecurityCouncil, account1); - } - - function testCannotAssignSecurityCouncilOutsideOfGovernance() public { - vm.expectRevert("Governor: onlyGovernance"); - governor.assignSecurityCouncil(account1); - } } diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 0b24e4cf70..04edd42323 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -18,79 +18,19 @@ */ pragma solidity ^0.8.17; -import "./BaseTest.t.sol"; -import "../src/ZeroExTimelock.sol"; -import "../src/ZeroExTreasuryGovernor.sol"; -import "../src/ZRXWrappedToken.sol"; -import "@openzeppelin/token/ERC20/ERC20.sol"; -import "@openzeppelin/mocks/CallReceiverMock.sol"; - -contract ZeroExTreasuryGovernorTest is BaseTest { - IERC20 public token; - ZRXWrappedToken internal wToken; - ZeroExVotes internal votes; - ZeroExTimelock internal timelock; - ZeroExTreasuryGovernor internal treasuryGovernor; - CallReceiverMock internal callReceiverMock; +import "./ZeroExGovernorBaseTest.t.sol"; +contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { function setUp() public { - vm.startPrank(account1); - (token, wToken, votes, , timelock, , treasuryGovernor) = setupGovernance(); - // quorum 5000000e18 - // proposal threshold 250000e18 = sqrt(6.25e46) 5e11 - token.transfer(account2, 10000000e18); - token.transfer(account3, 2000000e18); - token.transfer(account4, 3000000e18); - vm.stopPrank(); - - // Setup accounts 2,3 and 4 to vote - vm.startPrank(account2); - token.approve(address(wToken), 10000000e18); - wToken.depositFor(account2, 10000000e18); - wToken.delegate(account2); - vm.stopPrank(); - - vm.startPrank(account3); - token.approve(address(wToken), 2000000e18); - wToken.depositFor(account3, 2000000e18); - wToken.delegate(account3); - vm.stopPrank(); - - vm.startPrank(account4); - token.approve(address(wToken), 3000000e18); - wToken.depositFor(account4, 3000000e18); - wToken.delegate(account4); - vm.stopPrank(); - - callReceiverMock = new CallReceiverMock(); - } - - function testShouldReturnCorrectName() public { - assertEq(treasuryGovernor.name(), "ZeroExTreasuryGovernor"); - } - - function testShouldReturnCorrectVotingDelay() public { - assertEq(treasuryGovernor.votingDelay(), 14400); - } + governorName = "ZeroExTreasuryGovernor"; + proposalThreshold = 5e11; + quorum = 23e11; - function testShouldReturnCorrectVotingPeriod() public { - assertEq(treasuryGovernor.votingPeriod(), 50400); - } - - function testShouldReturnCorrectProposalThreshold() public { - assertEq(treasuryGovernor.proposalThreshold(), 5e11); - } - - function testShouldReturnCorrectQuorum() public { - assertEq(treasuryGovernor.quorum(block.number), 23e11); - } + address governorAddress; + (token, wToken, votes, , timelock, , governorAddress) = setupGovernance(); + governor = IZeroExGovernor(governorAddress); - function testShouldReturnCorrectToken() public { - assertEq(address(treasuryGovernor.token()), address(votes)); - } - - function testShouldReturnCorrectSecurityCouncil() public { - assertEq(treasuryGovernor.securityCouncil(), securityCouncil); + initialiseAccounts(); } function testShouldBeAbleToExecuteASuccessfulProposal() public { @@ -106,253 +46,41 @@ contract ZeroExTreasuryGovernorTest is BaseTest { vm.roll(2); vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Proposal description"); + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); vm.stopPrank(); // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); + vm.roll(governor.proposalSnapshot(proposalId) + 1); // Vote vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" + governor.castVote(proposalId, 1); // Vote "for" vm.stopPrank(); vm.prank(account3); - treasuryGovernor.castVote(proposalId, 0); // Vote "against" + governor.castVote(proposalId, 0); // Vote "against" vm.stopPrank(); vm.prank(account4); - treasuryGovernor.castVote(proposalId, 2); // Vote "abstain" + governor.castVote(proposalId, 2); // Vote "abstain" vm.stopPrank(); // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); + vm.roll(governor.proposalDeadline(proposalId) + 1); // Get vote results - (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = treasuryGovernor.proposalVotes(proposalId); + (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); assertEq(votesFor, Math.sqrt(10000000e18)); assertEq(votesAgainst, Math.sqrt(2000000e18)); assertEq(votesAbstain, Math.sqrt(3000000e18)); - IGovernor.ProposalState state = treasuryGovernor.state(proposalId); + IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); // Queue proposal - treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); - vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + vm.warp(governor.proposalEta(proposalId) + 1); - treasuryGovernor.execute(targets, values, calldatas, keccak256("Proposal description")); - state = treasuryGovernor.state(proposalId); + governor.execute(targets, values, calldatas, keccak256("Proposal description")); + state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } - - function testCanUpdateVotingDelaySetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(treasuryGovernor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setVotingDelay.selector, 3 days); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Increase voting delay to 3 days"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); - - // Queue proposal - treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days"))); - vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); - - // Execute proposal - treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); - - uint256 votingDelay = treasuryGovernor.votingDelay(); - assertEq(votingDelay, 3 days); - } - - function testCanUpdateVotingPeriodSetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(treasuryGovernor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setVotingPeriod.selector, 14 days); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Increase voting period to 14 days"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); - - // Queue proposal - treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days"))); - vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); - - // Execute proposal - treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); - - uint256 votingPeriod = treasuryGovernor.votingPeriod(); - assertEq(votingPeriod, 14 days); - } - - function testCanUpdateProposalThresholdSetting() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(treasuryGovernor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(treasuryGovernor.setProposalThreshold.selector, 2000000e18); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose( - targets, - values, - calldatas, - "Increase proposal threshold to 2000000e18" - ); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); - - // Queue proposal - treasuryGovernor.queue( - targets, - values, - calldatas, - keccak256(bytes("Increase proposal threshold to 2000000e18")) - ); - vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); - - // Execute proposal - treasuryGovernor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); - - uint256 proposalThreshold = treasuryGovernor.proposalThreshold(); - assertEq(proposalThreshold, 2000000e18); - } - - function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(callReceiverMock); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSignature("mockFunction()"); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Proposal description"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); - - IGovernor.ProposalState state = treasuryGovernor.state(proposalId); - assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); - - // Queue proposal - treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); - - // Cancel the proposal - vm.warp(treasuryGovernor.proposalEta(proposalId)); - vm.prank(securityCouncil); - treasuryGovernor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); - vm.stopPrank(); - - state = treasuryGovernor.state(proposalId); - assertEq(uint256(state), uint256(IGovernor.ProposalState.Canceled)); - - assertEq(treasuryGovernor.securityCouncil(), address(0)); - } - - function testCanAssignSecurityCouncil() public { - // Create a proposal - address[] memory targets = new address[](1); - targets[0] = address(treasuryGovernor); - - uint256[] memory values = new uint256[](1); - values[0] = 0; - - bytes[] memory calldatas = new bytes[](1); - calldatas[0] = abi.encodeWithSelector(treasuryGovernor.assignSecurityCouncil.selector, account1); - - vm.roll(2); - vm.startPrank(account2); - uint256 proposalId = treasuryGovernor.propose(targets, values, calldatas, "Assign new security council"); - vm.stopPrank(); - - // Fast forward to after vote start - vm.roll(treasuryGovernor.proposalSnapshot(proposalId) + 1); - - // Vote - vm.prank(account2); - treasuryGovernor.castVote(proposalId, 1); // Vote "for" - vm.stopPrank(); - - // Fast forward to vote end - vm.roll(treasuryGovernor.proposalDeadline(proposalId) + 1); - - // Queue proposal - treasuryGovernor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); - vm.warp(treasuryGovernor.proposalEta(proposalId) + 1); - - // Execute proposal - treasuryGovernor.execute(targets, values, calldatas, keccak256("Assign new security council")); - - address newSecurityCouncil = treasuryGovernor.securityCouncil(); - assertEq(newSecurityCouncil, account1); - } - - function testCannotAssignSecurityCouncilOutsideOfGovernance() public { - vm.expectRevert("Governor: onlyGovernance"); - treasuryGovernor.assignSecurityCouncil(account1); - } } From e1c141690943535a7358a27fe8eeaa84d4c363d3 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 22 Feb 2023 17:37:55 +0200 Subject: [PATCH 050/106] Add an empty test function to base test contract to remove it from coverage reports. Fudge but no other way to ignore it in report --- contracts/governance/test/BaseTest.t.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 3fd1a0afd0..fe77ec2946 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -106,4 +106,6 @@ contract BaseTest is Test { return (zrxToken, token, votes); } + + function test() public {} } From 905c99315fe56bf2087d239ed9df09d1d0242cc2 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 26 Feb 2023 17:10:52 +0200 Subject: [PATCH 051/106] Security council can rollback protocol upgrades --- contracts/governance/foundry.toml | 9 ++- .../governance/src/ZeroExProtocolGovernor.sol | 24 ++++++- contracts/governance/src/ZeroExTimelock.sol | 39 ++++++++++++ contracts/governance/test/ZeroExMock.sol | 11 ++++ .../test/ZeroExProtocolGovernor.t.sol | 63 ++++++++++++++++++- 5 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 contracts/governance/test/ZeroExMock.sol diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index b9e3277b8e..e6079ae05c 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -1,7 +1,10 @@ [profile.default] src = 'src' out = 'out' -libs = ['lib'] -fs_permissions = [{ access = "read", path = "./ZRXToken.json"}] -remappings = ['@openzeppelin/=./lib/openzeppelin-contracts/contracts/'] +libs = ['lib', "../utils/contracts/src/"] +fs_permissions = [{ access = "read", path = "./" }] +remappings = [ + '@openzeppelin/=./lib/openzeppelin-contracts/contracts/', + '@0x/contracts-utils/=../utils/', +] optimizer_runs = 20_000 diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index ae8b192340..4077a8a66c 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -18,6 +18,7 @@ */ pragma solidity ^0.8.17; +import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; @@ -35,13 +36,13 @@ contract ZeroExProtocolGovernor is constructor( IVotes _token, - TimelockController _timelock, + ZeroExTimelock _timelock, address _securityCouncil ) Governor("ZeroExProtocolGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 1000000e18) GovernorVotes(_token) - GovernorTimelockControl(_timelock) + GovernorTimelockControl(TimelockController(payable(_timelock))) { securityCouncil = _securityCouncil; } @@ -90,6 +91,25 @@ contract ZeroExProtocolGovernor is securityCouncil = address(0); } + // Like the GovernorTimelockControl.queue function but without the proposal checks, + // (as there's effectively no proposal). + // And also using a delay of 0 as opposed to the minimum delay of the timelock + function executeRollback( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public { + require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); + + // Execute the batch of rollbacks via the timelock controller + ZeroExTimelock timelockController = ZeroExTimelock(payable(timelock())); + timelockController.executeRollbackBatch(targets, values, calldatas, 0, descriptionHash); + + // Eject security council + securityCouncil = address(0); + } + function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { securityCouncil = _securityCouncil; } diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index 724e21a6d2..c7d83773cb 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -18,10 +18,13 @@ */ pragma solidity ^0.8.17; +import "@0x/contracts-utils/contracts/src/v08/LibBytesV08.sol"; import "@openzeppelin/governance/TimelockController.sol"; /// @custom:security-contact security@0xproject.com contract ZeroExTimelock is TimelockController { + using LibBytesV08 for bytes; + // minDelay is how long you have to wait before executing // proposers is the list of addresses that can propose // executors is the list of addresses that can execute @@ -31,4 +34,40 @@ contract ZeroExTimelock is TimelockController { address[] memory executors, address admin ) TimelockController(minDelay, proposers, executors, admin) {} + + /** + * @dev Execute a batch of rollback transactions. Similar to TimelockController.executeBatch function but without + * the timelock checks. + * Emits one {CallExecuted} event per transaction in the batch. + * + * Requirements: + * + * - the caller must have the 'executor' role. + */ + function executeRollbackBatch( + address[] calldata targets, + uint256[] calldata values, + bytes[] calldata payloads, + bytes32 predecessor, + bytes32 salt + ) public payable onlyRoleOrOpenRole(EXECUTOR_ROLE) { + require(targets.length == values.length, "TimelockController: length mismatch"); + require(targets.length == payloads.length, "TimelockController: length mismatch"); + + bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); + + for (uint256 i = 0; i < targets.length; ++i) { + address target = targets[i]; + uint256 value = values[i]; + bytes calldata payload = payloads[i]; + // Check this is a rollback transaction + // function signature for rollback(bytes4,address) + // = bytes4(keccak256("rollback(bytes4,address)")) + // = 0x9db64a40 + require(payload.readBytes4(0) == 0x9db64a40, "TimelockController: Not a rollback call"); + + _execute(target, value, payload); + emit CallExecuted(id, i, target, value, payload); + } + } } diff --git a/contracts/governance/test/ZeroExMock.sol b/contracts/governance/test/ZeroExMock.sol new file mode 100644 index 0000000000..f28c680935 --- /dev/null +++ b/contracts/governance/test/ZeroExMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +contract ZeroExMock { + mapping(bytes4 => address) public implementations; + + function rollback(bytes4 selector, address targetImpl) public { + implementations[selector] = targetImpl; + } +} diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 965d26548b..91f377875e 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -19,8 +19,14 @@ pragma solidity ^0.8.17; import "./ZeroExGovernorBaseTest.t.sol"; +import "./ZeroExMock.sol"; +import "../src/ZeroExProtocolGovernor.sol"; contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { + ZeroExProtocolGovernor internal protocolGovernor; + ZeroExMock internal zeroExMock; + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); + function setUp() public { governorName = "ZeroExProtocolGovernor"; proposalThreshold = 1000000e18; @@ -29,7 +35,8 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { address governorAddress; (token, wToken, votes, timelock, , governorAddress, ) = setupGovernance(); governor = IZeroExGovernor(governorAddress); - + protocolGovernor = ZeroExProtocolGovernor(payable(governorAddress)); + zeroExMock = new ZeroExMock(); initialiseAccounts(); } @@ -83,4 +90,58 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed)); } + + function testSecurityCouncilShouldBeAbleToExecuteRollback() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(zeroExMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + bytes4 testFunctionSig = 0xc853c969; + address testFunctionImpl = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + calldatas[0] = abi.encodeWithSignature("rollback(bytes4,address)", testFunctionSig, testFunctionImpl); + + // Security council adds the batch of rollbacks to the queue + vm.startPrank(securityCouncil); + + bytes32 proposalId = timelock.hashOperationBatch( + targets, + values, + calldatas, + 0, + keccak256(bytes("Emergency rollback")) + ); + vm.expectEmit(false, false, false, false); + emit CallExecuted(proposalId, 0, targets[0], values[0], calldatas[0]); + + protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); + } + + function testSecurityCouncilShouldNotBeAbleToExecuteArbitraryFunctions() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + // Security council tries to + vm.startPrank(securityCouncil); + + bytes32 proposalId = timelock.hashOperationBatch( + targets, + values, + calldatas, + 0, + keccak256(bytes("Proposal description")) + ); + vm.expectRevert("TimelockController: Not a rollback call"); + protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Proposal description"))); + } } From 1d9d3247ed15aece305d23c57a33ab62bf04f0d1 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 27 Feb 2023 09:33:12 +0200 Subject: [PATCH 052/106] Upgrade to solidity 0.8.19 --- contracts/governance/foundry.toml | 1 + contracts/governance/src/IZeroExVotes.sol | 2 +- contracts/governance/src/ZRXWrappedToken.sol | 2 +- contracts/governance/src/ZeroExProtocolGovernor.sol | 2 +- contracts/governance/src/ZeroExTimelock.sol | 2 +- contracts/governance/src/ZeroExTreasuryGovernor.sol | 2 +- contracts/governance/src/ZeroExVotes.sol | 2 +- contracts/governance/test/BaseTest.t.sol | 2 +- contracts/governance/test/IZeroExGovernor.sol | 2 +- contracts/governance/test/ZRXMock.sol | 2 +- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 2 +- contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 2 +- contracts/governance/test/ZeroExMock.sol | 2 +- contracts/governance/test/ZeroExProtocolGovernor.t.sol | 2 +- contracts/governance/test/ZeroExTreasuryGovernor.t.sol | 2 +- 15 files changed, 15 insertions(+), 14 deletions(-) diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index e6079ae05c..aaa6d5452a 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -7,4 +7,5 @@ remappings = [ '@openzeppelin/=./lib/openzeppelin-contracts/contracts/', '@0x/contracts-utils/=../utils/', ] +solc = '0.8.19' optimizer_runs = 20_000 diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 892103d0e4..e5c3676844 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; /// @custom:security-contact security@0xproject.com interface IZeroExVotes { diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 82d14575f0..5e41e68231 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/token/ERC20/extensions/draft-ERC20Permit.sol"; diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 4077a8a66c..0af50dcdce 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index c7d83773cb..a397ac2055 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@0x/contracts-utils/contracts/src/v08/LibBytesV08.sol"; import "@openzeppelin/governance/TimelockController.sol"; diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 4b8cacb873..c4aba05694 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index eb49ca6216..e1c1c256c3 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@openzeppelin/utils/math/SafeCast.sol"; import "@openzeppelin/utils/math/Math.sol"; diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index fe77ec2946..2621454bbc 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -17,7 +17,7 @@ */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "forge-std/console.sol"; diff --git a/contracts/governance/test/IZeroExGovernor.sol b/contracts/governance/test/IZeroExGovernor.sol index 8fb853876c..767e6bd191 100644 --- a/contracts/governance/test/IZeroExGovernor.sol +++ b/contracts/governance/test/IZeroExGovernor.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "@openzeppelin/governance/IGovernor.sol"; import "@openzeppelin/governance/extensions/IGovernorTimelock.sol"; diff --git a/contracts/governance/test/ZRXMock.sol b/contracts/governance/test/ZRXMock.sol index 9db1291172..9e9f71fc77 100644 --- a/contracts/governance/test/ZRXMock.sol +++ b/contracts/governance/test/ZRXMock.sol @@ -17,7 +17,7 @@ */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "forge-std/Test.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 41ed0ecd09..90000769bf 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "./BaseTest.t.sol"; import "../src/ZRXWrappedToken.sol"; diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 948aca3bc1..04f075eedd 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "./BaseTest.t.sol"; import "./IZeroExGovernor.sol"; diff --git a/contracts/governance/test/ZeroExMock.sol b/contracts/governance/test/ZeroExMock.sol index f28c680935..eb5ecf9297 100644 --- a/contracts/governance/test/ZeroExMock.sol +++ b/contracts/governance/test/ZeroExMock.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.19; contract ZeroExMock { mapping(bytes4 => address) public implementations; diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 91f377875e..2f3297d987 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "./ZeroExGovernorBaseTest.t.sol"; import "./ZeroExMock.sol"; diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 04edd42323..e4fb26be5d 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -16,7 +16,7 @@ limitations under the License. */ -pragma solidity ^0.8.17; +pragma solidity ^0.8.19; import "./ZeroExGovernorBaseTest.t.sol"; From 1484222b27f5002f0d37909d6b415395777676c4 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 27 Feb 2023 17:23:55 +0200 Subject: [PATCH 053/106] Move IZeroExGovernor to src --- contracts/governance/{test => src}/IZeroExGovernor.sol | 0 contracts/governance/src/ZeroExProtocolGovernor.sol | 1 + contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 2 +- contracts/governance/test/ZeroExProtocolGovernor.t.sol | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename contracts/governance/{test => src}/IZeroExGovernor.sol (100%) diff --git a/contracts/governance/test/IZeroExGovernor.sol b/contracts/governance/src/IZeroExGovernor.sol similarity index 100% rename from contracts/governance/test/IZeroExGovernor.sol rename to contracts/governance/src/IZeroExGovernor.sol diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 0af50dcdce..a41d744eca 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -18,6 +18,7 @@ */ pragma solidity ^0.8.19; +import "./IZeroExGovernor.sol"; import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 04f075eedd..067c30db42 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -19,7 +19,7 @@ pragma solidity ^0.8.19; import "./BaseTest.t.sol"; -import "./IZeroExGovernor.sol"; +import "../src/IZeroExGovernor.sol"; import "../src/ZeroExTimelock.sol"; import "../src/ZeroExProtocolGovernor.sol"; import "../src/ZRXWrappedToken.sol"; diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 2f3297d987..e7f929ec6e 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -114,7 +114,7 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { 0, keccak256(bytes("Emergency rollback")) ); - vm.expectEmit(false, false, false, false); + vm.expectEmit(true, true, true, true); emit CallExecuted(proposalId, 0, targets[0], values[0], calldatas[0]); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); From 14776b58d547cbd04235c5cb4aaeec08d9f477c3 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 27 Feb 2023 17:55:52 +0200 Subject: [PATCH 054/106] Abstract Security council interface into its own --- contracts/governance/src/IZeroExGovernor.sol | 14 ++------ .../governance/src/IZeroExSecurityCouncil.sol | 32 +++++++++++++++++++ .../governance/src/ZeroExProtocolGovernor.sol | 3 +- .../governance/src/ZeroExTreasuryGovernor.sol | 2 ++ 4 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 contracts/governance/src/IZeroExSecurityCouncil.sol diff --git a/contracts/governance/src/IZeroExGovernor.sol b/contracts/governance/src/IZeroExGovernor.sol index 767e6bd191..05968849bb 100644 --- a/contracts/governance/src/IZeroExGovernor.sol +++ b/contracts/governance/src/IZeroExGovernor.sol @@ -18,25 +18,15 @@ */ pragma solidity ^0.8.19; +import "./IZeroExSecurityCouncil.sol"; import "@openzeppelin/governance/IGovernor.sol"; import "@openzeppelin/governance/extensions/IGovernorTimelock.sol"; -abstract contract IZeroExGovernor is IGovernor, IGovernorTimelock { +abstract contract IZeroExGovernor is IZeroExSecurityCouncil, IGovernor, IGovernorTimelock { function token() public virtual returns (address); - function securityCouncil() public virtual returns (address); - function proposalThreshold() public view virtual returns (uint256); - function cancel( - address[] memory targets, - uint256[] memory values, - bytes[] memory calldatas, - bytes32 descriptionHash - ) public virtual; - - function assignSecurityCouncil(address _securityCouncil) public virtual; - function setVotingDelay(uint256 newVotingDelay) public virtual; function setVotingPeriod(uint256 newVotingPeriod) public virtual; diff --git a/contracts/governance/src/IZeroExSecurityCouncil.sol b/contracts/governance/src/IZeroExSecurityCouncil.sol new file mode 100644 index 0000000000..cef92848ea --- /dev/null +++ b/contracts/governance/src/IZeroExSecurityCouncil.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.19; + +interface IZeroExSecurityCouncil { + function securityCouncil() external returns (address); + + function cancel( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) external; + + function assignSecurityCouncil(address _securityCouncil) external; +} diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index a41d744eca..c3c0b6f4c7 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.19; -import "./IZeroExGovernor.sol"; +import "./IZeroExSecurityCouncil.sol"; import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; @@ -27,6 +27,7 @@ import "@openzeppelin/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; contract ZeroExProtocolGovernor is + IZeroExSecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index c4aba05694..4e0b74bce9 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -25,8 +25,10 @@ import "@openzeppelin/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; import "./IZeroExVotes.sol"; +import "./IZeroExSecurityCouncil.sol"; contract ZeroExTreasuryGovernor is + IZeroExSecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, From 58f8dd161b3346efdee15af2539395d9cc73178c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 27 Feb 2023 18:08:15 +0200 Subject: [PATCH 055/106] Emit events when assigning and ejecting the security council --- contracts/governance/src/IZeroExSecurityCouncil.sol | 8 ++++++-- contracts/governance/src/ZeroExProtocolGovernor.sol | 4 ++++ contracts/governance/src/ZeroExTreasuryGovernor.sol | 3 +++ contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 8 ++++++++ contracts/governance/test/ZeroExProtocolGovernor.t.sol | 2 ++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/contracts/governance/src/IZeroExSecurityCouncil.sol b/contracts/governance/src/IZeroExSecurityCouncil.sol index cef92848ea..0e21fef9ba 100644 --- a/contracts/governance/src/IZeroExSecurityCouncil.sol +++ b/contracts/governance/src/IZeroExSecurityCouncil.sol @@ -19,14 +19,18 @@ pragma solidity ^0.8.19; interface IZeroExSecurityCouncil { + event SecurityCouncilAssigned(address securityCouncil); + + event SecurityCouncilEjected(); + function securityCouncil() external returns (address); + function assignSecurityCouncil(address _securityCouncil) external; + function cancel( address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash ) external; - - function assignSecurityCouncil(address _securityCouncil) external; } diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index c3c0b6f4c7..a395f1c9bc 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -91,6 +91,7 @@ contract ZeroExProtocolGovernor is // Eject security council securityCouncil = address(0); + emit SecurityCouncilEjected(); } // Like the GovernorTimelockControl.queue function but without the proposal checks, @@ -110,10 +111,13 @@ contract ZeroExProtocolGovernor is // Eject security council securityCouncil = address(0); + emit SecurityCouncilEjected(); } function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { securityCouncil = _securityCouncil; + + emit SecurityCouncilAssigned(securityCouncil); } function _execute( diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 4e0b74bce9..450434471a 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -104,10 +104,13 @@ contract ZeroExTreasuryGovernor is // Eject security council securityCouncil = address(0); + emit SecurityCouncilEjected(); } function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { securityCouncil = _securityCouncil; + + emit SecurityCouncilAssigned(securityCouncil); } function _execute( diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 067c30db42..6322553eda 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -38,6 +38,9 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { uint256 internal proposalThreshold; uint256 internal quorum; + event SecurityCouncilAssigned(address securityCouncil); + event SecurityCouncilEjected(); + function initialiseAccounts() public { vm.startPrank(account1); token.transfer(account2, 10000000e18); @@ -131,6 +134,8 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { vm.warp(governor.proposalEta(proposalId) + 1); // Execute proposal + vm.expectEmit(true, false, false, false); + emit SecurityCouncilAssigned(account1); governor.execute(targets, values, calldatas, keccak256("Assign new security council")); address newSecurityCouncil = governor.securityCouncil(); @@ -179,6 +184,9 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { vm.warp(governor.proposalEta(proposalId)); vm.prank(securityCouncil); + + vm.expectEmit(true, false, false, false); + emit SecurityCouncilEjected(); governor.cancel(targets, values, calldatas, keccak256(bytes("Proposal description"))); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index e7f929ec6e..4ceb2f728b 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -117,6 +117,8 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { vm.expectEmit(true, true, true, true); emit CallExecuted(proposalId, 0, targets[0], values[0], calldatas[0]); + vm.expectEmit(true, false, false, false); + emit SecurityCouncilEjected(); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); } From 9bca5b971abacfd6c792bb8b75acb6908894eadd Mon Sep 17 00:00:00 2001 From: Elena Date: Mon, 27 Feb 2023 18:13:31 +0200 Subject: [PATCH 056/106] Use a cast to bytes4 instead of LibBytes Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com> --- contracts/governance/src/ZeroExTimelock.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index a397ac2055..71c9e17ffd 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -18,13 +18,10 @@ */ pragma solidity ^0.8.19; -import "@0x/contracts-utils/contracts/src/v08/LibBytesV08.sol"; import "@openzeppelin/governance/TimelockController.sol"; /// @custom:security-contact security@0xproject.com contract ZeroExTimelock is TimelockController { - using LibBytesV08 for bytes; - // minDelay is how long you have to wait before executing // proposers is the list of addresses that can propose // executors is the list of addresses that can execute @@ -64,7 +61,7 @@ contract ZeroExTimelock is TimelockController { // function signature for rollback(bytes4,address) // = bytes4(keccak256("rollback(bytes4,address)")) // = 0x9db64a40 - require(payload.readBytes4(0) == 0x9db64a40, "TimelockController: Not a rollback call"); + require(bytes4(payload) == bytes4(0x9db64a40), "TimelockController: Not a rollback call"); _execute(target, value, payload); emit CallExecuted(id, i, target, value, payload); From 5630eec32d9427a839f2b1d3c2f45f50af510fb6 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 2 Mar 2023 13:11:54 +0200 Subject: [PATCH 057/106] Writing total supply checkpoints and setup of quorum percentage of quadratic total supply for treasure governor --- contracts/governance/src/IZeroExVotes.sol | 10 ++- contracts/governance/src/ZRXWrappedToken.sol | 8 +- .../governance/src/ZeroExTreasuryGovernor.sol | 16 +++- contracts/governance/src/ZeroExVotes.sol | 64 ++++++++++++- .../governance/test/ZRXWrappedTokenTest.t.sol | 89 +++++++++++++++++++ .../test/ZeroExGovernorBaseTest.t.sol | 5 -- .../test/ZeroExProtocolGovernor.t.sol | 6 ++ .../test/ZeroExTreasuryGovernor.t.sol | 8 +- 8 files changed, 189 insertions(+), 17 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index e5c3676844..362c8eed3e 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -41,6 +41,12 @@ interface IZeroExVotes { */ event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + event TotalSupplyChanged(uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); + + event CheckpointAdded(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); + + event CheckpointUpdated(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); + /** * @dev Get the `pos`-th checkpoint for `account`. */ @@ -125,5 +131,7 @@ interface IZeroExVotes { uint256 amount ) external; - function writeCheckpointTotalSupply(uint256 totalSupply) external; + function writeCheckpointTotalSupplyMint(address account, uint256 amount, uint256 accountBalance) external; + + function writeCheckpointTotalSupplyBurn(address account, uint256 amount, uint256 accountBalance) external; } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 5e41e68231..11b3f8d26a 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -63,20 +63,20 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { return type(uint224).max; } - function _mint(address to, uint256 amount) internal override(ERC20) { - super._mint(to, amount); + function _mint(address account, uint256 amount) internal override(ERC20) { + super._mint(account, amount); // Snapshots the totalSupply after it has been increased. require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - zeroExVotes.writeCheckpointTotalSupply(totalSupply()); + zeroExVotes.writeCheckpointTotalSupplyMint(account, amount, balanceOf(account)); } function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); // Snapshots the totalSupply after it has been decreased. - zeroExVotes.writeCheckpointTotalSupply(totalSupply()); + zeroExVotes.writeCheckpointTotalSupplyBurn(account, amount, balanceOf(account)); } /** diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 450434471a..262bc1360a 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -22,6 +22,7 @@ import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; import "@openzeppelin/governance/extensions/GovernorCountingSimple.sol"; import "@openzeppelin/governance/extensions/GovernorVotes.sol"; +import "@openzeppelin/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; import "./IZeroExVotes.sol"; @@ -33,6 +34,7 @@ contract ZeroExTreasuryGovernor is GovernorSettings, GovernorCountingSimple, GovernorVotes, + GovernorVotesQuorumFraction, GovernorTimelockControl { address public securityCouncil; @@ -45,13 +47,23 @@ contract ZeroExTreasuryGovernor is Governor("ZeroExTreasuryGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 5e11) GovernorVotes(votes) + GovernorVotesQuorumFraction(10) GovernorTimelockControl(_timelock) { securityCouncil = _securityCouncil; } - function quorum(uint256 blockNumber) public pure override returns (uint256) { - return 23e11; + /** + * @dev Returns the "quadratic" quorum for a block number, in terms of number of votes: + * `quadratic total supply * numerator / denominator` + */ + function quorum( + uint256 blockNumber + ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { + IZeroExVotes votes = IZeroExVotes(address(token)); + uint256 quorum = (votes.getPastQuadraticTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / + quorumDenominator(); + return quorum; } // The following functions are overrides required by Solidity. diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index e1c1c256c3..913e2b03bf 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -231,10 +231,62 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupply(uint256 totalSupply) public override onlyToken { - // Currently we don't keep track of total supply checkpoints as all governance settings are fixed numbers - // i.e. governance quorum is not a percentage of total - // _writeCheckpoint(_totalSupplyCheckpoints, totalSupply, Math.sqrt(totalSupply)); + function writeCheckpointTotalSupplyMint( + address account, + uint256 amount, + uint256 accountBalance + ) public override onlyToken { + uint256 pos = _totalSupplyCheckpoints.length; + Checkpoint memory oldCkptTotalSuply = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_totalSupplyCheckpoints, pos - 1); + + // Remove the account sqrt balance from total quadratic supply. + // `accountBalance` is value _after_ minting + if (pos > 0) oldCkptTotalSuply.quadraticVotes -= SafeCast.toUint48(Math.sqrt(accountBalance - amount)); + + uint256 newLinearBalance = oldCkptTotalSuply.votes + amount; + uint256 newQuadraticBalance = oldCkptTotalSuply.quadraticVotes + Math.sqrt(accountBalance); + + _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); + + emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); + // emit DelegateVotesChanged(dstDelegatee, oldCkptTotalSuply.votes, newLinearBalance); + + // emit DelegateQuadraticVotesChanged( + // dstDelegatee, + // oldCkptTotalSuply.quadraticVotes, + // newQuadraticBalance + // ); + } + + function writeCheckpointTotalSupplyBurn( + address account, + uint256 amount, + uint256 accountBalance + ) public override onlyToken { + uint256 pos = _totalSupplyCheckpoints.length; + Checkpoint memory oldCkptTotalSuply = pos == 0 + ? Checkpoint(0, 0, 0) + : _unsafeAccess(_totalSupplyCheckpoints, pos - 1); + + // Remove the account sqrt balance from total quadratic supply. + // `accountBalance` is value _after_ burning + if (pos > 0) oldCkptTotalSuply.quadraticVotes -= SafeCast.toUint48(Math.sqrt(accountBalance + amount)); + + uint256 newLinearBalance = oldCkptTotalSuply.votes - amount; + uint256 newQuadraticBalance = oldCkptTotalSuply.quadraticVotes + Math.sqrt(accountBalance); + + _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); + + emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); + // emit DelegateVotesChanged(dstDelegatee, oldCkptTotalSuply.votes, newLinearBalance); + + // emit DelegateQuadraticVotesChanged( + // dstDelegatee, + // oldCkptTotalSuply.quadraticVotes, + // newQuadraticBalance + // ); } /** @@ -300,6 +352,8 @@ contract ZeroExVotes is IZeroExVotes { Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); chpt.votes = SafeCast.toUint96(voteWeight); chpt.quadraticVotes = SafeCast.toUint48(quadraticVoteWeight); + + emit CheckpointUpdated(chpt.fromBlock, chpt.votes, chpt.quadraticVotes); } else { ckpts.push( Checkpoint({ @@ -308,6 +362,8 @@ contract ZeroExVotes is IZeroExVotes { quadraticVotes: SafeCast.toUint48(quadraticVoteWeight) }) ); + + emit CheckpointAdded(ckpts[pos].fromBlock, ckpts[pos].votes, ckpts[pos].quadraticVotes); } } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 90000769bf..cfe5012df8 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -139,6 +139,95 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votes.getPastQuadraticTotalSupply(0), 0); } + function testWhenMintingFirstTimeForAccountTotalSupplyCheckpointsAreCorrect() public { + vm.startPrank(account2); + + // Approve the wrapped token and deposit 1e18 ZRX + token.approve(address(wToken), 1e18); + vm.roll(2); + wToken.depositFor(account2, 1e18); + vm.roll(3); + + // Check the totals are correct + uint256 totalSupplyVotes = votes.getPastTotalSupply(2); + uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); + assertEq(totalSupplyVotes, 1e18); + assertEq(totalSupplyQuadraticVotes, Math.sqrt(1e18)); + } + + function testWhenMintingForAccountWithExistingBalanceTotalSupplyCheckpointsAreCorrect() public { + vm.startPrank(account2); + + // Approve the wrapped token and deposit 1e18 ZRX + token.approve(address(wToken), 5e18); + wToken.depositFor(account2, 1e18); + + vm.roll(2); + // Depost 3e18 more for the same account + wToken.depositFor(account2, 3e18); + vm.roll(3); + + // Check the totals are correct + uint256 totalSupplyVotes = votes.getPastTotalSupply(2); + uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); + assertEq(totalSupplyVotes, 4e18); + assertEq(totalSupplyQuadraticVotes, Math.sqrt(4e18)); + } + + function testWhenMintingForMultipleAccountsTotalSupplyCheckpointsAreCorrect() public { + // Deposit 1e18 ZRX by account2 + vm.startPrank(account2); + token.approve(address(wToken), 5e18); + wToken.depositFor(account2, 1e18); + vm.stopPrank(); + + // Deposit 2e18 ZRX by account3 + vm.startPrank(account3); + token.approve(address(wToken), 2e18); + wToken.depositFor(account3, 2e18); + vm.stopPrank(); + + // Deposit 4e18 ZRX by account2 + vm.startPrank(account2); + vm.roll(2); + wToken.depositFor(account2, 4e18); + vm.stopPrank(); + vm.roll(3); + + // Check the totals are correct + uint256 totalSupplyVotes = votes.getPastTotalSupply(2); + uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); + assertEq(totalSupplyVotes, 7e18); + assertEq(totalSupplyQuadraticVotes, Math.sqrt(5e18) + Math.sqrt(2e18)); + } + + function testWhenBurningForMultipleAccountsTotalSupplyCheckpointsAreCorrect() public { + // Deposit 5e18 ZRX by account2 + vm.startPrank(account2); + token.approve(address(wToken), 5e18); + wToken.depositFor(account2, 5e18); + vm.stopPrank(); + + // Deposit 2e18 ZRX by account3 + vm.startPrank(account3); + token.approve(address(wToken), 2e18); + wToken.depositFor(account3, 2e18); + vm.stopPrank(); + + // Burn 4e18 ZRX by account2 + vm.startPrank(account2); + vm.roll(2); + wToken.withdrawTo(account2, 4e18); + vm.stopPrank(); + vm.roll(3); + + // Check the totals are correct + uint256 totalSupplyVotes = votes.getPastTotalSupply(2); + uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); + assertEq(totalSupplyVotes, 3e18); + assertEq(totalSupplyQuadraticVotes, Math.sqrt(1e18) + Math.sqrt(2e18)); + } + function testShouldBeAbleToTransferCorrectly() public { assertEq(wToken.balanceOf(account4), 0); diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 6322553eda..bd0e731654 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -36,7 +36,6 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { string internal governorName; uint256 internal proposalThreshold; - uint256 internal quorum; event SecurityCouncilAssigned(address securityCouncil); event SecurityCouncilEjected(); @@ -86,10 +85,6 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { assertEq(governor.proposalThreshold(), proposalThreshold); } - function testShouldReturnCorrectQuorum() public { - assertEq(governor.quorum(block.number), quorum); - } - function testShouldReturnCorrectToken() public { assertEq(address(governor.token()), address(votes)); } diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 4ceb2f728b..d4bb4973dc 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -25,6 +25,8 @@ import "../src/ZeroExProtocolGovernor.sol"; contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { ZeroExProtocolGovernor internal protocolGovernor; ZeroExMock internal zeroExMock; + uint256 internal quorum; + event CallExecuted(bytes32 indexed id, uint256 indexed index, address target, uint256 value, bytes data); function setUp() public { @@ -40,6 +42,10 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { initialiseAccounts(); } + function testShouldReturnCorrectQuorum() public { + assertEq(governor.quorum(block.number), quorum); + } + function testShouldBeAbleToExecuteASuccessfulProposal() public { // Create a proposal address[] memory targets = new address[](1); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index e4fb26be5d..8bf043dad4 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -24,7 +24,6 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { function setUp() public { governorName = "ZeroExTreasuryGovernor"; proposalThreshold = 5e11; - quorum = 23e11; address governorAddress; (token, wToken, votes, , timelock, , governorAddress) = setupGovernance(); @@ -33,6 +32,13 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { initialiseAccounts(); } + function testShouldReturnCorrectQuorum() public { + vm.roll(3); + uint256 totalSupplyQuadraticVotes = Math.sqrt(10000000e18) + Math.sqrt(2000000e18) + Math.sqrt(3000000e18); + uint256 quorum = (totalSupplyQuadraticVotes * 10) / 100; + assertEq(governor.quorum(2), quorum); + } + function testShouldBeAbleToExecuteASuccessfulProposal() public { // Create a proposal address[] memory targets = new address[](1); From 2867181ac7ff265dcf38f7595c1bd0cf2f58f802 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 2 Mar 2023 15:34:13 +0200 Subject: [PATCH 058/106] Add test for transferring tokens when delegating --- contracts/governance/src/ZeroExVotes.sol | 14 ----------- .../governance/test/ZRXWrappedTokenTest.t.sol | 23 ++++++++++++++++++- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 913e2b03bf..be00245482 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -251,13 +251,6 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); - // emit DelegateVotesChanged(dstDelegatee, oldCkptTotalSuply.votes, newLinearBalance); - - // emit DelegateQuadraticVotesChanged( - // dstDelegatee, - // oldCkptTotalSuply.quadraticVotes, - // newQuadraticBalance - // ); } function writeCheckpointTotalSupplyBurn( @@ -280,13 +273,6 @@ contract ZeroExVotes is IZeroExVotes { _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); - // emit DelegateVotesChanged(dstDelegatee, oldCkptTotalSuply.votes, newLinearBalance); - - // emit DelegateQuadraticVotesChanged( - // dstDelegatee, - // oldCkptTotalSuply.quadraticVotes, - // newQuadraticBalance - // ); } /** diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index cfe5012df8..d30e89cf60 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -269,7 +269,7 @@ contract ZRXWrappedTokenTest is BaseTest { function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { // Check voting power initially is 0 uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); assertEq(votingPowerAccount3, 0); assertEq(votingQuadraticPowerAccount3, 0); @@ -415,4 +415,25 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votingPowerAccount4, 0); assertEq(votingQuadraticPowerAccount4, 0); } + + function testShouldTransferVotingPowerWhenTransferringTokens() public { + // Account 2 wraps ZRX and delegates voting power to itself + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + wToken.delegate(account2); + + wToken.transfer(account3, 3e18); + + uint256 votingPowerAccount2 = votes.getVotes(account2); + uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 7e18); + assertEq(votingQuadraticPowerAccount2, Math.sqrt(7e18)); + + // Since account3 is not delegating to anyone, they should have no voting power + uint256 votingPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 0); + assertEq(votingQuadraticPowerAccount3, 0); + } } From 43bc935ef60e77ddb5ec2918d1aa93ceb8175cf8 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 3 Mar 2023 14:53:40 +0200 Subject: [PATCH 059/106] Rename IZeroExSecurityCouncil to ISecurityCouncil --- .../src/{IZeroExSecurityCouncil.sol => ISecurityCouncil.sol} | 2 +- contracts/governance/src/IZeroExGovernor.sol | 4 ++-- contracts/governance/src/ZeroExProtocolGovernor.sol | 4 ++-- contracts/governance/src/ZeroExTreasuryGovernor.sol | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename contracts/governance/src/{IZeroExSecurityCouncil.sol => ISecurityCouncil.sol} (96%) diff --git a/contracts/governance/src/IZeroExSecurityCouncil.sol b/contracts/governance/src/ISecurityCouncil.sol similarity index 96% rename from contracts/governance/src/IZeroExSecurityCouncil.sol rename to contracts/governance/src/ISecurityCouncil.sol index 0e21fef9ba..8e10b06873 100644 --- a/contracts/governance/src/IZeroExSecurityCouncil.sol +++ b/contracts/governance/src/ISecurityCouncil.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.19; -interface IZeroExSecurityCouncil { +interface ISecurityCouncil { event SecurityCouncilAssigned(address securityCouncil); event SecurityCouncilEjected(); diff --git a/contracts/governance/src/IZeroExGovernor.sol b/contracts/governance/src/IZeroExGovernor.sol index 05968849bb..0e3110e855 100644 --- a/contracts/governance/src/IZeroExGovernor.sol +++ b/contracts/governance/src/IZeroExGovernor.sol @@ -18,11 +18,11 @@ */ pragma solidity ^0.8.19; -import "./IZeroExSecurityCouncil.sol"; +import "./ISecurityCouncil.sol"; import "@openzeppelin/governance/IGovernor.sol"; import "@openzeppelin/governance/extensions/IGovernorTimelock.sol"; -abstract contract IZeroExGovernor is IZeroExSecurityCouncil, IGovernor, IGovernorTimelock { +abstract contract IZeroExGovernor is ISecurityCouncil, IGovernor, IGovernorTimelock { function token() public virtual returns (address); function proposalThreshold() public view virtual returns (uint256); diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index a395f1c9bc..1a06da1dd7 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.19; -import "./IZeroExSecurityCouncil.sol"; +import "./ISecurityCouncil.sol"; import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; @@ -27,7 +27,7 @@ import "@openzeppelin/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; contract ZeroExProtocolGovernor is - IZeroExSecurityCouncil, + ISecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 262bc1360a..08fe39e2a4 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -26,10 +26,10 @@ import "@openzeppelin/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; import "./IZeroExVotes.sol"; -import "./IZeroExSecurityCouncil.sol"; +import "./ISecurityCouncil.sol"; contract ZeroExTreasuryGovernor is - IZeroExSecurityCouncil, + ISecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, From 42e1b13682b7bfcc28c915b2d3ea79e5732c473c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 3 Mar 2023 16:45:08 +0200 Subject: [PATCH 060/106] Add security council restrictions to governors --- contracts/governance/src/ISecurityCouncil.sol | 40 +++++++- .../governance/src/ZeroExProtocolGovernor.sol | 28 +++--- .../governance/src/ZeroExTreasuryGovernor.sol | 24 ++--- .../test/ZeroExGovernorBaseTest.t.sol | 96 +++++++++++++++++++ 4 files changed, 159 insertions(+), 29 deletions(-) diff --git a/contracts/governance/src/ISecurityCouncil.sol b/contracts/governance/src/ISecurityCouncil.sol index 8e10b06873..f1bcf45527 100644 --- a/contracts/governance/src/ISecurityCouncil.sol +++ b/contracts/governance/src/ISecurityCouncil.sol @@ -18,19 +18,51 @@ */ pragma solidity ^0.8.19; -interface ISecurityCouncil { +abstract contract ISecurityCouncil { + address public securityCouncil; + event SecurityCouncilAssigned(address securityCouncil); event SecurityCouncilEjected(); - function securityCouncil() external returns (address); + modifier onlySecurityCouncil() { + require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); + _; + } + + modifier securityCouncilAssigned(bytes[] memory payloads) { + if (securityCouncil == address(0) && !_payloadIsAssignSecurityCouncil(payloads)) { + revert("SecurityCouncil: security council not assigned and this is not an assignment call"); + } + _; + } + + function assignSecurityCouncil(address _securityCouncil) public virtual { + securityCouncil = _securityCouncil; - function assignSecurityCouncil(address _securityCouncil) external; + emit SecurityCouncilAssigned(securityCouncil); + } + + function ejectSecurityCouncil() internal { + securityCouncil = address(0); + emit SecurityCouncilEjected(); + } function cancel( address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) external; + ) public virtual; + + function _payloadIsAssignSecurityCouncil(bytes[] memory payloads) private pure returns (bool) { + require(payloads.length == 1, "SecurityCouncil: there should be exactly 1 transaction in proposal"); + bytes memory payload = payloads[0]; + // Check this is as assignSecurityCouncil(address) transaction + // function signature for assignSecurityCouncil(address) + // = bytes4(keccak256("assignSecurityCouncil(address)")) + // = 0x2761c3cd + if (bytes4(payload) == bytes4(0x2761c3cd)) return true; + else return false; + } } diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 1a06da1dd7..4da6360ff4 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -34,8 +34,6 @@ contract ZeroExProtocolGovernor is GovernorVotes, GovernorTimelockControl { - address public securityCouncil; - constructor( IVotes _token, ZeroExTimelock _timelock, @@ -72,7 +70,7 @@ contract ZeroExProtocolGovernor is uint256[] memory values, bytes[] memory calldatas, string memory description - ) public override(Governor, IGovernor) returns (uint256) { + ) public override(Governor, IGovernor) securityCouncilAssigned(calldatas) returns (uint256) { return super.propose(targets, values, calldatas, description); } @@ -85,13 +83,10 @@ contract ZeroExProtocolGovernor is uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) public { - require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); + ) public override onlySecurityCouncil { _cancel(targets, values, calldatas, descriptionHash); - // Eject security council - securityCouncil = address(0); - emit SecurityCouncilEjected(); + ejectSecurityCouncil(); } // Like the GovernorTimelockControl.queue function but without the proposal checks, @@ -109,15 +104,20 @@ contract ZeroExProtocolGovernor is ZeroExTimelock timelockController = ZeroExTimelock(payable(timelock())); timelockController.executeRollbackBatch(targets, values, calldatas, 0, descriptionHash); - // Eject security council - securityCouncil = address(0); - emit SecurityCouncilEjected(); + ejectSecurityCouncil(); } - function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { - securityCouncil = _securityCouncil; + function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance { + super.assignSecurityCouncil(_securityCouncil); + } - emit SecurityCouncilAssigned(securityCouncil); + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public override securityCouncilAssigned(calldatas) returns (uint256) { + return super.queue(targets, values, calldatas, descriptionHash); } function _execute( diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 08fe39e2a4..5bca08aade 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -37,8 +37,6 @@ contract ZeroExTreasuryGovernor is GovernorVotesQuorumFraction, GovernorTimelockControl { - address public securityCouncil; - constructor( IVotes votes, TimelockController _timelock, @@ -101,7 +99,7 @@ contract ZeroExTreasuryGovernor is uint256[] memory values, bytes[] memory calldatas, string memory description - ) public override(Governor, IGovernor) returns (uint256) { + ) public override(Governor, IGovernor) securityCouncilAssigned(calldatas) returns (uint256) { return super.propose(targets, values, calldatas, description); } @@ -110,19 +108,23 @@ contract ZeroExTreasuryGovernor is uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash - ) public { - require(msg.sender == securityCouncil, "ZeroExTreasuryGovernor: Only security council allowed"); + ) public override onlySecurityCouncil { _cancel(targets, values, calldatas, descriptionHash); - // Eject security council - securityCouncil = address(0); - emit SecurityCouncilEjected(); + ejectSecurityCouncil(); } - function assignSecurityCouncil(address _securityCouncil) public onlyGovernance { - securityCouncil = _securityCouncil; + function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance { + super.assignSecurityCouncil(_securityCouncil); + } - emit SecurityCouncilAssigned(securityCouncil); + function queue( + address[] memory targets, + uint256[] memory values, + bytes[] memory calldatas, + bytes32 descriptionHash + ) public override securityCouncilAssigned(calldatas) returns (uint256) { + return super.queue(targets, values, calldatas, descriptionHash); } function _execute( diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index bd0e731654..aac32352e8 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -69,6 +69,43 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { callReceiverMock = new CallReceiverMock(); } + function setSecurityCouncil(address council) internal { + address[] memory targets = new address[](1); + targets[0] = address(governor); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSelector(governor.assignSecurityCouncil.selector, council); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Assign new security council"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Queue proposal + governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council"))); + vm.warp(governor.proposalEta(proposalId) + 1); + + // Execute proposal + governor.execute(targets, values, calldatas, keccak256("Assign new security council")); + + address newSecurityCouncil = governor.securityCouncil(); + assertEq(newSecurityCouncil, council); + } + function testShouldReturnCorrectName() public { assertEq(governor.name(), governorName); } @@ -191,6 +228,65 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { assertEq(governor.securityCouncil(), address(0)); } + function testWhenNoSecurityCouncilCannottSubmitProposals() public { + setSecurityCouncil(address(0)); + + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.expectRevert("SecurityCouncil: security council not assigned and this is not an assignment call"); + governor.propose(targets, values, calldatas, "Proposal description"); + } + + function testWhenNoSecurityCouncilCannotQueueSuccessfulProposals() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(callReceiverMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + calldatas[0] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + uint256 proposalId = governor.propose(targets, values, calldatas, "Proposal description"); + vm.stopPrank(); + + // Fast forward to after vote start + vm.roll(governor.proposalSnapshot(proposalId) + 1); + + // Vote + vm.prank(account2); + governor.castVote(proposalId, 1); // Vote "for" + vm.stopPrank(); + + // Fast forward to vote end + vm.roll(governor.proposalDeadline(proposalId) + 1); + + // Set security council to address(0) + setSecurityCouncil(address(0)); + + vm.expectRevert("SecurityCouncil: security council not assigned and this is not an assignment call"); + governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description"))); + + IGovernor.ProposalState state = governor.state(proposalId); + assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); + } + + function testWhenNoSecurityCouncilCanPassProposalToAssignSecurityCouncil() public { + setSecurityCouncil(address(0)); + + setSecurityCouncil(account1); + } + function testCanUpdateVotingDelaySetting() public { // Create a proposal address[] memory targets = new address[](1); From bcbf03990e88419566615b325287805a2022e126 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 4 Mar 2023 14:40:40 +0200 Subject: [PATCH 061/106] Remove obsolete overflow check --- contracts/governance/src/ZRXWrappedToken.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 11b3f8d26a..88e6375b87 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -46,7 +46,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { // The functions below are the required overrides from the base contracts function decimals() public view override(ERC20, ERC20Wrapper) returns (uint8) { - super.decimals(); + return 18; } function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) { @@ -67,8 +67,6 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { super._mint(account, amount); // Snapshots the totalSupply after it has been increased. - require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - zeroExVotes.writeCheckpointTotalSupplyMint(account, amount, balanceOf(account)); } From 986ebf3060929bf19b2453deb0086beddebdbb67 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 4 Mar 2023 14:37:18 +0200 Subject: [PATCH 062/106] Improve test coverage --- .../governance/test/ZRXWrappedTokenTest.t.sol | 3 +-- .../test/ZeroExGovernorBaseTest.t.sol | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index d30e89cf60..15b52a2898 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -45,8 +45,7 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(wZRXName, "Wrapped ZRX"); } - function shouldReturnCorrectNumberOfDecimals() public { - // TODO: decimals is set to 0 + function testshouldReturnCorrectNumberOfDecimals() public { uint8 wZRXDecimals = wToken.decimals(); assertEq(wZRXDecimals, 18); } diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index aac32352e8..b223a59357 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -287,6 +287,27 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { setSecurityCouncil(account1); } + function testCannotPassABadProposalToSetSecurityCouncil() public { + setSecurityCouncil(address(0)); + + address[] memory targets = new address[](2); + targets[0] = address(governor); + targets[1] = address(callReceiverMock); + + uint256[] memory values = new uint256[](2); + values[0] = 0; + values[1] = 0; + + bytes[] memory calldatas = new bytes[](2); + calldatas[0] = abi.encodeWithSelector(governor.assignSecurityCouncil.selector, account1); + calldatas[1] = abi.encodeWithSignature("mockFunction()"); + + vm.roll(2); + vm.startPrank(account2); + vm.expectRevert("SecurityCouncil: there should be exactly 1 transaction in proposal"); + governor.propose(targets, values, calldatas, "Assign new security council"); + } + function testCanUpdateVotingDelaySetting() public { // Create a proposal address[] memory targets = new address[](1); From 094de017f065676cb9b04cd9b5245015f5ce499d Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 10:19:18 +0200 Subject: [PATCH 063/106] Upgrade open-zeppelin contracts to 4.8.2 --- contracts/governance/lib/openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/lib/openzeppelin-contracts b/contracts/governance/lib/openzeppelin-contracts index 0457042d93..d00acef405 160000 --- a/contracts/governance/lib/openzeppelin-contracts +++ b/contracts/governance/lib/openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 0457042d93d9dfd760dbaa06a4d2f1216fdbe297 +Subproject commit d00acef4059807535af0bd0dd0ddf619747a044b From 9e873e1a94fb2403077b19fd1d416a67ef4e9872 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 11:26:52 +0200 Subject: [PATCH 064/106] Test delegation by signature --- contracts/governance/test/BaseTest.t.sol | 3 ++ .../governance/test/ZRXWrappedTokenTest.t.sol | 34 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 2621454bbc..c2821879e6 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -37,6 +37,9 @@ contract BaseTest is Test { address payable internal account4 = payable(vm.addr(4)); address payable internal securityCouncil = payable(vm.addr(5)); + bytes32 internal constant DELEGATION_TYPEHASH = + keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); + constructor() public { vm.deal(account1, 1e20); vm.deal(account2, 1e20); diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 15b52a2898..af94aaad16 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -45,7 +45,7 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(wZRXName, "Wrapped ZRX"); } - function testshouldReturnCorrectNumberOfDecimals() public { + function testShouldReturnCorrectNumberOfDecimals() public { uint8 wZRXDecimals = wToken.decimals(); assertEq(wZRXDecimals, 18); } @@ -286,6 +286,38 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); } + function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { + uint256 nonce = 0; + uint256 expiry = type(uint256).max; + uint256 privateKey = 2; + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + vm.stopPrank(); + + assertEq(wToken.delegates(account2), address(0)); + assertEq(votes.getVotes(account3), 0); + assertEq(votes.getQuadraticVotes(account3), 0); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + wToken.DOMAIN_SEPARATOR(), + keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) + ) + ) + ); + wToken.delegateBySig(account3, nonce, expiry, v, r, s); + + assertEq(wToken.delegates(account2), account3); + assertEq(votes.getVotes(account3), 10e18); + assertEq(votes.getQuadraticVotes(account3), Math.sqrt(10e18)); + } + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { // Check account4 voting power initially is 0 uint256 votingPowerAccount4 = votes.getVotes(account4); From 6a86ba199ae5849ab3531720984014661529a8ee Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 11:31:26 +0200 Subject: [PATCH 065/106] Test non security council requests to rollback protocol changes cannot be executed --- .../test/ZeroExProtocolGovernor.t.sol | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index d4bb4973dc..6da3046a5a 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -152,4 +152,32 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { vm.expectRevert("TimelockController: Not a rollback call"); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Proposal description"))); } + + function testRollbackShouldNotBeExecutableByNonSecurityCouncilAccounts() public { + // Create a proposal + address[] memory targets = new address[](1); + targets[0] = address(zeroExMock); + + uint256[] memory values = new uint256[](1); + values[0] = 0; + + bytes[] memory calldatas = new bytes[](1); + bytes4 testFunctionSig = 0xc853c969; + address testFunctionImpl = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + calldatas[0] = abi.encodeWithSignature("rollback(bytes4,address)", testFunctionSig, testFunctionImpl); + + // Security council adds the batch of rollbacks to the queue + vm.startPrank(account2); + + bytes32 proposalId = timelock.hashOperationBatch( + targets, + values, + calldatas, + 0, + keccak256(bytes("Emergency rollback")) + ); + + vm.expectRevert("ZeroExProtocolGovernor: Only security council allowed"); + protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); + } } From 4dbbc05fb1e5c522702adc473cb596540d949ac1 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 11:40:23 +0200 Subject: [PATCH 066/106] Better revert messages --- contracts/governance/src/ISecurityCouncil.sol | 4 ++-- contracts/governance/src/ZeroExProtocolGovernor.sol | 2 +- contracts/governance/src/ZeroExTimelock.sol | 6 +++--- contracts/governance/src/ZeroExVotes.sol | 4 ++-- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 2 +- contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 2 +- contracts/governance/test/ZeroExProtocolGovernor.t.sol | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/governance/src/ISecurityCouncil.sol b/contracts/governance/src/ISecurityCouncil.sol index f1bcf45527..bf52b79f70 100644 --- a/contracts/governance/src/ISecurityCouncil.sol +++ b/contracts/governance/src/ISecurityCouncil.sol @@ -26,7 +26,7 @@ abstract contract ISecurityCouncil { event SecurityCouncilEjected(); modifier onlySecurityCouncil() { - require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); + require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: only security council allowed"); _; } @@ -56,7 +56,7 @@ abstract contract ISecurityCouncil { ) public virtual; function _payloadIsAssignSecurityCouncil(bytes[] memory payloads) private pure returns (bool) { - require(payloads.length == 1, "SecurityCouncil: there should be exactly 1 transaction in proposal"); + require(payloads.length == 1, "SecurityCouncil: more than 1 transaction in proposal"); bytes memory payload = payloads[0]; // Check this is as assignSecurityCouncil(address) transaction // function signature for assignSecurityCouncil(address) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 4da6360ff4..d33141d764 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -98,7 +98,7 @@ contract ZeroExProtocolGovernor is bytes[] memory calldatas, bytes32 descriptionHash ) public { - require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: Only security council allowed"); + require(msg.sender == securityCouncil, "ZeroExProtocolGovernor: only security council allowed"); // Execute the batch of rollbacks via the timelock controller ZeroExTimelock timelockController = ZeroExTimelock(payable(timelock())); diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index 71c9e17ffd..ea670b366d 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -48,8 +48,8 @@ contract ZeroExTimelock is TimelockController { bytes32 predecessor, bytes32 salt ) public payable onlyRoleOrOpenRole(EXECUTOR_ROLE) { - require(targets.length == values.length, "TimelockController: length mismatch"); - require(targets.length == payloads.length, "TimelockController: length mismatch"); + require(targets.length == values.length, "ZeroExTimelock: length mismatch"); + require(targets.length == payloads.length, "ZeroExTimelock: length mismatch"); bytes32 id = hashOperationBatch(targets, values, payloads, predecessor, salt); @@ -61,7 +61,7 @@ contract ZeroExTimelock is TimelockController { // function signature for rollback(bytes4,address) // = bytes4(keccak256("rollback(bytes4,address)")) // = 0x9db64a40 - require(bytes4(payload) == bytes4(0x9db64a40), "TimelockController: Not a rollback call"); + require(bytes4(payload) == bytes4(0x9db64a40), "ZeroExTimelock: not rollback"); _execute(target, value, payload); emit CallExecuted(id, i, target, value, payload); diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index be00245482..3b7e9634bf 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -31,12 +31,12 @@ contract ZeroExVotes is IZeroExVotes { Checkpoint[] private _totalSupplyCheckpoints; modifier onlyToken() { - require(msg.sender == token, "Only the token is allowed to perform this operation"); + require(msg.sender == token, "ZeroExVotes: only token allowed"); _; } function initialize(address _token) public { - require(token == address(0), "ZeroExVotes: Already initialized"); + require(token == address(0), "ZeroExVotes: already initialized"); token = _token; } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index af94aaad16..3e31c984df 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -240,7 +240,7 @@ contract ZRXWrappedTokenTest is BaseTest { } function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { - vm.expectRevert("ZeroExVotes: Already initialized"); + vm.expectRevert("ZeroExVotes: already initialized"); votes.initialize(account2); } diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index b223a59357..ea8a27afc9 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -304,7 +304,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { vm.roll(2); vm.startPrank(account2); - vm.expectRevert("SecurityCouncil: there should be exactly 1 transaction in proposal"); + vm.expectRevert("SecurityCouncil: more than 1 transaction in proposal"); governor.propose(targets, values, calldatas, "Assign new security council"); } diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 6da3046a5a..94f0805c59 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -149,7 +149,7 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { 0, keccak256(bytes("Proposal description")) ); - vm.expectRevert("TimelockController: Not a rollback call"); + vm.expectRevert("ZeroExTimelock: not rollback"); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Proposal description"))); } @@ -177,7 +177,7 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { keccak256(bytes("Emergency rollback")) ); - vm.expectRevert("ZeroExProtocolGovernor: Only security council allowed"); + vm.expectRevert("ZeroExProtocolGovernor: only security council allowed"); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); } } From d361ba76eb4569baa142fdfd3c51d8fe8ff22638 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 11:53:23 +0200 Subject: [PATCH 067/106] Test correct interfaces are supported --- contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index ea8a27afc9..81c9aedc10 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -455,4 +455,10 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { uint256 timelockDelay = timelock.getMinDelay(); assertEq(timelockDelay, 7 days); } + + function testSupportsGovernanceInterfaces() public { + assertTrue(governor.supportsInterface(type(IGovernorTimelock).interfaceId)); + assertTrue(governor.supportsInterface(type(IGovernor).interfaceId)); + assertTrue(governor.supportsInterface(type(IERC1155Receiver).interfaceId)); + } } From 73070bdcfb2233c84aedfb48fc085a9cea4c0dbf Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 11:54:43 +0200 Subject: [PATCH 068/106] Remove obsoleted funciton --- contracts/governance/src/ZRXWrappedToken.sol | 7 ------- 1 file changed, 7 deletions(-) diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 88e6375b87..d9b1c58d3c 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -56,13 +56,6 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { zeroExVotes.movePartialVotingPower(delegates(from), delegates(to), balanceOf(from), balanceOf(to), amount); } - /** - * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). - */ - function _maxSupply() internal view virtual returns (uint224) { - return type(uint224).max; - } - function _mint(address account, uint256 amount) internal override(ERC20) { super._mint(account, amount); From 99a7b738a7e392256989c20e08f1da84112616d3 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sun, 5 Mar 2023 12:00:21 +0200 Subject: [PATCH 069/106] Further test delegation by signature scenario --- .../governance/test/ZRXWrappedTokenTest.t.sol | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 3e31c984df..f68d1bc06b 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -318,6 +318,33 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votes.getQuadraticVotes(account3), Math.sqrt(10e18)); } + function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { + uint256 nonce = 0; + uint256 expiry = block.timestamp; + uint256 privateKey = 2; + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + vm.stopPrank(); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + wToken.DOMAIN_SEPARATOR(), + keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) + ) + ) + ); + + vm.warp(block.timestamp + 1); + vm.expectRevert("ERC20Votes: signature expired"); + wToken.delegateBySig(account3, nonce, expiry, v, r, s); + } + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { // Check account4 voting power initially is 0 uint256 votingPowerAccount4 = votes.getVotes(account4); From 76e12ab1c6a991ca1898b349356de93268430a54 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 10:33:31 +0200 Subject: [PATCH 070/106] Split the delegation functionality tests --- contracts/governance/test/BaseTest.t.sol | 2 - .../governance/test/ZRXWrappedTokenTest.t.sol | 235 --------------- .../governance/test/ZeroExVotesTest.t.sol | 272 ++++++++++++++++++ 3 files changed, 272 insertions(+), 237 deletions(-) create mode 100644 contracts/governance/test/ZeroExVotesTest.t.sol diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index c2821879e6..b02b4d376b 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -109,6 +109,4 @@ contract BaseTest is Test { return (zrxToken, token, votes); } - - function test() public {} } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index f68d1bc06b..fd0bac3c63 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -239,241 +239,6 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(wToken.balanceOf(account4), 1e17); } - function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { - vm.expectRevert("ZeroExVotes: already initialized"); - votes.initialize(account2); - } - - function testShouldBeAbleToSelfDelegateVotingPower() public { - // Check voting power initially is 0 - uint256 votingPowerAccount2 = votes.getVotes(account2); - uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 0); - assertEq(votingQuadraticPowerAccount2, 0); - - // Wrap ZRX and delegate voting power to themselves - vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); - wToken.delegate(account2); - - // Check voting power is now = token balance - votingPowerAccount2 = votes.getVotes(account2); - assertEq(votingPowerAccount2, 100e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingQuadraticPowerAccount2, Math.sqrt(100e18)); - } - - function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { - // Check voting power initially is 0 - uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 0); - assertEq(votingQuadraticPowerAccount3, 0); - - // Account 2 wraps ZRX and delegates voting power to account3 - vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); - wToken.delegate(account3); - - // Check voting power is now = token balance - votingPowerAccount3 = votes.getVotes(account3); - assertEq(votingPowerAccount3, 10e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); - } - - function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { - uint256 nonce = 0; - uint256 expiry = type(uint256).max; - uint256 privateKey = 2; - - // Account 2 wraps ZRX and delegates voting power to account3 - vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); - vm.stopPrank(); - - assertEq(wToken.delegates(account2), address(0)); - assertEq(votes.getVotes(account3), 0); - assertEq(votes.getQuadraticVotes(account3), 0); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - wToken.DOMAIN_SEPARATOR(), - keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) - ) - ) - ); - wToken.delegateBySig(account3, nonce, expiry, v, r, s); - - assertEq(wToken.delegates(account2), account3); - assertEq(votes.getVotes(account3), 10e18); - assertEq(votes.getQuadraticVotes(account3), Math.sqrt(10e18)); - } - - function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { - uint256 nonce = 0; - uint256 expiry = block.timestamp; - uint256 privateKey = 2; - - // Account 2 wraps ZRX and delegates voting power to account3 - vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); - vm.stopPrank(); - - (uint8 v, bytes32 r, bytes32 s) = vm.sign( - privateKey, - keccak256( - abi.encodePacked( - "\x19\x01", - wToken.DOMAIN_SEPARATOR(), - keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) - ) - ) - ); - - vm.warp(block.timestamp + 1); - vm.expectRevert("ERC20Votes: signature expired"); - wToken.delegateBySig(account3, nonce, expiry, v, r, s); - } - - function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { - // Check account4 voting power initially is 0 - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); - - // Account 2 wraps ZRX and delegates voting power to account4 - vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); - wToken.delegate(account4); - vm.stopPrank(); - - // Account 3 also wraps ZRX and delegates voting power to account4 - vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 200e18); - wToken.delegate(account4); - vm.stopPrank(); - - // Check voting power is now = token balance - votingPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 300e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); - } - - function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnDifferentBlock() public { - // Check account4 voting power initially is 0 - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); - - // Account 2 wraps ZRX and delegates voting power to account4 - vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); - wToken.delegate(account4); - vm.stopPrank(); - - // Different block height - vm.roll(2); - // Account 3 also wraps ZRX and delegates voting power to account4 - vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 200e18); - wToken.delegate(account4); - vm.stopPrank(); - - // Check voting power is now = token balance - votingPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 300e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); - } - - function testComplexDelegationScenario() public { - // Account 2 wraps ZRX and delegates to itself - vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 90e18); - wToken.delegate(account2); - vm.stopPrank(); - - uint256 votingPowerAccount2 = votes.getVotes(account2); - uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 90e18); - assertEq(votingQuadraticPowerAccount2, Math.sqrt(90e18)); // 9486832980 - - // Account 3 wraps ZRX and delegates to account4 - vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 100e18); - wToken.delegate(account4); - vm.stopPrank(); - - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingPowerAccount4, 100e18); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18)); - - // Voting power distribution now is as follows - // account2 -> account2 90e18 | 9486832980 - // account3 -> account4 100e18 | 10e9 - - // Account 2 deposits the remaining 10e18 and delegates to account3 - vm.startPrank(account2); - wToken.depositFor(account2, 10e18); - wToken.delegate(account3); - vm.stopPrank(); - - uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 100e18); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18)); - - // Voting power distribution now is as follows - // account2 -> account3 100e18 | 10e18 - // account3 -> account4 100e18 | 10e9 - - // Account 3 delegates to itself - vm.startPrank(account3); - wToken.delegate(account3); - vm.stopPrank(); - - votingPowerAccount3 = votes.getVotes(account3); - votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 200e18); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18) + Math.sqrt(100e18)); - - // Voting power distribution now is as follows - // account2, account3 -> account3 100e18 | 20e18 - - // Check account2 and account4 no longer have voting power - votingPowerAccount2 = votes.getVotes(account2); - votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 0); - assertEq(votingQuadraticPowerAccount2, 0); - - votingPowerAccount4 = votes.getVotes(account4); - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); - } - function testShouldTransferVotingPowerWhenTransferringTokens() public { // Account 2 wraps ZRX and delegates voting power to itself vm.startPrank(account2); diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol new file mode 100644 index 0000000000..5ec4a1dfe7 --- /dev/null +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.19; + +import "./BaseTest.t.sol"; +import "../src/ZRXWrappedToken.sol"; +import "@openzeppelin/token/ERC20/ERC20.sol"; + +contract ZeroExVotesTest is BaseTest { + IERC20 private token; + ZRXWrappedToken private wToken; + ZeroExVotes private votes; + + function setUp() public { + (token, wToken, votes, , , , ) = setupGovernance(); + vm.startPrank(account1); + token.transfer(account2, 100e18); + token.transfer(account3, 200e18); + vm.stopPrank(); + } + + function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { + vm.expectRevert("ZeroExVotes: already initialized"); + votes.initialize(account2); + } + + function testShouldBeAbleToSelfDelegateVotingPower() public { + // Check voting power initially is 0 + uint256 votingPowerAccount2 = votes.getVotes(account2); + uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 0); + assertEq(votingQuadraticPowerAccount2, 0); + + // Wrap ZRX and delegate voting power to themselves + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account2); + + // Check voting power is now = token balance + votingPowerAccount2 = votes.getVotes(account2); + assertEq(votingPowerAccount2, 100e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingQuadraticPowerAccount2, Math.sqrt(100e18)); + } + + function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { + // Check voting power initially is 0 + uint256 votingPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 0); + assertEq(votingQuadraticPowerAccount3, 0); + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + wToken.delegate(account3); + + // Check voting power is now = token balance + votingPowerAccount3 = votes.getVotes(account3); + assertEq(votingPowerAccount3, 10e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); + } + + function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { + uint256 nonce = 0; + uint256 expiry = type(uint256).max; + uint256 privateKey = 2; + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + vm.stopPrank(); + + assertEq(wToken.delegates(account2), address(0)); + assertEq(votes.getVotes(account3), 0); + assertEq(votes.getQuadraticVotes(account3), 0); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + wToken.DOMAIN_SEPARATOR(), + keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) + ) + ) + ); + wToken.delegateBySig(account3, nonce, expiry, v, r, s); + + assertEq(wToken.delegates(account2), account3); + assertEq(votes.getVotes(account3), 10e18); + assertEq(votes.getQuadraticVotes(account3), Math.sqrt(10e18)); + } + + function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { + uint256 nonce = 0; + uint256 expiry = block.timestamp; + uint256 privateKey = 2; + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + vm.stopPrank(); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign( + privateKey, + keccak256( + abi.encodePacked( + "\x19\x01", + wToken.DOMAIN_SEPARATOR(), + keccak256(abi.encode(DELEGATION_TYPEHASH, account3, nonce, expiry)) + ) + ) + ); + + vm.warp(block.timestamp + 1); + vm.expectRevert("ERC20Votes: signature expired"); + wToken.delegateBySig(account3, nonce, expiry, v, r, s); + } + + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { + // Check account4 voting power initially is 0 + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + + // Account 2 wraps ZRX and delegates voting power to account4 + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Account 3 also wraps ZRX and delegates voting power to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Check voting power is now = token balance + votingPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 300e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + } + + function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnDifferentBlock() public { + // Check account4 voting power initially is 0 + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + + // Account 2 wraps ZRX and delegates voting power to account4 + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Different block height + vm.roll(2); + // Account 3 also wraps ZRX and delegates voting power to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account4); + vm.stopPrank(); + + // Check voting power is now = token balance + votingPowerAccount4 = votes.getVotes(account4); + assertEq(votingPowerAccount4, 300e18); + // Check quadratic voting power is now = sqrt(token balance) + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + } + + function testComplexDelegationScenario() public { + // Account 2 wraps ZRX and delegates to itself + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 90e18); + wToken.delegate(account2); + vm.stopPrank(); + + uint256 votingPowerAccount2 = votes.getVotes(account2); + uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 90e18); + assertEq(votingQuadraticPowerAccount2, Math.sqrt(90e18)); // 9486832980 + + // Account 3 wraps ZRX and delegates to account4 + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 100e18); + wToken.delegate(account4); + vm.stopPrank(); + + uint256 votingPowerAccount4 = votes.getVotes(account4); + uint256 votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingPowerAccount4, 100e18); + assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2 -> account2 90e18 | 9486832980 + // account3 -> account4 100e18 | 10e9 + + // Account 2 deposits the remaining 10e18 and delegates to account3 + vm.startPrank(account2); + wToken.depositFor(account2, 10e18); + wToken.delegate(account3); + vm.stopPrank(); + + uint256 votingPowerAccount3 = votes.getVotes(account3); + uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 100e18); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2 -> account3 100e18 | 10e18 + // account3 -> account4 100e18 | 10e9 + + // Account 3 delegates to itself + vm.startPrank(account3); + wToken.delegate(account3); + vm.stopPrank(); + + votingPowerAccount3 = votes.getVotes(account3); + votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); + assertEq(votingPowerAccount3, 200e18); + assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18) + Math.sqrt(100e18)); + + // Voting power distribution now is as follows + // account2, account3 -> account3 100e18 | 20e18 + + // Check account2 and account4 no longer have voting power + votingPowerAccount2 = votes.getVotes(account2); + votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); + assertEq(votingPowerAccount2, 0); + assertEq(votingQuadraticPowerAccount2, 0); + + votingPowerAccount4 = votes.getVotes(account4); + votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); + assertEq(votingPowerAccount4, 0); + assertEq(votingQuadraticPowerAccount4, 0); + } +} From 1d8e3ca44f5ecf9195d6fab5b1f761d1c92aca19 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 10:48:47 +0200 Subject: [PATCH 071/106] Add test for initialisation of voting contract --- contracts/governance/test/BaseTest.t.sol | 4 +++- contracts/governance/test/ZeroExVotesTest.t.sol | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index b02b4d376b..1f8697ed79 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -52,9 +52,9 @@ contract BaseTest is Test { internal returns (IERC20, ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExTimelock, address, address) { - vm.startPrank(account1); (IERC20 zrxToken, ZRXWrappedToken token, ZeroExVotes votes) = setupZRXWrappedToken(); + vm.startPrank(account1); address[] memory proposers = new address[](0); address[] memory executors = new address[](0); @@ -92,6 +92,7 @@ contract BaseTest is Test { } function setupZRXWrappedToken() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes) { + vm.startPrank(account1); bytes memory _bytecode = vm.getCode("./ZRXToken.json"); address _address; assembly { @@ -106,6 +107,7 @@ contract BaseTest is Test { ZRXWrappedToken token = new ZRXWrappedToken(zrxToken, IZeroExVotes(address(votesProxy))); votes.initialize(address(token)); + vm.stopPrank(); return (zrxToken, token, votes); } diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 5ec4a1dfe7..39cd7bb4d5 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -28,14 +28,23 @@ contract ZeroExVotesTest is BaseTest { ZeroExVotes private votes; function setUp() public { - (token, wToken, votes, , , , ) = setupGovernance(); + (token, wToken, votes) = setupZRXWrappedToken(); vm.startPrank(account1); token.transfer(account2, 100e18); token.transfer(account3, 200e18); vm.stopPrank(); } - function testShouldNotBeAbleToReinitialiseTheZeroExVotes() public { + function testShouldCorrectlyInitialiseToken() public { + assertEq(votes.token(), address(wToken)); + } + + function testShouldNotBeAbleToInitialiseWithZeroAddressToken() public { + ZeroExVotes votes = new ZeroExVotes(); + votes.initialize(address(0)); + } + + function testShouldNotBeAbleToReinitialise() public { vm.expectRevert("ZeroExVotes: already initialized"); votes.initialize(account2); } From d8a867f5b986eb2e3dab4d2198f7828cfba2d4aa Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 11:04:13 +0200 Subject: [PATCH 072/106] Add test for reading checkpoints --- contracts/governance/test/ZeroExVotesTest.t.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 39cd7bb4d5..31fd8b6063 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -49,6 +49,22 @@ contract ZeroExVotesTest is BaseTest { votes.initialize(account2); } + function testShouldBeAbleToReadCheckpoints() public { + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + vm.roll(2); + wToken.delegate(account3); + + assertEq(votes.numCheckpoints(account3), 1); + + IZeroExVotes.Checkpoint memory checkpoint = votes.checkpoints(account3, 0); + assertEq(checkpoint.fromBlock, 2); + assertEq(checkpoint.votes, 10e18); + assertEq(checkpoint.quadraticVotes, Math.sqrt(10e18)); + } + function testShouldBeAbleToSelfDelegateVotingPower() public { // Check voting power initially is 0 uint256 votingPowerAccount2 = votes.getVotes(account2); From da8537c9873063c801af712a04de68819bb41a45 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 14:33:24 +0200 Subject: [PATCH 073/106] Update code comments --- contracts/governance/src/ISecurityCouncil.sol | 13 +++++++++++++ contracts/governance/src/IZeroExVotes.sol | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/contracts/governance/src/ISecurityCouncil.sol b/contracts/governance/src/ISecurityCouncil.sol index bf52b79f70..13dd4d14cd 100644 --- a/contracts/governance/src/ISecurityCouncil.sol +++ b/contracts/governance/src/ISecurityCouncil.sol @@ -30,6 +30,9 @@ abstract contract ISecurityCouncil { _; } + /** + * @dev Checks that either a security council is assigned or the payloads array is a council assignment call. + */ modifier securityCouncilAssigned(bytes[] memory payloads) { if (securityCouncil == address(0) && !_payloadIsAssignSecurityCouncil(payloads)) { revert("SecurityCouncil: security council not assigned and this is not an assignment call"); @@ -37,17 +40,27 @@ abstract contract ISecurityCouncil { _; } + /** + * @dev Assigns new security council. + */ function assignSecurityCouncil(address _securityCouncil) public virtual { securityCouncil = _securityCouncil; emit SecurityCouncilAssigned(securityCouncil); } + /** + * @dev Ejects the current security council via setting the security council address to 0. + * Security council is ejected after they either cancel a proposal or execute a protocol rollback. + */ function ejectSecurityCouncil() internal { securityCouncil = address(0); emit SecurityCouncilEjected(); } + /** + * @dev Cancel existing proposal with the submitted `targets`, `values`, `calldatas` and `descriptionHash`. + */ function cancel( address[] memory targets, uint256[] memory values, diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 362c8eed3e..d2ecaf708c 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -41,10 +41,20 @@ interface IZeroExVotes { */ event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance); + /** + * @dev Emitted when the total supply of the token is changed due to minting and burning which results in + * the total supply checkpoint being writtenor updated. + */ event TotalSupplyChanged(uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); + /** + * @dev Emitted a new checkpoint is written. + */ event CheckpointAdded(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); + /** + * @dev Emitted when an existing checkpoint is updated. + */ event CheckpointUpdated(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); /** From 4283fbd65b642c083df2fa49e7836e74111fc1ef Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 14:44:56 +0200 Subject: [PATCH 074/106] Fix compilation warnings --- contracts/governance/src/IZeroExVotes.sol | 4 ++-- contracts/governance/src/ZRXWrappedToken.sol | 6 +++--- .../governance/src/ZeroExTreasuryGovernor.sol | 4 +--- contracts/governance/src/ZeroExVotes.sol | 12 ++---------- contracts/governance/test/BaseTest.t.sol | 2 +- .../test/ZeroExGovernorBaseTest.t.sol | 18 ++++++------------ .../test/ZeroExProtocolGovernor.t.sol | 19 ------------------- .../governance/test/ZeroExVotesTest.t.sol | 4 ++-- 8 files changed, 17 insertions(+), 52 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index d2ecaf708c..f794f9ef9a 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -141,7 +141,7 @@ interface IZeroExVotes { uint256 amount ) external; - function writeCheckpointTotalSupplyMint(address account, uint256 amount, uint256 accountBalance) external; + function writeCheckpointTotalSupplyMint(uint256 amount, uint256 accountBalance) external; - function writeCheckpointTotalSupplyBurn(address account, uint256 amount, uint256 accountBalance) external; + function writeCheckpointTotalSupplyBurn(uint256 amount, uint256 accountBalance) external; } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index d9b1c58d3c..9200468b1b 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -45,7 +45,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { // The functions below are the required overrides from the base contracts - function decimals() public view override(ERC20, ERC20Wrapper) returns (uint8) { + function decimals() public pure override(ERC20, ERC20Wrapper) returns (uint8) { return 18; } @@ -60,14 +60,14 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { super._mint(account, amount); // Snapshots the totalSupply after it has been increased. - zeroExVotes.writeCheckpointTotalSupplyMint(account, amount, balanceOf(account)); + zeroExVotes.writeCheckpointTotalSupplyMint(amount, balanceOf(account)); } function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); // Snapshots the totalSupply after it has been decreased. - zeroExVotes.writeCheckpointTotalSupplyBurn(account, amount, balanceOf(account)); + zeroExVotes.writeCheckpointTotalSupplyBurn(amount, balanceOf(account)); } /** diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 5bca08aade..2e46dd8c57 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -59,9 +59,7 @@ contract ZeroExTreasuryGovernor is uint256 blockNumber ) public view override(IGovernor, GovernorVotesQuorumFraction) returns (uint256) { IZeroExVotes votes = IZeroExVotes(address(token)); - uint256 quorum = (votes.getPastQuadraticTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / - quorumDenominator(); - return quorum; + return (votes.getPastQuadraticTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / quorumDenominator(); } // The following functions are overrides required by Solidity. diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 3b7e9634bf..3999621eee 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -231,11 +231,7 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyMint( - address account, - uint256 amount, - uint256 accountBalance - ) public override onlyToken { + function writeCheckpointTotalSupplyMint(uint256 amount, uint256 accountBalance) public override onlyToken { uint256 pos = _totalSupplyCheckpoints.length; Checkpoint memory oldCkptTotalSuply = pos == 0 ? Checkpoint(0, 0, 0) @@ -253,11 +249,7 @@ contract ZeroExVotes is IZeroExVotes { emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); } - function writeCheckpointTotalSupplyBurn( - address account, - uint256 amount, - uint256 accountBalance - ) public override onlyToken { + function writeCheckpointTotalSupplyBurn(uint256 amount, uint256 accountBalance) public override onlyToken { uint256 pos = _totalSupplyCheckpoints.length; Checkpoint memory oldCkptTotalSuply = pos == 0 ? Checkpoint(0, 0, 0) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 1f8697ed79..2b736dde80 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -40,7 +40,7 @@ contract BaseTest is Test { bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - constructor() public { + constructor() { vm.deal(account1, 1e20); vm.deal(account2, 1e20); vm.deal(account3, 1e20); diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 81c9aedc10..3698180cdd 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -102,8 +102,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // Execute proposal governor.execute(targets, values, calldatas, keccak256("Assign new security council")); - address newSecurityCouncil = governor.securityCouncil(); - assertEq(newSecurityCouncil, council); + assertEq(governor.securityCouncil(), council); } function testShouldReturnCorrectName() public { @@ -170,8 +169,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { emit SecurityCouncilAssigned(account1); governor.execute(targets, values, calldatas, keccak256("Assign new security council")); - address newSecurityCouncil = governor.securityCouncil(); - assertEq(newSecurityCouncil, account1); + assertEq(governor.securityCouncil(), account1); } function testCannotAssignSecurityCouncilOutsideOfGovernance() public { @@ -341,8 +339,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days")); - uint256 votingDelay = governor.votingDelay(); - assertEq(votingDelay, 3 days); + assertEq(governor.votingDelay(), 3 days); } function testCanUpdateVotingPeriodSetting() public { @@ -378,8 +375,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days")); - uint256 votingPeriod = governor.votingPeriod(); - assertEq(votingPeriod, 14 days); + assertEq(governor.votingPeriod(), 14 days); } function testCanUpdateProposalThresholdSetting() public { @@ -415,8 +411,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18")); - uint256 proposalThreshold = governor.proposalThreshold(); - assertEq(proposalThreshold, 2000000e18); + assertEq(governor.proposalThreshold(), 2000000e18); } function testCanUpdateTimelockDelay() public { @@ -452,8 +447,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // Execute proposal governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 7 days")); - uint256 timelockDelay = timelock.getMinDelay(); - assertEq(timelockDelay, 7 days); + assertEq(timelock.getMinDelay(), 7 days); } function testSupportsGovernanceInterfaces() public { diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 94f0805c59..6a863e6a4a 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -139,16 +139,7 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { bytes[] memory calldatas = new bytes[](1); calldatas[0] = abi.encodeWithSignature("mockFunction()"); - // Security council tries to vm.startPrank(securityCouncil); - - bytes32 proposalId = timelock.hashOperationBatch( - targets, - values, - calldatas, - 0, - keccak256(bytes("Proposal description")) - ); vm.expectRevert("ZeroExTimelock: not rollback"); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Proposal description"))); } @@ -166,17 +157,7 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { address testFunctionImpl = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; calldatas[0] = abi.encodeWithSignature("rollback(bytes4,address)", testFunctionSig, testFunctionImpl); - // Security council adds the batch of rollbacks to the queue vm.startPrank(account2); - - bytes32 proposalId = timelock.hashOperationBatch( - targets, - values, - calldatas, - 0, - keccak256(bytes("Emergency rollback")) - ); - vm.expectRevert("ZeroExProtocolGovernor: only security council allowed"); protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); } diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 31fd8b6063..09746582db 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -40,8 +40,8 @@ contract ZeroExVotesTest is BaseTest { } function testShouldNotBeAbleToInitialiseWithZeroAddressToken() public { - ZeroExVotes votes = new ZeroExVotes(); - votes.initialize(address(0)); + ZeroExVotes _votes = new ZeroExVotes(); + _votes.initialize(address(0)); } function testShouldNotBeAbleToReinitialise() public { From 2132b9ae5d249b826b0f690584d24da9e9c1081f Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 7 Mar 2023 16:23:06 +0200 Subject: [PATCH 075/106] Run smt checker --- contracts/governance/foundry.toml | 12 ++++++++++++ contracts/governance/package.json | 4 +++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index aaa6d5452a..50b69e7fe8 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -9,3 +9,15 @@ remappings = [ ] solc = '0.8.19' optimizer_runs = 20_000 + +[profile.smt.model_checker] +engine = 'chc' +timeout = 10_000 +targets = [ + 'assert', + 'constantCondition', + 'divByZero', + 'outOfBounds', + 'underflow' +] +contracts = { 'src/ZeroExProtocolGovernor.sol' = [ 'ZeroExProtocolGovernor' ] } diff --git a/contracts/governance/package.json b/contracts/governance/package.json index 4906739f40..ad4b5c78f6 100644 --- a/contracts/governance/package.json +++ b/contracts/governance/package.json @@ -8,7 +8,9 @@ "test": "test" }, "scripts": { - "test": "forge test" + "test": "forge test", + "build": "forge build", + "build:smt": "FOUNDRY_PROFILE=smt forge build" }, "repository": { "type": "git", From 0e84a1d29d9eafb7f5df9661c1794cc907162b35 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 8 Mar 2023 18:40:14 +0200 Subject: [PATCH 076/106] Add checkpoint tests --- .../governance/test/ZeroExVotesTest.t.sol | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 09746582db..d508fd334f 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -294,4 +294,71 @@ contract ZeroExVotesTest is BaseTest { assertEq(votingPowerAccount4, 0); assertEq(votingQuadraticPowerAccount4, 0); } + + function testCheckpointIsCorrectlyUpdatedOnTheSameBlock() public { + // Account 2 wraps ZRX and delegates 20e18 to itself + vm.startPrank(account2); + token.approve(address(wToken), 20e18); + wToken.depositFor(account2, 20e18); + wToken.delegate(account2); + vm.stopPrank(); + + uint256 numCheckpointsAccount2 = votes.numCheckpoints(account2); + assertEq(numCheckpointsAccount2, 1); + IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0); + assertEq(checkpoint1Account2.fromBlock, 1); + assertEq(checkpoint1Account2.votes, 20e18); + assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + + // Account 3 wraps ZRX and delegates 10e18 to account2 + vm.startPrank(account3); + token.approve(address(wToken), 10e18); + wToken.depositFor(account3, 10e18); + wToken.delegate(account2); + vm.stopPrank(); + + numCheckpointsAccount2 = votes.numCheckpoints(account2); + assertEq(numCheckpointsAccount2, 1); + checkpoint1Account2 = votes.checkpoints(account2, 0); + assertEq(checkpoint1Account2.fromBlock, 1); + assertEq(checkpoint1Account2.votes, 30e18); + assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18) + Math.sqrt(10e18)); + } + + function testCheckpointIsCorrectlyUpdatedOnDifferentBlocks() public { + // Account 2 wraps ZRX and delegates 20e18 to itself + vm.startPrank(account2); + token.approve(address(wToken), 20e18); + wToken.depositFor(account2, 20e18); + wToken.delegate(account2); + vm.stopPrank(); + + uint256 numCheckpointsAccount2 = votes.numCheckpoints(account2); + assertEq(numCheckpointsAccount2, 1); + IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0); + assertEq(checkpoint1Account2.fromBlock, 1); + assertEq(checkpoint1Account2.votes, 20e18); + assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + + vm.roll(2); + // Account 3 wraps ZRX and delegates 10e18 to account2 + vm.startPrank(account3); + token.approve(address(wToken), 10e18); + wToken.depositFor(account3, 10e18); + wToken.delegate(account2); + vm.stopPrank(); + + numCheckpointsAccount2 = votes.numCheckpoints(account2); + assertEq(numCheckpointsAccount2, 2); + IZeroExVotes.Checkpoint memory checkpoint2Account2 = votes.checkpoints(account2, 1); + assertEq(checkpoint2Account2.fromBlock, 2); + assertEq(checkpoint2Account2.votes, 30e18); + assertEq(checkpoint2Account2.quadraticVotes, Math.sqrt(20e18) + Math.sqrt(10e18)); + + // Check the old checkpoint hasn't changed + checkpoint1Account2 = votes.checkpoints(account2, 0); + assertEq(checkpoint1Account2.fromBlock, 1); + assertEq(checkpoint1Account2.votes, 20e18); + assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + } } From 7024a11da316c22addcea2036185fa4ed089ca38 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Thu, 9 Mar 2023 13:15:28 +0200 Subject: [PATCH 077/106] Rename parameter in moveEntireVotingPower to match the one in movePartialVotingPower --- contracts/governance/src/IZeroExVotes.sol | 5 +++-- contracts/governance/src/ZeroExVotes.sol | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index f794f9ef9a..01fa1a2712 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -117,10 +117,11 @@ interface IZeroExVotes { function getPastQuadraticTotalSupply(uint256 blockNumber) external view returns (uint256); /** - * @dev Moves the voting power for an account with balance `delegateBalance` from `srcDelegatee` to `dstDelegatee`. + * @dev Moves the voting power for an account with balance `srcDelegateBalance` from `srcDelegatee` to + * `dstDelegatee`. * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) */ - function moveEntireVotingPower(address srcDelegatee, address dstDelegatee, uint256 delegateBalance) external; + function moveEntireVotingPower(address srcDelegatee, address dstDelegatee, uint256 srcDelegateBalance) external; /** * @dev Moves the voting power corresponding to `amount` number of tokens from `srcDelegatee` to `dstDelegatee`. diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 3999621eee..5936d79260 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -120,17 +120,17 @@ contract ZeroExVotes is IZeroExVotes { function moveEntireVotingPower( address srcDelegatee, address dstDelegatee, - uint256 delegateBalance + uint256 srcDelegateBalance ) public override onlyToken { - if (srcDelegatee != dstDelegatee && delegateBalance > 0) { + if (srcDelegatee != dstDelegatee && srcDelegateBalance > 0) { if (srcDelegatee != address(0)) { uint256 pos = _checkpoints[srcDelegatee].length; Checkpoint memory oldCkptSrcDelegate = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(_checkpoints[srcDelegatee], pos - 1); - uint256 newLinearBalance = oldCkptSrcDelegate.votes - delegateBalance; - uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes - Math.sqrt(delegateBalance); + uint256 newLinearBalance = oldCkptSrcDelegate.votes - srcDelegateBalance; + uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes - Math.sqrt(srcDelegateBalance); _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); @@ -149,8 +149,8 @@ contract ZeroExVotes is IZeroExVotes { ? Checkpoint(0, 0, 0) : _unsafeAccess(_checkpoints[dstDelegatee], pos - 1); - uint256 newLinearBalance = oldCkptDstDelegate.votes + delegateBalance; - uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(delegateBalance); + uint256 newLinearBalance = oldCkptDstDelegate.votes + srcDelegateBalance; + uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(srcDelegateBalance); _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); From 4132cfbd677cd80d9931fb9f59b58c3c8ba6050a Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 10 Mar 2023 12:50:50 +0200 Subject: [PATCH 078/106] Switch moveEntireVotingPower to a more generic moveVotingPower implementation as in the open-zeppelin contracts --- contracts/governance/src/IZeroExVotes.sol | 15 +-- contracts/governance/src/ZRXWrappedToken.sol | 2 +- contracts/governance/src/ZeroExVotes.sol | 122 +++++++++++-------- 3 files changed, 71 insertions(+), 68 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 01fa1a2712..e2a9448878 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -47,16 +47,6 @@ interface IZeroExVotes { */ event TotalSupplyChanged(uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); - /** - * @dev Emitted a new checkpoint is written. - */ - event CheckpointAdded(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); - - /** - * @dev Emitted when an existing checkpoint is updated. - */ - event CheckpointUpdated(uint256 blockNumber, uint256 totalSupplyVotes, uint256 totalSupplyQuadraticVotes); - /** * @dev Get the `pos`-th checkpoint for `account`. */ @@ -117,11 +107,10 @@ interface IZeroExVotes { function getPastQuadraticTotalSupply(uint256 blockNumber) external view returns (uint256); /** - * @dev Moves the voting power for an account with balance `srcDelegateBalance` from `srcDelegatee` to - * `dstDelegatee`. + * @dev TODO Moves the voting power for an account with balance `amount` from `src` to `dst`. * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) */ - function moveEntireVotingPower(address srcDelegatee, address dstDelegatee, uint256 srcDelegateBalance) external; + function moveVotingPower(address src, address dst, uint256 amount) external; /** * @dev Moves the voting power corresponding to `amount` number of tokens from `srcDelegatee` to `dstDelegatee`. diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 9200468b1b..4c5977bf74 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -111,6 +111,6 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { emit DelegateChanged(delegator, currentDelegate, delegatee); - zeroExVotes.moveEntireVotingPower(currentDelegate, delegatee, delegatorBalance); + zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance); } } diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 5936d79260..5d581cdf37 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -114,57 +114,6 @@ contract ZeroExVotes is IZeroExVotes { return checkpoint.quadraticVotes; } - /** - * @inheritdoc IZeroExVotes - */ - function moveEntireVotingPower( - address srcDelegatee, - address dstDelegatee, - uint256 srcDelegateBalance - ) public override onlyToken { - if (srcDelegatee != dstDelegatee && srcDelegateBalance > 0) { - if (srcDelegatee != address(0)) { - uint256 pos = _checkpoints[srcDelegatee].length; - Checkpoint memory oldCkptSrcDelegate = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_checkpoints[srcDelegatee], pos - 1); - - uint256 newLinearBalance = oldCkptSrcDelegate.votes - srcDelegateBalance; - uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes - Math.sqrt(srcDelegateBalance); - - _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); - - emit DelegateVotesChanged(srcDelegatee, oldCkptSrcDelegate.votes, newLinearBalance); - - emit DelegateQuadraticVotesChanged( - srcDelegatee, - oldCkptSrcDelegate.quadraticVotes, - newQuadraticBalance - ); - } - - if (dstDelegatee != address(0)) { - uint256 pos = _checkpoints[dstDelegatee].length; - Checkpoint memory oldCkptDstDelegate = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_checkpoints[dstDelegatee], pos - 1); - - uint256 newLinearBalance = oldCkptDstDelegate.votes + srcDelegateBalance; - uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(srcDelegateBalance); - - _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); - - emit DelegateVotesChanged(dstDelegatee, oldCkptDstDelegate.votes, newLinearBalance); - - emit DelegateQuadraticVotesChanged( - dstDelegatee, - oldCkptDstDelegate.quadraticVotes, - newQuadraticBalance - ); - } - } - } - /** * @inheritdoc IZeroExVotes */ @@ -228,6 +177,37 @@ contract ZeroExVotes is IZeroExVotes { } } + /** + * @inheritdoc IZeroExVotes + */ + function moveVotingPower(address src, address dst, uint256 amount) public { + if (src != dst && amount > 0) { + if (src != address(0)) { + ( + uint256 oldWeight, + uint256 newWeight, + uint256 oldQuadraticWeight, + uint256 newQuadraticWeight + ) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + + emit DelegateVotesChanged(src, oldWeight, newWeight); + emit DelegateQuadraticVotesChanged(src, oldQuadraticWeight, newQuadraticWeight); + } + + if (dst != address(0)) { + ( + uint256 oldWeight, + uint256 newWeight, + uint256 oldQuadraticWeight, + uint256 newQuadraticWeight + ) = _writeCheckpoint(_checkpoints[dst], _add, amount); + + emit DelegateVotesChanged(dst, oldWeight, newWeight); + emit DelegateQuadraticVotesChanged(dst, oldQuadraticWeight, newQuadraticWeight); + } + } + } + /** * @inheritdoc IZeroExVotes */ @@ -330,8 +310,6 @@ contract ZeroExVotes is IZeroExVotes { Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); chpt.votes = SafeCast.toUint96(voteWeight); chpt.quadraticVotes = SafeCast.toUint48(quadraticVoteWeight); - - emit CheckpointUpdated(chpt.fromBlock, chpt.votes, chpt.quadraticVotes); } else { ckpts.push( Checkpoint({ @@ -340,11 +318,47 @@ contract ZeroExVotes is IZeroExVotes { quadraticVotes: SafeCast.toUint48(quadraticVoteWeight) }) ); + } + } + + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight) { + uint256 pos = ckpts.length; + + Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); - emit CheckpointAdded(ckpts[pos].fromBlock, ckpts[pos].votes, ckpts[pos].quadraticVotes); + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + oldQuadraticWeight = oldCkpt.quadraticVotes; + newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); + chpt.votes = SafeCast.toUint96(newWeight); + chpt.quadraticVotes = SafeCast.toUint48(newQuadraticWeight); + } else { + ckpts.push( + Checkpoint({ + fromBlock: SafeCast.toUint32(block.number), + votes: SafeCast.toUint96(newWeight), + quadraticVotes: SafeCast.toUint48(newQuadraticWeight) + }) + ); } } + function _add(uint256 a, uint256 b) private pure returns (uint256) { + return a + b; + } + + function _subtract(uint256 a, uint256 b) private pure returns (uint256) { + return a - b; + } + /** * @dev Access an element of the array without performing bounds check. The position is assumed to be within bounds. * Implementation from openzeppelin/token/ERC20/extensions/ERC20Votes.sol From 6c8ac9a7b916f475fec1bc2ac57fabd76132cd4a Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 11 Mar 2023 13:10:45 +0200 Subject: [PATCH 079/106] Install foundry earlier in CI --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e80d499d98..cae81c2a2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,11 @@ jobs: - name: Install dependencies run: yarn install --frozen-lockfile + - name: Add foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + - name: Build solution run: yarn build @@ -78,11 +83,6 @@ jobs: -p @0x/order-utils \ -m --serial -c test:ci - - name: Add foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly - - name: Run Forge build for erc20 working-directory: contracts/erc20 run: | From a6504186eba783ca679668000fb2a6e8eaeb96da Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 11 Mar 2023 15:22:44 +0200 Subject: [PATCH 080/106] Switch movePartialVotingPower to the generic moveVotingPower implementation --- contracts/governance/src/IZeroExVotes.sol | 28 ++----- contracts/governance/src/ZRXWrappedToken.sol | 11 ++- contracts/governance/src/ZeroExVotes.sol | 77 +++---------------- .../governance/test/ZRXWrappedTokenTest.t.sol | 15 ++-- 4 files changed, 32 insertions(+), 99 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index e2a9448878..a87477e5db 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -107,29 +107,15 @@ interface IZeroExVotes { function getPastQuadraticTotalSupply(uint256 blockNumber) external view returns (uint256); /** - * @dev TODO Moves the voting power for an account with balance `amount` from `src` to `dst`. - * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) - */ - function moveVotingPower(address src, address dst, uint256 amount) external; - - /** - * @dev Moves the voting power corresponding to `amount` number of tokens from `srcDelegatee` to `dstDelegatee`. - * Note that if the delegator isn't delegating to anyone before the function call `srcDelegatee` = address(0) - * @param srcDelegatee the delegatee we are moving voting power away from - * @param dstDelegatee the delegatee we are moving voting power to - * @param srcDelegateBalance balance of the delegate whose delegatee is `srcDelegatee`. - * This is value _after_ the transfer. - * @param dstDelegateBalance balance of the delegate whose delegatee is `dstDelegatee`. - * This is value _after_ the transfer. + * @dev Moves the voting power corresponding to `amount` number of tokens from `src` to `dst`. + * Note that if the delegator isn't delegating to anyone before the function call `src` = address(0) + * @param src the delegatee we are moving voting power away from + * @param dst the delegatee we are moving voting power to + * @param srcBalance balance of the delegate whose delegatee is `src`. This is value _after_ the transfer. + * @param dstBalance balance of the delegate whose delegatee is `dst`. This is value _after_ the transfer. * @param amount The amount of tokens transferred from the source delegate to destination delegate. */ - function movePartialVotingPower( - address srcDelegatee, - address dstDelegatee, - uint256 srcDelegateBalance, - uint256 dstDelegateBalance, - uint256 amount - ) external; + function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; function writeCheckpointTotalSupplyMint(uint256 amount, uint256 accountBalance) external; diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 4c5977bf74..c1ed9a002d 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -52,8 +52,13 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) { super._afterTokenTransfer(from, to, amount); - // Move voting power when tokens are transferred. - zeroExVotes.movePartialVotingPower(delegates(from), delegates(to), balanceOf(from), balanceOf(to), amount); + zeroExVotes.moveVotingPower( + delegates(from), + delegates(to), + balanceOf(from) + amount, + balanceOf(to) - amount, + amount + ); } function _mint(address account, uint256 amount) internal override(ERC20) { @@ -111,6 +116,6 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { emit DelegateChanged(delegator, currentDelegate, delegatee); - zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance); + zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance, 0, delegatorBalance); } } diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 5d581cdf37..c9cb4756cc 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -117,70 +117,7 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function movePartialVotingPower( - address srcDelegatee, - address dstDelegatee, - uint256 srcDelegateBalance, - uint256 dstDelegateBalance, - uint256 amount - ) public override onlyToken { - if (srcDelegatee != dstDelegatee && amount > 0) { - if (srcDelegatee != address(0)) { - uint256 pos = _checkpoints[srcDelegatee].length; - Checkpoint memory oldCkptSrcDelegate = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_checkpoints[srcDelegatee], pos - 1); - - // Remove the entire source delegator's sqrt balance from delegatee's voting power. - // `srcDelegateBalance` is value _after_ transfer so add the amount that was transferred. - if (pos > 0) - oldCkptSrcDelegate.quadraticVotes -= SafeCast.toUint48(Math.sqrt(srcDelegateBalance + amount)); - - uint256 newLinearBalance = oldCkptSrcDelegate.votes - amount; - uint256 newQuadraticBalance = oldCkptSrcDelegate.quadraticVotes + Math.sqrt(srcDelegateBalance); - - _writeCheckpoint(_checkpoints[srcDelegatee], newLinearBalance, newQuadraticBalance); - - emit DelegateVotesChanged(srcDelegatee, oldCkptSrcDelegate.votes, newLinearBalance); - - emit DelegateQuadraticVotesChanged( - srcDelegatee, - oldCkptSrcDelegate.quadraticVotes, - newQuadraticBalance - ); - } - - if (dstDelegatee != address(0)) { - uint256 pos = _checkpoints[dstDelegatee].length; - Checkpoint memory oldCkptDstDelegate = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_checkpoints[dstDelegatee], pos - 1); - - // Remove the entire destination delegator's sqrt balance from delegatee's voting power. - // `dstDelegateBalance` is value _after_ transfer so remove the amount that was transferred. - if (pos > 0) - oldCkptDstDelegate.quadraticVotes -= SafeCast.toUint48(Math.sqrt(dstDelegateBalance - amount)); - - uint256 newLinearBalance = oldCkptDstDelegate.votes + amount; - uint256 newQuadraticBalance = oldCkptDstDelegate.quadraticVotes + Math.sqrt(dstDelegateBalance); - - _writeCheckpoint(_checkpoints[dstDelegatee], newLinearBalance, newQuadraticBalance); - - emit DelegateVotesChanged(dstDelegatee, oldCkptDstDelegate.votes, newLinearBalance); - - emit DelegateQuadraticVotesChanged( - dstDelegatee, - oldCkptDstDelegate.quadraticVotes, - newQuadraticBalance - ); - } - } - } - - /** - * @inheritdoc IZeroExVotes - */ - function moveVotingPower(address src, address dst, uint256 amount) public { + function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) public { if (src != dst && amount > 0) { if (src != address(0)) { ( @@ -188,7 +125,7 @@ contract ZeroExVotes is IZeroExVotes { uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight - ) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + ) = _writeCheckpoint(_checkpoints[src], _subtract, srcBalance, amount); emit DelegateVotesChanged(src, oldWeight, newWeight); emit DelegateQuadraticVotesChanged(src, oldQuadraticWeight, newQuadraticWeight); @@ -200,7 +137,7 @@ contract ZeroExVotes is IZeroExVotes { uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight - ) = _writeCheckpoint(_checkpoints[dst], _add, amount); + ) = _writeCheckpoint(_checkpoints[dst], _add, dstBalance, amount); emit DelegateVotesChanged(dst, oldWeight, newWeight); emit DelegateQuadraticVotesChanged(dst, oldQuadraticWeight, newQuadraticWeight); @@ -324,6 +261,7 @@ contract ZeroExVotes is IZeroExVotes { function _writeCheckpoint( Checkpoint[] storage ckpts, function(uint256, uint256) view returns (uint256) op, + uint256 userBalance, uint256 delta ) private returns (uint256 oldWeight, uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight) { uint256 pos = ckpts.length; @@ -334,7 +272,12 @@ contract ZeroExVotes is IZeroExVotes { newWeight = op(oldWeight, delta); oldQuadraticWeight = oldCkpt.quadraticVotes; - newQuadraticWeight = op(oldQuadraticWeight, Math.sqrt(delta)); + + // Remove the entire sqrt userBalance from quadratic voting power. + // Note that `userBalance` is value _before_ transfer. + if (pos > 0) oldCkpt.quadraticVotes -= SafeCast.toUint48(Math.sqrt(userBalance)); + + newQuadraticWeight = oldCkpt.quadraticVotes + Math.sqrt(op(userBalance, delta)); if (pos > 0 && oldCkpt.fromBlock == block.number) { Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index fd0bac3c63..a5aeab253f 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -248,15 +248,14 @@ contract ZRXWrappedTokenTest is BaseTest { wToken.transfer(account3, 3e18); - uint256 votingPowerAccount2 = votes.getVotes(account2); - uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 7e18); - assertEq(votingQuadraticPowerAccount2, Math.sqrt(7e18)); + assertEq(wToken.balanceOf(account2), 7e18); + assertEq(wToken.balanceOf(account3), 3e18); + + assertEq(votes.getVotes(account2), 7e18); + assertEq(votes.getQuadraticVotes(account2), Math.sqrt(7e18)); // Since account3 is not delegating to anyone, they should have no voting power - uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 0); - assertEq(votingQuadraticPowerAccount3, 0); + assertEq(votes.getVotes(account3), 0); + assertEq(votes.getQuadraticVotes(account3), 0); } } From a54a94ef94f0a0c98bb27505fe5fcae9aaad68d5 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 11 Mar 2023 15:57:30 +0200 Subject: [PATCH 081/106] Write totalSupplyCheckpoints via the generic _writeCheckpoint --- contracts/governance/src/IZeroExVotes.sol | 4 +- contracts/governance/src/ZRXWrappedToken.sol | 17 ++--- contracts/governance/src/ZeroExVotes.sol | 76 ++++++-------------- 3 files changed, 29 insertions(+), 68 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index a87477e5db..56a81152c5 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -117,7 +117,7 @@ interface IZeroExVotes { */ function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; - function writeCheckpointTotalSupplyMint(uint256 amount, uint256 accountBalance) external; + function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) external; - function writeCheckpointTotalSupplyBurn(uint256 amount, uint256 accountBalance) external; + function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) external; } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index c1ed9a002d..570f9241d0 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -52,27 +52,22 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) { super._afterTokenTransfer(from, to, amount); - zeroExVotes.moveVotingPower( - delegates(from), - delegates(to), - balanceOf(from) + amount, - balanceOf(to) - amount, - amount - ); + uint256 fromBalance = delegates(from) == address(0) ? 0 : balanceOf(from) + amount; + uint256 toBalance = delegates(to) == address(0) ? 0 : balanceOf(to) - amount; + + zeroExVotes.moveVotingPower(delegates(from), delegates(to), fromBalance, toBalance, amount); } function _mint(address account, uint256 amount) internal override(ERC20) { super._mint(account, amount); - // Snapshots the totalSupply after it has been increased. - zeroExVotes.writeCheckpointTotalSupplyMint(amount, balanceOf(account)); + zeroExVotes.writeCheckpointTotalSupplyMint(balanceOf(account) - amount, amount); } function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); - // Snapshots the totalSupply after it has been decreased. - zeroExVotes.writeCheckpointTotalSupplyBurn(amount, balanceOf(account)); + zeroExVotes.writeCheckpointTotalSupplyBurn(balanceOf(account) + amount, amount); } /** diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index c9cb4756cc..b850729eda 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -148,40 +148,29 @@ contract ZeroExVotes is IZeroExVotes { /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyMint(uint256 amount, uint256 accountBalance) public override onlyToken { - uint256 pos = _totalSupplyCheckpoints.length; - Checkpoint memory oldCkptTotalSuply = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_totalSupplyCheckpoints, pos - 1); - - // Remove the account sqrt balance from total quadratic supply. - // `accountBalance` is value _after_ minting - if (pos > 0) oldCkptTotalSuply.quadraticVotes -= SafeCast.toUint48(Math.sqrt(accountBalance - amount)); - - uint256 newLinearBalance = oldCkptTotalSuply.votes + amount; - uint256 newQuadraticBalance = oldCkptTotalSuply.quadraticVotes + Math.sqrt(accountBalance); - - _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); - - emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); + function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) public override onlyToken { + (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( + _totalSupplyCheckpoints, + _add, + accountBalance, + amount + ); + + emit TotalSupplyChanged(newWeight, newQuadraticWeight); } - function writeCheckpointTotalSupplyBurn(uint256 amount, uint256 accountBalance) public override onlyToken { - uint256 pos = _totalSupplyCheckpoints.length; - Checkpoint memory oldCkptTotalSuply = pos == 0 - ? Checkpoint(0, 0, 0) - : _unsafeAccess(_totalSupplyCheckpoints, pos - 1); - - // Remove the account sqrt balance from total quadratic supply. - // `accountBalance` is value _after_ burning - if (pos > 0) oldCkptTotalSuply.quadraticVotes -= SafeCast.toUint48(Math.sqrt(accountBalance + amount)); - - uint256 newLinearBalance = oldCkptTotalSuply.votes - amount; - uint256 newQuadraticBalance = oldCkptTotalSuply.quadraticVotes + Math.sqrt(accountBalance); - - _writeCheckpoint(_totalSupplyCheckpoints, newLinearBalance, newQuadraticBalance); - - emit TotalSupplyChanged(newLinearBalance, newQuadraticBalance); + /** + * @inheritdoc IZeroExVotes + */ + function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) public override onlyToken { + (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( + _totalSupplyCheckpoints, + _subtract, + accountBalance, + amount + ); + + emit TotalSupplyChanged(newWeight, newQuadraticWeight); } /** @@ -235,29 +224,6 @@ contract ZeroExVotes is IZeroExVotes { return checkpoint; } - /** - * Alternative version of openzeppelin/token/ERC20/extensions/ERC20Votes.sol implementation - * which accepts the new voting weight values directly as opposed to calculating these within the function - * based on a addition/subtraction operation. - */ - function _writeCheckpoint(Checkpoint[] storage ckpts, uint256 voteWeight, uint256 quadraticVoteWeight) private { - uint256 pos = ckpts.length; - - if (pos > 0 && _unsafeAccess(ckpts, pos - 1).fromBlock == block.number) { - Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); - chpt.votes = SafeCast.toUint96(voteWeight); - chpt.quadraticVotes = SafeCast.toUint48(quadraticVoteWeight); - } else { - ckpts.push( - Checkpoint({ - fromBlock: SafeCast.toUint32(block.number), - votes: SafeCast.toUint96(voteWeight), - quadraticVotes: SafeCast.toUint48(quadraticVoteWeight) - }) - ); - } - } - function _writeCheckpoint( Checkpoint[] storage ckpts, function(uint256, uint256) view returns (uint256) op, From 46632d51fda6b07551829cb9a83263a339480b42 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 11 Mar 2023 18:16:54 +0200 Subject: [PATCH 082/106] Add threshold for quadratic voting power --- contracts/governance/src/IZeroExVotes.sol | 2 +- contracts/governance/src/ZeroExVotes.sol | 22 ++++++++++++++----- contracts/governance/test/BaseTest.t.sol | 3 ++- .../test/ZeroExTreasuryGovernor.t.sol | 12 ++++++---- .../governance/test/ZeroExVotesTest.t.sol | 4 ++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 56a81152c5..b86baf9ad8 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -23,7 +23,7 @@ interface IZeroExVotes { struct Checkpoint { uint32 fromBlock; uint96 votes; - uint48 quadraticVotes; + uint96 quadraticVotes; } /** diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index b850729eda..7231103460 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -26,6 +26,7 @@ import "./IZeroExVotes.sol"; contract ZeroExVotes is IZeroExVotes { address public token; + uint256 public quadraticThreshold; mapping(address => Checkpoint[]) private _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; @@ -35,9 +36,10 @@ contract ZeroExVotes is IZeroExVotes { _; } - function initialize(address _token) public { + function initialize(address _token, uint256 _quadraticThreshold) public { require(token == address(0), "ZeroExVotes: already initialized"); token = _token; + quadraticThreshold = _quadraticThreshold; } /** @@ -241,20 +243,30 @@ contract ZeroExVotes is IZeroExVotes { // Remove the entire sqrt userBalance from quadratic voting power. // Note that `userBalance` is value _before_ transfer. - if (pos > 0) oldCkpt.quadraticVotes -= SafeCast.toUint48(Math.sqrt(userBalance)); + if (pos > 0) { + uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold + ? Math.sqrt(userBalance) + : quadraticThreshold + Math.sqrt(userBalance - quadraticThreshold); + oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower); + } - newQuadraticWeight = oldCkpt.quadraticVotes + Math.sqrt(op(userBalance, delta)); + // if wallet > threshold, calculate quadratic power over the treshold only, below threshold is linear + uint256 newBalance = op(userBalance, delta); + uint256 newQuadraticBalance = newBalance <= quadraticThreshold + ? Math.sqrt(newBalance) + : quadraticThreshold + Math.sqrt(newBalance - quadraticThreshold); + newQuadraticWeight = oldCkpt.quadraticVotes + newQuadraticBalance; if (pos > 0 && oldCkpt.fromBlock == block.number) { Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1); chpt.votes = SafeCast.toUint96(newWeight); - chpt.quadraticVotes = SafeCast.toUint48(newQuadraticWeight); + chpt.quadraticVotes = SafeCast.toUint96(newQuadraticWeight); } else { ckpts.push( Checkpoint({ fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint96(newWeight), - quadraticVotes: SafeCast.toUint48(newQuadraticWeight) + quadraticVotes: SafeCast.toUint96(newQuadraticWeight) }) ); } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 2b736dde80..91cbbd64ed 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -36,6 +36,7 @@ contract BaseTest is Test { address payable internal account3 = payable(vm.addr(3)); address payable internal account4 = payable(vm.addr(4)); address payable internal securityCouncil = payable(vm.addr(5)); + uint256 internal quadraticThreshold = 500000e18; bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -106,7 +107,7 @@ contract BaseTest is Test { votes = ZeroExVotes(address(votesProxy)); ZRXWrappedToken token = new ZRXWrappedToken(zrxToken, IZeroExVotes(address(votesProxy))); - votes.initialize(address(token)); + votes.initialize(address(token), quadraticThreshold); vm.stopPrank(); return (zrxToken, token, votes); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 8bf043dad4..7deb3dedbb 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -34,7 +34,11 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { function testShouldReturnCorrectQuorum() public { vm.roll(3); - uint256 totalSupplyQuadraticVotes = Math.sqrt(10000000e18) + Math.sqrt(2000000e18) + Math.sqrt(3000000e18); + uint256 totalSupplyQuadraticVotes = quadraticThreshold * + 3 + + Math.sqrt(10000000e18 - quadraticThreshold) + + Math.sqrt(2000000e18 - quadraticThreshold) + + Math.sqrt(3000000e18 - quadraticThreshold); uint256 quorum = (totalSupplyQuadraticVotes * 10) / 100; assertEq(governor.quorum(2), quorum); } @@ -74,9 +78,9 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { // Get vote results (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); - assertEq(votesFor, Math.sqrt(10000000e18)); - assertEq(votesAgainst, Math.sqrt(2000000e18)); - assertEq(votesAbstain, Math.sqrt(3000000e18)); + assertEq(votesFor, (quadraticThreshold + Math.sqrt(10000000e18 - quadraticThreshold))); + assertEq(votesAgainst, quadraticThreshold + Math.sqrt(2000000e18 - quadraticThreshold)); + assertEq(votesAbstain, quadraticThreshold + Math.sqrt(3000000e18 - quadraticThreshold)); IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index d508fd334f..20695130dc 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -41,12 +41,12 @@ contract ZeroExVotesTest is BaseTest { function testShouldNotBeAbleToInitialiseWithZeroAddressToken() public { ZeroExVotes _votes = new ZeroExVotes(); - _votes.initialize(address(0)); + _votes.initialize(address(0), quadraticThreshold); } function testShouldNotBeAbleToReinitialise() public { vm.expectRevert("ZeroExVotes: already initialized"); - votes.initialize(account2); + votes.initialize(account2, quadraticThreshold); } function testShouldBeAbleToReadCheckpoints() public { From 80066469e11a4a808ce13c1bad2f7811b42110a3 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Sat, 11 Mar 2023 18:19:29 +0200 Subject: [PATCH 083/106] Remove autoinserted code by OZ --- contracts/governance/src/IZeroExVotes.sol | 1 - contracts/governance/src/ZeroExTimelock.sol | 1 - 2 files changed, 2 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index b86baf9ad8..20a7bce527 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -18,7 +18,6 @@ */ pragma solidity ^0.8.19; -/// @custom:security-contact security@0xproject.com interface IZeroExVotes { struct Checkpoint { uint32 fromBlock; diff --git a/contracts/governance/src/ZeroExTimelock.sol b/contracts/governance/src/ZeroExTimelock.sol index ea670b366d..a7c5c6d273 100644 --- a/contracts/governance/src/ZeroExTimelock.sol +++ b/contracts/governance/src/ZeroExTimelock.sol @@ -20,7 +20,6 @@ pragma solidity ^0.8.19; import "@openzeppelin/governance/TimelockController.sol"; -/// @custom:security-contact security@0xproject.com contract ZeroExTimelock is TimelockController { // minDelay is how long you have to wait before executing // proposers is the list of addresses that can propose From 55806c62ca4bd37f6ca95bc626611414282c85bd Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Mar 2023 17:26:03 +0200 Subject: [PATCH 084/106] Add openzeppelin/contracts-upgradable --- .gitmodules | 5 +++++ contracts/governance/lib/openzeppelin-contracts-upgradeable | 1 + 2 files changed, 6 insertions(+) create mode 160000 contracts/governance/lib/openzeppelin-contracts-upgradeable diff --git a/.gitmodules b/.gitmodules index b83bfd45ae..eec23ed020 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,8 @@ [submodule "contracts/governance/lib/openzeppelin-contracts"] path = contracts/governance/lib/openzeppelin-contracts url = https://github.com/openzeppelin/openzeppelin-contracts +[submodule "contracts/governance/lib/openzeppelin-contracts-upgradeable"] + path = contracts/governance/lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "lib/openzeppelin-contracts-upgradeable"] + branch = v4.8.2 diff --git a/contracts/governance/lib/openzeppelin-contracts-upgradeable b/contracts/governance/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000000..f6c4c9c4ec --- /dev/null +++ b/contracts/governance/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit f6c4c9c4ec601665ca74d2c9dddf547fc425658c From ce87bad307912230e238610ccc90bed198235813 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Mar 2023 18:00:51 +0200 Subject: [PATCH 085/106] Add initializable base to Voting contract --- contracts/governance/foundry.toml | 1 + contracts/governance/src/ZeroExVotes.sol | 17 +++++++++++++++-- contracts/governance/test/ZeroExVotesTest.t.sol | 6 +++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index 50b69e7fe8..6c140515d5 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -5,6 +5,7 @@ libs = ['lib', "../utils/contracts/src/"] fs_permissions = [{ access = "read", path = "./" }] remappings = [ '@openzeppelin/=./lib/openzeppelin-contracts/contracts/', + '@openzeppelin-contracts-upgradeable/=./lib/openzeppelin-contracts-upgradeable/contracts/', '@0x/contracts-utils/=../utils/', ] solc = '0.8.19' diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 7231103460..2b3cbc01f9 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -22,21 +22,34 @@ import "@openzeppelin/utils/math/SafeCast.sol"; import "@openzeppelin/utils/math/Math.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/governance/utils/IVotes.sol"; +import "@openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin-contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; import "./IZeroExVotes.sol"; -contract ZeroExVotes is IZeroExVotes { +contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpgradeable { address public token; uint256 public quadraticThreshold; mapping(address => Checkpoint[]) private _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; + constructor() { + _disableInitializers(); + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + modifier onlyToken() { require(msg.sender == token, "ZeroExVotes: only token allowed"); _; } - function initialize(address _token, uint256 _quadraticThreshold) public { + function initialize(address _token, uint256 _quadraticThreshold) public initializer { + __Ownable_init(); + __UUPSUpgradeable_init(); + + require(_token != address(0), "ZeroExVotes: token cannot be 0"); require(token == address(0), "ZeroExVotes: already initialized"); token = _token; quadraticThreshold = _quadraticThreshold; diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 20695130dc..17a92001f5 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -41,11 +41,15 @@ contract ZeroExVotesTest is BaseTest { function testShouldNotBeAbleToInitialiseWithZeroAddressToken() public { ZeroExVotes _votes = new ZeroExVotes(); + ERC1967Proxy _votesProxy = new ERC1967Proxy(address(_votes), new bytes(0)); + _votes = ZeroExVotes(address(_votesProxy)); + + vm.expectRevert("ZeroExVotes: token cannot be 0"); _votes.initialize(address(0), quadraticThreshold); } function testShouldNotBeAbleToReinitialise() public { - vm.expectRevert("ZeroExVotes: already initialized"); + vm.expectRevert("Initializable: contract is already initialized"); votes.initialize(account2, quadraticThreshold); } From 5773efd301188bcff0848ef37165604f88401ae4 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Mar 2023 18:07:14 +0200 Subject: [PATCH 086/106] Fix terminogy error in natspec --- contracts/governance/src/IZeroExVotes.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 20a7bce527..168edd5909 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -110,8 +110,8 @@ interface IZeroExVotes { * Note that if the delegator isn't delegating to anyone before the function call `src` = address(0) * @param src the delegatee we are moving voting power away from * @param dst the delegatee we are moving voting power to - * @param srcBalance balance of the delegate whose delegatee is `src`. This is value _after_ the transfer. - * @param dstBalance balance of the delegate whose delegatee is `dst`. This is value _after_ the transfer. + * @param srcBalance balance of the delegator whose delegatee is `src`. This is value _after_ the transfer. + * @param dstBalance balance of the delegator whose delegatee is `dst`. This is value _after_ the transfer. * @param amount The amount of tokens transferred from the source delegate to destination delegate. */ function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; From 403455b59cd28ef3919ff6b4c5367b06d17d7904 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 13 Mar 2023 18:15:25 +0200 Subject: [PATCH 087/106] Fix code comment --- contracts/governance/src/ZeroExVotes.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 2b3cbc01f9..c6534b0bf2 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -255,7 +255,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg oldQuadraticWeight = oldCkpt.quadraticVotes; // Remove the entire sqrt userBalance from quadratic voting power. - // Note that `userBalance` is value _before_ transfer. + // Note that `userBalance` is value _after_ transfer. if (pos > 0) { uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold ? Math.sqrt(userBalance) From 545cf87a1962e965a33ad409112129d812d41862 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 14 Mar 2023 13:08:34 +0200 Subject: [PATCH 088/106] Remove obsoleted overrides and add a missing modifier to moveVotingPower --- contracts/governance/src/ZeroExVotes.sol | 28 ++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index c6534b0bf2..6cee7fa66d 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -58,21 +58,21 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function checkpoints(address account, uint32 pos) public view override returns (Checkpoint memory) { + function checkpoints(address account, uint32 pos) public view returns (Checkpoint memory) { return _checkpoints[account][pos]; } /** * @inheritdoc IZeroExVotes */ - function numCheckpoints(address account) public view override returns (uint32) { + function numCheckpoints(address account) public view returns (uint32) { return SafeCast.toUint32(_checkpoints[account].length); } /** * @inheritdoc IZeroExVotes */ - function getVotes(address account) public view override returns (uint256) { + function getVotes(address account) public view returns (uint256) { uint256 pos = _checkpoints[account].length; return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes; } @@ -80,7 +80,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function getQuadraticVotes(address account) public view override returns (uint256) { + function getQuadraticVotes(address account) public view returns (uint256) { uint256 pos = _checkpoints[account].length; return pos == 0 ? 0 : _checkpoints[account][pos - 1].quadraticVotes; } @@ -88,7 +88,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function getPastVotes(address account, uint256 blockNumber) public view override returns (uint256) { + function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); Checkpoint memory checkpoint = _checkpointsLookup(_checkpoints[account], blockNumber); @@ -98,7 +98,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function getPastQuadraticVotes(address account, uint256 blockNumber) public view override returns (uint256) { + function getPastQuadraticVotes(address account, uint256 blockNumber) public view returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); Checkpoint memory checkpoint = _checkpointsLookup(_checkpoints[account], blockNumber); @@ -108,7 +108,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function getPastTotalSupply(uint256 blockNumber) public view override returns (uint256) { + function getPastTotalSupply(uint256 blockNumber) public view returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); // Note that due to the disabled updates of `_totalSupplyCheckpoints` in `writeCheckpointTotalSupply` function @@ -120,7 +120,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function getPastQuadraticTotalSupply(uint256 blockNumber) public view override returns (uint256) { + function getPastQuadraticTotalSupply(uint256 blockNumber) public view returns (uint256) { require(blockNumber < block.number, "ZeroExVotes: block not yet mined"); // Note that due to the disabled updates of `_totalSupplyCheckpoints` in `writeCheckpointTotalSupply` function @@ -132,7 +132,13 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) public { + function moveVotingPower( + address src, + address dst, + uint256 srcBalance, + uint256 dstBalance, + uint256 amount + ) public onlyToken { if (src != dst && amount > 0) { if (src != address(0)) { ( @@ -163,7 +169,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) public override onlyToken { + function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) public onlyToken { (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( _totalSupplyCheckpoints, _add, @@ -177,7 +183,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) public override onlyToken { + function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) public onlyToken { (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( _totalSupplyCheckpoints, _subtract, From b55baabe14134fea5b8c9502c915730070dd4e19 Mon Sep 17 00:00:00 2001 From: Elena Date: Tue, 14 Mar 2023 13:10:17 +0200 Subject: [PATCH 089/106] Remove amount check Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com> --- contracts/governance/src/ZeroExVotes.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 6cee7fa66d..959d954271 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -139,7 +139,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg uint256 dstBalance, uint256 amount ) public onlyToken { - if (src != dst && amount > 0) { + if (src != dst) { if (src != address(0)) { ( uint256 oldWeight, From b8d67f0eb33db9dee25cd7bb2e06a4bb02ca3687 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 14 Mar 2023 13:36:18 +0200 Subject: [PATCH 090/106] Fix a calculation error and clean tests --- contracts/governance/src/ZeroExVotes.sol | 4 +- .../governance/test/ZRXWrappedTokenTest.t.sol | 10 +- .../governance/test/ZeroExVotesTest.t.sol | 205 ++++++++---------- 3 files changed, 99 insertions(+), 120 deletions(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 959d954271..3161293393 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -264,7 +264,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg // Note that `userBalance` is value _after_ transfer. if (pos > 0) { uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold - ? Math.sqrt(userBalance) + ? userBalance : quadraticThreshold + Math.sqrt(userBalance - quadraticThreshold); oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower); } @@ -272,7 +272,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg // if wallet > threshold, calculate quadratic power over the treshold only, below threshold is linear uint256 newBalance = op(userBalance, delta); uint256 newQuadraticBalance = newBalance <= quadraticThreshold - ? Math.sqrt(newBalance) + ? newBalance : quadraticThreshold + Math.sqrt(newBalance - quadraticThreshold); newQuadraticWeight = oldCkpt.quadraticVotes + newQuadraticBalance; diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index a5aeab253f..101c873239 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -151,7 +151,7 @@ contract ZRXWrappedTokenTest is BaseTest { uint256 totalSupplyVotes = votes.getPastTotalSupply(2); uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); assertEq(totalSupplyVotes, 1e18); - assertEq(totalSupplyQuadraticVotes, Math.sqrt(1e18)); + assertEq(totalSupplyQuadraticVotes, 1e18); } function testWhenMintingForAccountWithExistingBalanceTotalSupplyCheckpointsAreCorrect() public { @@ -170,7 +170,7 @@ contract ZRXWrappedTokenTest is BaseTest { uint256 totalSupplyVotes = votes.getPastTotalSupply(2); uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); assertEq(totalSupplyVotes, 4e18); - assertEq(totalSupplyQuadraticVotes, Math.sqrt(4e18)); + assertEq(totalSupplyQuadraticVotes, 4e18); } function testWhenMintingForMultipleAccountsTotalSupplyCheckpointsAreCorrect() public { @@ -197,7 +197,7 @@ contract ZRXWrappedTokenTest is BaseTest { uint256 totalSupplyVotes = votes.getPastTotalSupply(2); uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); assertEq(totalSupplyVotes, 7e18); - assertEq(totalSupplyQuadraticVotes, Math.sqrt(5e18) + Math.sqrt(2e18)); + assertEq(totalSupplyQuadraticVotes, 5e18 + 2e18); } function testWhenBurningForMultipleAccountsTotalSupplyCheckpointsAreCorrect() public { @@ -224,7 +224,7 @@ contract ZRXWrappedTokenTest is BaseTest { uint256 totalSupplyVotes = votes.getPastTotalSupply(2); uint256 totalSupplyQuadraticVotes = votes.getPastQuadraticTotalSupply(2); assertEq(totalSupplyVotes, 3e18); - assertEq(totalSupplyQuadraticVotes, Math.sqrt(1e18) + Math.sqrt(2e18)); + assertEq(totalSupplyQuadraticVotes, 1e18 + 2e18); } function testShouldBeAbleToTransferCorrectly() public { @@ -252,7 +252,7 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(wToken.balanceOf(account3), 3e18); assertEq(votes.getVotes(account2), 7e18); - assertEq(votes.getQuadraticVotes(account2), Math.sqrt(7e18)); + assertEq(votes.getQuadraticVotes(account2), 7e18); // Since account3 is not delegating to anyone, they should have no voting power assertEq(votes.getVotes(account3), 0); diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 17a92001f5..23a3a90402 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -30,8 +30,9 @@ contract ZeroExVotesTest is BaseTest { function setUp() public { (token, wToken, votes) = setupZRXWrappedToken(); vm.startPrank(account1); - token.transfer(account2, 100e18); - token.transfer(account3, 200e18); + token.transfer(account2, 700000e18); + token.transfer(account3, 600000e18); + token.transfer(account4, 500000e18); vm.stopPrank(); } @@ -56,8 +57,8 @@ contract ZeroExVotesTest is BaseTest { function testShouldBeAbleToReadCheckpoints() public { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); vm.roll(2); wToken.delegate(account3); @@ -65,50 +66,40 @@ contract ZeroExVotesTest is BaseTest { IZeroExVotes.Checkpoint memory checkpoint = votes.checkpoints(account3, 0); assertEq(checkpoint.fromBlock, 2); - assertEq(checkpoint.votes, 10e18); - assertEq(checkpoint.quadraticVotes, Math.sqrt(10e18)); + assertEq(checkpoint.votes, 700000e18); + assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); } function testShouldBeAbleToSelfDelegateVotingPower() public { // Check voting power initially is 0 - uint256 votingPowerAccount2 = votes.getVotes(account2); - uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 0); - assertEq(votingQuadraticPowerAccount2, 0); + assertEq(votes.getVotes(account2), 0); + assertEq(votes.getQuadraticVotes(account2), 0); // Wrap ZRX and delegate voting power to themselves vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); wToken.delegate(account2); - // Check voting power is now = token balance - votingPowerAccount2 = votes.getVotes(account2); - assertEq(votingPowerAccount2, 100e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingQuadraticPowerAccount2, Math.sqrt(100e18)); + // Check voting power + assertEq(votes.getVotes(account2), 700000e18); + assertEq(votes.getQuadraticVotes(account2), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { // Check voting power initially is 0 - uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 0); - assertEq(votingQuadraticPowerAccount3, 0); + assertEq(votes.getVotes(account3), 0); + assertEq(votes.getQuadraticVotes(account3), 0); // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); wToken.delegate(account3); - // Check voting power is now = token balance - votingPowerAccount3 = votes.getVotes(account3); - assertEq(votingPowerAccount3, 10e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(10e18)); + // Check voting power + assertEq(votes.getVotes(account3), 700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { @@ -118,8 +109,8 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); vm.stopPrank(); assertEq(wToken.delegates(account2), address(0)); @@ -139,8 +130,8 @@ contract ZeroExVotesTest is BaseTest { wToken.delegateBySig(account3, nonce, expiry, v, r, s); assertEq(wToken.delegates(account2), account3); - assertEq(votes.getVotes(account3), 10e18); - assertEq(votes.getQuadraticVotes(account3), Math.sqrt(10e18)); + assertEq(votes.getVotes(account3), 700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); } function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { @@ -150,8 +141,8 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 10e18); - wToken.depositFor(account2, 10e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); vm.stopPrank(); (uint8 v, bytes32 r, bytes32 s) = vm.sign( @@ -172,44 +163,43 @@ contract ZeroExVotesTest is BaseTest { function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public { // Check account4 voting power initially is 0 - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); + assertEq(votes.getVotes(account4), 0); + assertEq(votes.getQuadraticVotes(account4), 0); // Account 2 wraps ZRX and delegates voting power to account4 vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); wToken.delegate(account4); vm.stopPrank(); // Account 3 also wraps ZRX and delegates voting power to account4 vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 200e18); + token.approve(address(wToken), 600000e18); + wToken.depositFor(account3, 600000e18); wToken.delegate(account4); vm.stopPrank(); - // Check voting power is now = token balance - votingPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 300e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + // Check voting power + assertEq(votes.getVotes(account4), 1300000e18); + assertEq( + votes.getQuadraticVotes(account4), + quadraticThreshold * + 2 + + Math.sqrt(700000e18 - quadraticThreshold) + + Math.sqrt(600000e18 - quadraticThreshold) + ); } function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnDifferentBlock() public { // Check account4 voting power initially is 0 - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); + assertEq(votes.getVotes(account4), 0); + assertEq(votes.getQuadraticVotes(account4), 0); // Account 2 wraps ZRX and delegates voting power to account4 vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 100e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 700000e18); wToken.delegate(account4); vm.stopPrank(); @@ -217,86 +207,79 @@ contract ZeroExVotesTest is BaseTest { vm.roll(2); // Account 3 also wraps ZRX and delegates voting power to account4 vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 200e18); + token.approve(address(wToken), 600000e18); + wToken.depositFor(account3, 600000e18); wToken.delegate(account4); vm.stopPrank(); - // Check voting power is now = token balance - votingPowerAccount4 = votes.getVotes(account4); - assertEq(votingPowerAccount4, 300e18); - // Check quadratic voting power is now = sqrt(token balance) - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18) + Math.sqrt(200e18)); + // Check voting power + assertEq(votes.getVotes(account4), 1300000e18); + assertEq( + votes.getQuadraticVotes(account4), + quadraticThreshold * + 2 + + Math.sqrt(700000e18 - quadraticThreshold) + + Math.sqrt(600000e18 - quadraticThreshold) + ); } function testComplexDelegationScenario() public { // Account 2 wraps ZRX and delegates to itself vm.startPrank(account2); - token.approve(address(wToken), 100e18); - wToken.depositFor(account2, 90e18); + token.approve(address(wToken), 700000e18); + wToken.depositFor(account2, 490000e18); wToken.delegate(account2); vm.stopPrank(); - uint256 votingPowerAccount2 = votes.getVotes(account2); - uint256 votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 90e18); - assertEq(votingQuadraticPowerAccount2, Math.sqrt(90e18)); // 9486832980 + assertEq(votes.getVotes(account2), 490000e18); + assertEq(votes.getQuadraticVotes(account2), 490000e18); // Account 3 wraps ZRX and delegates to account4 vm.startPrank(account3); - token.approve(address(wToken), 200e18); - wToken.depositFor(account3, 100e18); + token.approve(address(wToken), 500000e18); + wToken.depositFor(account3, 500000e18); wToken.delegate(account4); vm.stopPrank(); - uint256 votingPowerAccount4 = votes.getVotes(account4); - uint256 votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingPowerAccount4, 100e18); - assertEq(votingQuadraticPowerAccount4, Math.sqrt(100e18)); + assertEq(votes.getVotes(account4), 500000e18); + assertEq(votes.getQuadraticVotes(account4), 500000e18); // Voting power distribution now is as follows - // account2 -> account2 90e18 | 9486832980 - // account3 -> account4 100e18 | 10e9 + // account2 -> account2 490000e18 | 490000e18 + // account3 -> account4 500000e18 | 500000e18 - // Account 2 deposits the remaining 10e18 and delegates to account3 + // Account 2 deposits the remaining 210000e18 and delegates to account3 vm.startPrank(account2); - wToken.depositFor(account2, 10e18); + wToken.depositFor(account2, 210000e18); wToken.delegate(account3); vm.stopPrank(); - uint256 votingPowerAccount3 = votes.getVotes(account3); - uint256 votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 100e18); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18)); + assertEq(votes.getVotes(account3), 700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); // Voting power distribution now is as follows - // account2 -> account3 100e18 | 10e18 - // account3 -> account4 100e18 | 10e9 + // account2 -> account3 700000e18 | 500000e18 + Math.sqrt(700000e18 - 500000e18) + // account3 -> account4 500000e18 | 500000e18 // Account 3 delegates to itself vm.startPrank(account3); wToken.delegate(account3); vm.stopPrank(); - votingPowerAccount3 = votes.getVotes(account3); - votingQuadraticPowerAccount3 = votes.getQuadraticVotes(account3); - assertEq(votingPowerAccount3, 200e18); - assertEq(votingQuadraticPowerAccount3, Math.sqrt(100e18) + Math.sqrt(100e18)); + assertEq(votes.getVotes(account3), 1200000e18); + assertEq( + votes.getQuadraticVotes(account3), + quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold) + 500000e18 + ); // Voting power distribution now is as follows - // account2, account3 -> account3 100e18 | 20e18 + // account2, account3 -> account3 1200000e18 | 500000e18 + Math.sqrt(700000e18 - 500000e18) + 500000e18 // Check account2 and account4 no longer have voting power - votingPowerAccount2 = votes.getVotes(account2); - votingQuadraticPowerAccount2 = votes.getQuadraticVotes(account2); - assertEq(votingPowerAccount2, 0); - assertEq(votingQuadraticPowerAccount2, 0); - - votingPowerAccount4 = votes.getVotes(account4); - votingQuadraticPowerAccount4 = votes.getQuadraticVotes(account4); - assertEq(votingPowerAccount4, 0); - assertEq(votingQuadraticPowerAccount4, 0); + assertEq(votes.getVotes(account2), 0); + assertEq(votes.getQuadraticVotes(account2), 0); + assertEq(votes.getVotes(account4), 0); + assertEq(votes.getQuadraticVotes(account4), 0); } function testCheckpointIsCorrectlyUpdatedOnTheSameBlock() public { @@ -307,12 +290,11 @@ contract ZeroExVotesTest is BaseTest { wToken.delegate(account2); vm.stopPrank(); - uint256 numCheckpointsAccount2 = votes.numCheckpoints(account2); - assertEq(numCheckpointsAccount2, 1); + assertEq(votes.numCheckpoints(account2), 1); IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0); assertEq(checkpoint1Account2.fromBlock, 1); assertEq(checkpoint1Account2.votes, 20e18); - assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + assertEq(checkpoint1Account2.quadraticVotes, 20e18); // Account 3 wraps ZRX and delegates 10e18 to account2 vm.startPrank(account3); @@ -321,12 +303,11 @@ contract ZeroExVotesTest is BaseTest { wToken.delegate(account2); vm.stopPrank(); - numCheckpointsAccount2 = votes.numCheckpoints(account2); - assertEq(numCheckpointsAccount2, 1); + assertEq(votes.numCheckpoints(account2), 1); checkpoint1Account2 = votes.checkpoints(account2, 0); assertEq(checkpoint1Account2.fromBlock, 1); assertEq(checkpoint1Account2.votes, 30e18); - assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18) + Math.sqrt(10e18)); + assertEq(checkpoint1Account2.quadraticVotes, 20e18 + 10e18); } function testCheckpointIsCorrectlyUpdatedOnDifferentBlocks() public { @@ -337,12 +318,11 @@ contract ZeroExVotesTest is BaseTest { wToken.delegate(account2); vm.stopPrank(); - uint256 numCheckpointsAccount2 = votes.numCheckpoints(account2); - assertEq(numCheckpointsAccount2, 1); + assertEq(votes.numCheckpoints(account2), 1); IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0); assertEq(checkpoint1Account2.fromBlock, 1); assertEq(checkpoint1Account2.votes, 20e18); - assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + assertEq(checkpoint1Account2.quadraticVotes, 20e18); vm.roll(2); // Account 3 wraps ZRX and delegates 10e18 to account2 @@ -352,17 +332,16 @@ contract ZeroExVotesTest is BaseTest { wToken.delegate(account2); vm.stopPrank(); - numCheckpointsAccount2 = votes.numCheckpoints(account2); - assertEq(numCheckpointsAccount2, 2); + assertEq(votes.numCheckpoints(account2), 2); IZeroExVotes.Checkpoint memory checkpoint2Account2 = votes.checkpoints(account2, 1); assertEq(checkpoint2Account2.fromBlock, 2); assertEq(checkpoint2Account2.votes, 30e18); - assertEq(checkpoint2Account2.quadraticVotes, Math.sqrt(20e18) + Math.sqrt(10e18)); + assertEq(checkpoint2Account2.quadraticVotes, 20e18 + 10e18); // Check the old checkpoint hasn't changed checkpoint1Account2 = votes.checkpoints(account2, 0); assertEq(checkpoint1Account2.fromBlock, 1); assertEq(checkpoint1Account2.votes, 20e18); - assertEq(checkpoint1Account2.quadraticVotes, Math.sqrt(20e18)); + assertEq(checkpoint1Account2.quadraticVotes, 20e18); } } From 8961e1deb8c5e5ffa1bf26425365d6e386065cee Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 15 Mar 2023 10:18:14 +0200 Subject: [PATCH 091/106] Update thresholds for treasury governor --- .../governance/src/ZeroExTreasuryGovernor.sol | 2 +- contracts/governance/test/BaseTest.t.sol | 2 +- .../test/ZeroExTreasuryGovernor.t.sol | 2 +- .../governance/test/ZeroExVotesTest.t.sol | 100 +++++++++--------- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 2e46dd8c57..0df4313995 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -43,7 +43,7 @@ contract ZeroExTreasuryGovernor is address _securityCouncil ) Governor("ZeroExTreasuryGovernor") - GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 5e11) + GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 250000e18) GovernorVotes(votes) GovernorVotesQuorumFraction(10) GovernorTimelockControl(_timelock) diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 91cbbd64ed..e5e35cce33 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -36,7 +36,7 @@ contract BaseTest is Test { address payable internal account3 = payable(vm.addr(3)); address payable internal account4 = payable(vm.addr(4)); address payable internal securityCouncil = payable(vm.addr(5)); - uint256 internal quadraticThreshold = 500000e18; + uint256 internal quadraticThreshold = 1000000e18; bytes32 internal constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 7deb3dedbb..3e91b1802e 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -23,7 +23,7 @@ import "./ZeroExGovernorBaseTest.t.sol"; contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { function setUp() public { governorName = "ZeroExTreasuryGovernor"; - proposalThreshold = 5e11; + proposalThreshold = 250000e18; address governorAddress; (token, wToken, votes, , timelock, , governorAddress) = setupGovernance(); diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 23a3a90402..61b1ee8d38 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -30,9 +30,9 @@ contract ZeroExVotesTest is BaseTest { function setUp() public { (token, wToken, votes) = setupZRXWrappedToken(); vm.startPrank(account1); - token.transfer(account2, 700000e18); - token.transfer(account3, 600000e18); - token.transfer(account4, 500000e18); + token.transfer(account2, 1700000e18); + token.transfer(account3, 1600000e18); + token.transfer(account4, 1000000e18); vm.stopPrank(); } @@ -57,8 +57,8 @@ contract ZeroExVotesTest is BaseTest { function testShouldBeAbleToReadCheckpoints() public { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); vm.roll(2); wToken.delegate(account3); @@ -66,8 +66,8 @@ contract ZeroExVotesTest is BaseTest { IZeroExVotes.Checkpoint memory checkpoint = votes.checkpoints(account3, 0); assertEq(checkpoint.fromBlock, 2); - assertEq(checkpoint.votes, 700000e18); - assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); + assertEq(checkpoint.votes, 1700000e18); + assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); } function testShouldBeAbleToSelfDelegateVotingPower() public { @@ -77,13 +77,13 @@ contract ZeroExVotesTest is BaseTest { // Wrap ZRX and delegate voting power to themselves vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); wToken.delegate(account2); // Check voting power - assertEq(votes.getVotes(account2), 700000e18); - assertEq(votes.getQuadraticVotes(account2), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); + assertEq(votes.getVotes(account2), 1700000e18); + assertEq(votes.getQuadraticVotes(account2), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { @@ -93,13 +93,13 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); wToken.delegate(account3); // Check voting power - assertEq(votes.getVotes(account3), 700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); + assertEq(votes.getVotes(account3), 1700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { @@ -109,8 +109,8 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); vm.stopPrank(); assertEq(wToken.delegates(account2), address(0)); @@ -130,8 +130,8 @@ contract ZeroExVotesTest is BaseTest { wToken.delegateBySig(account3, nonce, expiry, v, r, s); assertEq(wToken.delegates(account2), account3); - assertEq(votes.getVotes(account3), 700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); + assertEq(votes.getVotes(account3), 1700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); } function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { @@ -141,8 +141,8 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); vm.stopPrank(); (uint8 v, bytes32 r, bytes32 s) = vm.sign( @@ -168,26 +168,26 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account4 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); wToken.delegate(account4); vm.stopPrank(); // Account 3 also wraps ZRX and delegates voting power to account4 vm.startPrank(account3); - token.approve(address(wToken), 600000e18); - wToken.depositFor(account3, 600000e18); + token.approve(address(wToken), 1600000e18); + wToken.depositFor(account3, 1600000e18); wToken.delegate(account4); vm.stopPrank(); // Check voting power - assertEq(votes.getVotes(account4), 1300000e18); + assertEq(votes.getVotes(account4), 3300000e18); assertEq( votes.getQuadraticVotes(account4), quadraticThreshold * 2 + - Math.sqrt(700000e18 - quadraticThreshold) + - Math.sqrt(600000e18 - quadraticThreshold) + Math.sqrt(1700000e18 - quadraticThreshold) + + Math.sqrt(1600000e18 - quadraticThreshold) ); } @@ -198,8 +198,8 @@ contract ZeroExVotesTest is BaseTest { // Account 2 wraps ZRX and delegates voting power to account4 vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 700000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); wToken.delegate(account4); vm.stopPrank(); @@ -207,32 +207,32 @@ contract ZeroExVotesTest is BaseTest { vm.roll(2); // Account 3 also wraps ZRX and delegates voting power to account4 vm.startPrank(account3); - token.approve(address(wToken), 600000e18); - wToken.depositFor(account3, 600000e18); + token.approve(address(wToken), 1600000e18); + wToken.depositFor(account3, 1600000e18); wToken.delegate(account4); vm.stopPrank(); // Check voting power - assertEq(votes.getVotes(account4), 1300000e18); + assertEq(votes.getVotes(account4), 3300000e18); assertEq( votes.getQuadraticVotes(account4), quadraticThreshold * 2 + - Math.sqrt(700000e18 - quadraticThreshold) + - Math.sqrt(600000e18 - quadraticThreshold) + Math.sqrt(1700000e18 - quadraticThreshold) + + Math.sqrt(1600000e18 - quadraticThreshold) ); } function testComplexDelegationScenario() public { // Account 2 wraps ZRX and delegates to itself vm.startPrank(account2); - token.approve(address(wToken), 700000e18); - wToken.depositFor(account2, 490000e18); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1000000e18); wToken.delegate(account2); vm.stopPrank(); - assertEq(votes.getVotes(account2), 490000e18); - assertEq(votes.getQuadraticVotes(account2), 490000e18); + assertEq(votes.getVotes(account2), 1000000e18); + assertEq(votes.getQuadraticVotes(account2), 1000000e18); // Account 3 wraps ZRX and delegates to account4 vm.startPrank(account3); @@ -245,35 +245,35 @@ contract ZeroExVotesTest is BaseTest { assertEq(votes.getQuadraticVotes(account4), 500000e18); // Voting power distribution now is as follows - // account2 -> account2 490000e18 | 490000e18 - // account3 -> account4 500000e18 | 500000e18 + // account2 -> account2 1000000e18 | 1000000e18 + // account3 -> account4 500000e18 | 500000e18 - // Account 2 deposits the remaining 210000e18 and delegates to account3 + // Account 2 deposits the remaining 700000e18 and delegates to account3 vm.startPrank(account2); - wToken.depositFor(account2, 210000e18); + wToken.depositFor(account2, 700000e18); wToken.delegate(account3); vm.stopPrank(); - assertEq(votes.getVotes(account3), 700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold)); + assertEq(votes.getVotes(account3), 1700000e18); + assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); // Voting power distribution now is as follows - // account2 -> account3 700000e18 | 500000e18 + Math.sqrt(700000e18 - 500000e18) - // account3 -> account4 500000e18 | 500000e18 + // account2 -> account3 1700000e18 | 1000000e18 + Math.sqrt(1700000e18 - 1000000e18) + // account3 -> account4 500000e18 | 500000e18 // Account 3 delegates to itself vm.startPrank(account3); wToken.delegate(account3); vm.stopPrank(); - assertEq(votes.getVotes(account3), 1200000e18); + assertEq(votes.getVotes(account3), 2200000e18); assertEq( votes.getQuadraticVotes(account3), - quadraticThreshold + Math.sqrt(700000e18 - quadraticThreshold) + 500000e18 + quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold) + 500000e18 ); // Voting power distribution now is as follows - // account2, account3 -> account3 1200000e18 | 500000e18 + Math.sqrt(700000e18 - 500000e18) + 500000e18 + // account2, account3 -> account3 2200000e18 | 1000000e18 + Math.sqrt(2200000e18 - 1000000e18) + 500000e18 // Check account2 and account4 no longer have voting power assertEq(votes.getVotes(account2), 0); From 7be528ecb50d93e9866f7bc853e76ee4d7f38457 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 13 Mar 2023 17:07:51 -0400 Subject: [PATCH 092/106] Fix testShouldNotBeAbleToDelegateWithSignatureAfterExpiry --- contracts/governance/foundry.toml | 1 + contracts/governance/test/ZeroExVotesTest.t.sol | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/governance/foundry.toml b/contracts/governance/foundry.toml index 6c140515d5..d53b247d63 100644 --- a/contracts/governance/foundry.toml +++ b/contracts/governance/foundry.toml @@ -10,6 +10,7 @@ remappings = [ ] solc = '0.8.19' optimizer_runs = 20_000 +via_ir = true [profile.smt.model_checker] engine = 'chc' diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 61b1ee8d38..51a544e2c4 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -136,7 +136,7 @@ contract ZeroExVotesTest is BaseTest { function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { uint256 nonce = 0; - uint256 expiry = block.timestamp; + uint256 expiry = block.timestamp - 1; uint256 privateKey = 2; // Account 2 wraps ZRX and delegates voting power to account3 @@ -156,7 +156,6 @@ contract ZeroExVotesTest is BaseTest { ) ); - vm.warp(block.timestamp + 1); vm.expectRevert("ERC20Votes: signature expired"); wToken.delegateBySig(account3, nonce, expiry, v, r, s); } From 89c77a74979356ff8711a341c4366e3f77d5ced4 Mon Sep 17 00:00:00 2001 From: Elena Date: Wed, 15 Mar 2023 11:18:25 +0200 Subject: [PATCH 093/106] Update from @duncancmt without "memory-safe" the IR optimizer produces significantly worse code and it disables the stack limit evader Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com> --- contracts/governance/src/ZeroExVotes.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 3161293393..cd25e749ef 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -305,7 +305,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg * https://github.com/ethereum/solidity/issues/9117 */ function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { - assembly { + assembly ("memory-safe") { mstore(0, ckpts.slot) result.slot := add(keccak256(0, 0x20), pos) } From 15210fadbdd4132c7e138d791f6acc91db49c29a Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Tue, 7 Mar 2023 12:37:25 -0500 Subject: [PATCH 094/106] Add onlyProxy to initializer --- contracts/governance/src/ZeroExVotes.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index cd25e749ef..4946f5dd60 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -45,7 +45,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _; } - function initialize(address _token, uint256 _quadraticThreshold) public initializer { + function initialize(address _token, uint256 _quadraticThreshold) public onlyProxy initializer { __Ownable_init(); __UUPSUpgradeable_init(); From 0c1a7bcde9cc4be7a2efc9711dc622251665baa9 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 15 Mar 2023 12:56:16 +0200 Subject: [PATCH 095/106] Fix quadratic voting weight base --- contracts/governance/src/ZeroExVotes.sol | 4 +-- .../test/ZeroExTreasuryGovernor.t.sol | 12 +++---- .../governance/test/ZeroExVotesTest.t.sol | 36 ++++++++++++------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 4946f5dd60..20f5fc0400 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -265,7 +265,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg if (pos > 0) { uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold ? userBalance - : quadraticThreshold + Math.sqrt(userBalance - quadraticThreshold); + : quadraticThreshold + Math.sqrt((userBalance - quadraticThreshold) * 1e18); oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower); } @@ -273,7 +273,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg uint256 newBalance = op(userBalance, delta); uint256 newQuadraticBalance = newBalance <= quadraticThreshold ? newBalance - : quadraticThreshold + Math.sqrt(newBalance - quadraticThreshold); + : quadraticThreshold + Math.sqrt((newBalance - quadraticThreshold) * 1e18); newQuadraticWeight = oldCkpt.quadraticVotes + newQuadraticBalance; if (pos > 0 && oldCkpt.fromBlock == block.number) { diff --git a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol index 3e91b1802e..2a214c930f 100644 --- a/contracts/governance/test/ZeroExTreasuryGovernor.t.sol +++ b/contracts/governance/test/ZeroExTreasuryGovernor.t.sol @@ -36,9 +36,9 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { vm.roll(3); uint256 totalSupplyQuadraticVotes = quadraticThreshold * 3 + - Math.sqrt(10000000e18 - quadraticThreshold) + - Math.sqrt(2000000e18 - quadraticThreshold) + - Math.sqrt(3000000e18 - quadraticThreshold); + Math.sqrt((10000000e18 - quadraticThreshold) * 1e18) + + Math.sqrt((2000000e18 - quadraticThreshold) * 1e18) + + Math.sqrt((3000000e18 - quadraticThreshold) * 1e18); uint256 quorum = (totalSupplyQuadraticVotes * 10) / 100; assertEq(governor.quorum(2), quorum); } @@ -78,9 +78,9 @@ contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest { // Get vote results (uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId); - assertEq(votesFor, (quadraticThreshold + Math.sqrt(10000000e18 - quadraticThreshold))); - assertEq(votesAgainst, quadraticThreshold + Math.sqrt(2000000e18 - quadraticThreshold)); - assertEq(votesAbstain, quadraticThreshold + Math.sqrt(3000000e18 - quadraticThreshold)); + assertEq(votesFor, (quadraticThreshold + Math.sqrt((10000000e18 - quadraticThreshold) * 1e18))); + assertEq(votesAgainst, quadraticThreshold + Math.sqrt((2000000e18 - quadraticThreshold) * 1e18)); + assertEq(votesAbstain, quadraticThreshold + Math.sqrt((3000000e18 - quadraticThreshold) * 1e18)); IGovernor.ProposalState state = governor.state(proposalId); assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded)); diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 51a544e2c4..b2cc92a2c5 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -67,7 +67,7 @@ contract ZeroExVotesTest is BaseTest { IZeroExVotes.Checkpoint memory checkpoint = votes.checkpoints(account3, 0); assertEq(checkpoint.fromBlock, 2); assertEq(checkpoint.votes, 1700000e18); - assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); + assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18)); } function testShouldBeAbleToSelfDelegateVotingPower() public { @@ -83,7 +83,10 @@ contract ZeroExVotesTest is BaseTest { // Check voting power assertEq(votes.getVotes(account2), 1700000e18); - assertEq(votes.getQuadraticVotes(account2), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); + assertEq( + votes.getQuadraticVotes(account2), + quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + ); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public { @@ -99,7 +102,10 @@ contract ZeroExVotesTest is BaseTest { // Check voting power assertEq(votes.getVotes(account3), 1700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); + assertEq( + votes.getQuadraticVotes(account3), + quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + ); } function testShouldBeAbleToDelegateVotingPowerToAnotherAccountWithSignature() public { @@ -131,7 +137,10 @@ contract ZeroExVotesTest is BaseTest { assertEq(wToken.delegates(account2), account3); assertEq(votes.getVotes(account3), 1700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); + assertEq( + votes.getQuadraticVotes(account3), + quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + ); } function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public { @@ -185,8 +194,8 @@ contract ZeroExVotesTest is BaseTest { votes.getQuadraticVotes(account4), quadraticThreshold * 2 + - Math.sqrt(1700000e18 - quadraticThreshold) + - Math.sqrt(1600000e18 - quadraticThreshold) + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + + Math.sqrt((1600000e18 - quadraticThreshold) * 1e18) ); } @@ -217,8 +226,8 @@ contract ZeroExVotesTest is BaseTest { votes.getQuadraticVotes(account4), quadraticThreshold * 2 + - Math.sqrt(1700000e18 - quadraticThreshold) + - Math.sqrt(1600000e18 - quadraticThreshold) + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + + Math.sqrt((1600000e18 - quadraticThreshold) * 1e18) ); } @@ -254,10 +263,13 @@ contract ZeroExVotesTest is BaseTest { vm.stopPrank(); assertEq(votes.getVotes(account3), 1700000e18); - assertEq(votes.getQuadraticVotes(account3), quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold)); + assertEq( + votes.getQuadraticVotes(account3), + quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + ); // Voting power distribution now is as follows - // account2 -> account3 1700000e18 | 1000000e18 + Math.sqrt(1700000e18 - 1000000e18) + // account2 -> account3 1700000e18 | 1000000e18 + Math.sqrt((1700000e18 - 1000000e18) * 1e18) // account3 -> account4 500000e18 | 500000e18 // Account 3 delegates to itself @@ -268,11 +280,11 @@ contract ZeroExVotesTest is BaseTest { assertEq(votes.getVotes(account3), 2200000e18); assertEq( votes.getQuadraticVotes(account3), - quadraticThreshold + Math.sqrt(1700000e18 - quadraticThreshold) + 500000e18 + quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + 500000e18 ); // Voting power distribution now is as follows - // account2, account3 -> account3 2200000e18 | 1000000e18 + Math.sqrt(2200000e18 - 1000000e18) + 500000e18 + // account2, account3 -> account3 2200000e18 | 1000000e18 + Math.sqrt((2200000e18-1000000e18) *1e18) + 500000e18 // Check account2 and account4 no longer have voting power assertEq(votes.getVotes(account2), 0); From 9c4796990f10b513399bd96563de6503a213eb0e Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 15 Mar 2023 14:59:45 +0200 Subject: [PATCH 096/106] Rename voting parameter for clarity --- contracts/governance/src/ZeroExProtocolGovernor.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index d33141d764..bc76b2ee9c 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -35,13 +35,13 @@ contract ZeroExProtocolGovernor is GovernorTimelockControl { constructor( - IVotes _token, + IVotes _votes, ZeroExTimelock _timelock, address _securityCouncil ) Governor("ZeroExProtocolGovernor") GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 1000000e18) - GovernorVotes(_token) + GovernorVotes(_votes) GovernorTimelockControl(TimelockController(payable(_timelock))) { securityCouncil = _securityCouncil; From 92efa2e31e46bfba69354d5401840a931a7cee3f Mon Sep 17 00:00:00 2001 From: duncancmt <1207590+duncancmt@users.noreply.github.com> Date: Wed, 15 Mar 2023 12:19:09 -0400 Subject: [PATCH 097/106] Make addresses immutable (#680) * Make addresses immutable * Fix linting issues --------- Co-authored-by: elenadimitrova --- contracts/governance/src/ZRXWrappedToken.sol | 2 +- contracts/governance/src/ZeroExVotes.sol | 11 ++++---- contracts/governance/test/BaseTest.t.sol | 28 +++++++++++-------- .../governance/test/ZeroExVotesTest.t.sol | 11 +------- 4 files changed, 23 insertions(+), 29 deletions(-) diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 570f9241d0..5e20970298 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -32,7 +32,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { zeroExVotes = _zeroExVotes; } - IZeroExVotes public zeroExVotes; + IZeroExVotes public immutable zeroExVotes; mapping(address => address) private _delegates; bytes32 private constant _DELEGATION_TYPEHASH = diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 20f5fc0400..0ebfa0d7cd 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -28,13 +28,15 @@ import "@openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol"; import "./IZeroExVotes.sol"; contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpgradeable { - address public token; + address public immutable token; uint256 public quadraticThreshold; mapping(address => Checkpoint[]) private _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; - constructor() { + constructor(address _token) { + require(_token != address(0), "ZeroExVotes: token cannot be 0"); + token = _token; _disableInitializers(); } @@ -45,13 +47,10 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _; } - function initialize(address _token, uint256 _quadraticThreshold) public onlyProxy initializer { + function initialize(uint256 _quadraticThreshold) public onlyProxy initializer { __Ownable_init(); __UUPSUpgradeable_init(); - require(_token != address(0), "ZeroExVotes: token cannot be 0"); - require(token == address(0), "ZeroExVotes: already initialized"); - token = _token; quadraticThreshold = _quadraticThreshold; } diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index e5e35cce33..21504edc62 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -30,6 +30,11 @@ import "../src/ZeroExTimelock.sol"; import "../src/ZeroExProtocolGovernor.sol"; import "../src/ZeroExTreasuryGovernor.sol"; +function predict(address deployer, uint256 nonce) pure returns (address) { + require(nonce > 0 && nonce < 128); + return address(uint160(uint256(keccak256(abi.encodePacked(bytes2(0xd694), deployer, bytes1(uint8(nonce))))))); +} + contract BaseTest is Test { address payable internal account1 = payable(vm.addr(1)); address payable internal account2 = payable(vm.addr(2)); @@ -95,21 +100,20 @@ contract BaseTest is Test { function setupZRXWrappedToken() internal returns (IERC20, ZRXWrappedToken, ZeroExVotes) { vm.startPrank(account1); bytes memory _bytecode = vm.getCode("./ZRXToken.json"); - address _address; + IERC20 zrxToken; assembly { - _address := create(0, add(_bytecode, 0x20), mload(_bytecode)) + zrxToken := create(0, add(_bytecode, 0x20), mload(_bytecode)) } - - IERC20 zrxToken = IERC20(address(_address)); - - ZeroExVotes votes = new ZeroExVotes(); - ERC1967Proxy votesProxy = new ERC1967Proxy(address(votes), new bytes(0)); - votes = ZeroExVotes(address(votesProxy)); - - ZRXWrappedToken token = new ZRXWrappedToken(zrxToken, IZeroExVotes(address(votesProxy))); - votes.initialize(address(token), quadraticThreshold); + address wTokenPrediction = predict(account1, vm.getNonce(account1) + 2); + ZeroExVotes votesImpl = new ZeroExVotes(wTokenPrediction); + ERC1967Proxy votesProxy = new ERC1967Proxy( + address(votesImpl), + abi.encodeCall(votesImpl.initialize, (quadraticThreshold)) + ); + ZRXWrappedToken wToken = new ZRXWrappedToken(zrxToken, ZeroExVotes(address(votesProxy))); vm.stopPrank(); - return (zrxToken, token, votes); + assert(address(wToken) == wTokenPrediction); + return (zrxToken, wToken, ZeroExVotes(address(votesProxy))); } } diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index b2cc92a2c5..724625afbe 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -40,18 +40,9 @@ contract ZeroExVotesTest is BaseTest { assertEq(votes.token(), address(wToken)); } - function testShouldNotBeAbleToInitialiseWithZeroAddressToken() public { - ZeroExVotes _votes = new ZeroExVotes(); - ERC1967Proxy _votesProxy = new ERC1967Proxy(address(_votes), new bytes(0)); - _votes = ZeroExVotes(address(_votesProxy)); - - vm.expectRevert("ZeroExVotes: token cannot be 0"); - _votes.initialize(address(0), quadraticThreshold); - } - function testShouldNotBeAbleToReinitialise() public { vm.expectRevert("Initializable: contract is already initialized"); - votes.initialize(account2, quadraticThreshold); + votes.initialize(quadraticThreshold); } function testShouldBeAbleToReadCheckpoints() public { From b1c83eb3d015aa234d44671294c9c18506d0e4db Mon Sep 17 00:00:00 2001 From: duncancmt <1207590+duncancmt@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:39:46 -0400 Subject: [PATCH 098/106] Prevent griefing by a malicious ZeroExVotes upgrade (#681) * Gas optimization * Minimal change to prevent malicious ZeroExVotes from griefing * Add demonstration of griefing upgrade * Fix rebase issues with tests * Fix prettier issues * Add checks to test --------- Co-authored-by: elenadimitrova --- contracts/governance/src/CallWithGas.sol | 169 ++++++++++++++++++ contracts/governance/src/IZeroExVotes.sol | 12 +- contracts/governance/src/ZRXWrappedToken.sol | 9 +- contracts/governance/src/ZeroExVotes.sol | 15 +- .../governance/test/ZeroExVotesMalicious.sol | 32 ++++ .../governance/test/ZeroExVotesTest.t.sol | 23 +++ 6 files changed, 253 insertions(+), 7 deletions(-) create mode 100644 contracts/governance/src/CallWithGas.sol create mode 100644 contracts/governance/test/ZeroExVotesMalicious.sol diff --git a/contracts/governance/src/CallWithGas.sol b/contracts/governance/src/CallWithGas.sol new file mode 100644 index 0000000000..20441bb8cf --- /dev/null +++ b/contracts/governance/src/CallWithGas.sol @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +library CallWithGas { + /** + * @notice `staticcall` another contract forwarding a precomputed amount of + * gas. + * @dev contains protections against EIP-150-induced insufficient gas + * griefing + * @dev reverts iff the target is not a contract or we encounter an + * out-of-gas + * @return success true iff the call succeded and returned no more than + * `maxReturnBytes` of return data + * @return returnData the return data or revert reason of the call + * @param target the contract (reverts if non-contract) on which to make the + * `staticcall` + * @param data the calldata to pass + * @param callGas the gas to pass for the call. If the call requires more than + * the specified amount of gas and the caller didn't provide at + * least `callGas`, triggers an out-of-gas in the caller. + * @param maxReturnBytes Only this many bytes of return data are read back + * from the call. This prevents griefing the caller. If + * more bytes are returned or the revert reason is + * longer, success will be false and returnData will be + * `abi.encodeWithSignature("Error(string)", "CallWithGas: returnData too long")` + */ + function functionStaticCallWithGas( + address target, + bytes memory data, + uint256 callGas, + uint256 maxReturnBytes + ) internal view returns (bool success, bytes memory returnData) { + assembly ("memory-safe") { + returnData := mload(0x40) + success := staticcall(callGas, target, add(data, 0x20), mload(data), add(returnData, 0x20), maxReturnBytes) + + // As of the time this contract was written, `verbatim` doesn't work in + // inline assembly. Assignment of a value to a variable costs gas + // (although how much is unpredictable because it depends on the Yul/IR + // optimizer), as does the `GAS` opcode itself. Also solc tends to reorder + // the call to `gas()` with preparing the arguments for `div`. Therefore, + // the `gas()` below returns less than the actual amount of gas available + // for computation at the end of the call. That makes this check slightly + // too conservative. However, we do not correct for this because the + // correction would become outdated (possibly too permissive) if the + // opcodes are repriced. + + // https://eips.ethereum.org/EIPS/eip-150 + // https://ronan.eth.link/blog/ethereum-gas-dangers/ + if iszero(or(success, or(returndatasize(), lt(div(callGas, 63), gas())))) { + // The call failed due to not enough gas left. We deliberately consume + // all remaining gas with `invalid` (instead of `revert`) to make this + // failure distinguishable to our caller. + invalid() + } + + switch gt(returndatasize(), maxReturnBytes) + case 0 { + switch returndatasize() + case 0 { + returnData := 0x60 + success := and(success, iszero(iszero(extcodesize(target)))) + } + default { + mstore(returnData, returndatasize()) + mstore(0x40, add(returnData, add(0x20, returndatasize()))) + } + } + default { + // returnData = abi.encodeWithSignature("Error(string)", "CallWithGas: returnData too long") + success := 0 + mstore(returnData, 0) // clear potentially dirty bits + mstore(add(returnData, 0x04), 0x6408c379a0) // length and selector + mstore(add(returnData, 0x24), 0x20) + mstore(add(returnData, 0x44), 0x20) + mstore(add(returnData, 0x64), "CallWithGas: returnData too long") + mstore(0x40, add(returnData, 0x84)) + } + } + } + + /// See `functionCallWithGasAndValue` + function functionCallWithGas( + address target, + bytes memory data, + uint256 callGas, + uint256 maxReturnBytes + ) internal returns (bool success, bytes memory returnData) { + return functionCallWithGasAndValue(payable(target), data, callGas, 0, maxReturnBytes); + } + + /** + * @notice `call` another contract forwarding a precomputed amount of gas. + * @notice Unlike `functionStaticCallWithGas`, a failure is not signaled if + * there is too much return data. Instead, it is simply truncated. + * @dev contains protections against EIP-150-induced insufficient gas griefing + * @dev reverts iff caller doesn't have enough native asset balance, the + * target is not a contract, or due to out-of-gas + * @return success true iff the call succeded + * @return returnData the return data or revert reason of the call + * @param target the contract (reverts if non-contract) on which to make the + * `call` + * @param data the calldata to pass + * @param callGas the gas to pass for the call. If the call requires more than + * the specified amount of gas and the caller didn't provide at + * least `callGas`, triggers an out-of-gas in the caller. + * @param value the amount of the native asset in wei to pass to the callee + * with the call + * @param maxReturnBytes Only this many bytes of return data/revert reason are + * read back from the call. This prevents griefing the + * caller. If more bytes are returned or the revert + * reason is longer, returnData will be truncated + */ + function functionCallWithGasAndValue( + address payable target, + bytes memory data, + uint256 callGas, + uint256 value, + uint256 maxReturnBytes + ) internal returns (bool success, bytes memory returnData) { + if (value > 0 && (address(this).balance < value || target.code.length == 0)) { + return (success, returnData); + } + + assembly ("memory-safe") { + returnData := mload(0x40) + success := call(callGas, target, value, add(data, 0x20), mload(data), add(returnData, 0x20), maxReturnBytes) + + // As of the time this contract was written, `verbatim` doesn't work in + // inline assembly. Assignment of a value to a variable costs gas + // (although how much is unpredictable because it depends on the Yul/IR + // optimizer), as does the `GAS` opcode itself. Also solc tends to reorder + // the call to `gas()` with preparing the arguments for `div`. Therefore, + // the `gas()` below returns less than the actual amount of gas available + // for computation at the end of the call. That makes this check slightly + // too conservative. However, we do not correct for this because the + // correction would become outdated (possibly too permissive) if the + // opcodes are repriced. + + // https://eips.ethereum.org/EIPS/eip-150 + // https://ronan.eth.link/blog/ethereum-gas-dangers/ + if iszero(or(success, or(returndatasize(), lt(div(callGas, 63), gas())))) { + // The call failed due to not enough gas left. We deliberately consume + // all remaining gas with `invalid` (instead of `revert`) to make this + // failure distinguishable to our caller. + invalid() + } + + switch gt(returndatasize(), maxReturnBytes) + case 0 { + switch returndatasize() + case 0 { + returnData := 0x60 + if iszero(value) { + success := and(success, iszero(iszero(extcodesize(target)))) + } + } + default { + mstore(returnData, returndatasize()) + mstore(0x40, add(returnData, add(0x20, returndatasize()))) + } + } + default { + mstore(returnData, maxReturnBytes) + mstore(0x40, add(returnData, add(0x20, maxReturnBytes))) + } + } + } +} diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 168edd5909..4d2ed4d7fc 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -114,9 +114,15 @@ interface IZeroExVotes { * @param dstBalance balance of the delegator whose delegatee is `dst`. This is value _after_ the transfer. * @param amount The amount of tokens transferred from the source delegate to destination delegate. */ - function moveVotingPower(address src, address dst, uint256 srcBalance, uint256 dstBalance, uint256 amount) external; + function moveVotingPower( + address src, + address dst, + uint256 srcBalance, + uint256 dstBalance, + uint256 amount + ) external returns (bool); - function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) external; + function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) external returns (bool); - function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) external; + function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) external returns (bool); } diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 5e20970298..8a572585a9 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -23,8 +23,11 @@ import "@openzeppelin/token/ERC20/extensions/draft-ERC20Permit.sol"; import "@openzeppelin/token/ERC20/extensions/ERC20Wrapper.sol"; import "@openzeppelin/governance/utils/IVotes.sol"; import "./IZeroExVotes.sol"; +import "./CallWithGas.sol"; contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { + using CallWithGas for address; + constructor( IERC20 wrappedToken, IZeroExVotes _zeroExVotes @@ -67,7 +70,11 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { function _burn(address account, uint256 amount) internal override(ERC20) { super._burn(account, amount); - zeroExVotes.writeCheckpointTotalSupplyBurn(balanceOf(account) + amount, amount); + address(zeroExVotes).functionCallWithGas( + abi.encodeCall(zeroExVotes.writeCheckpointTotalSupplyBurn, (balanceOf(account) + amount, amount)), + 500_000, + 32 + ); } /** diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 0ebfa0d7cd..5e01e0c90f 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -137,7 +137,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg uint256 srcBalance, uint256 dstBalance, uint256 amount - ) public onlyToken { + ) public virtual onlyToken returns (bool) { if (src != dst) { if (src != address(0)) { ( @@ -163,12 +163,16 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg emit DelegateQuadraticVotesChanged(dst, oldQuadraticWeight, newQuadraticWeight); } } + return true; } /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) public onlyToken { + function writeCheckpointTotalSupplyMint( + uint256 accountBalance, + uint256 amount + ) public virtual onlyToken returns (bool) { (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( _totalSupplyCheckpoints, _add, @@ -177,12 +181,16 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg ); emit TotalSupplyChanged(newWeight, newQuadraticWeight); + return true; } /** * @inheritdoc IZeroExVotes */ - function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) public onlyToken { + function writeCheckpointTotalSupplyBurn( + uint256 accountBalance, + uint256 amount + ) public virtual onlyToken returns (bool) { (, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint( _totalSupplyCheckpoints, _subtract, @@ -191,6 +199,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg ); emit TotalSupplyChanged(newWeight, newQuadraticWeight); + return true; } /** diff --git a/contracts/governance/test/ZeroExVotesMalicious.sol b/contracts/governance/test/ZeroExVotesMalicious.sol new file mode 100644 index 0000000000..8381a7b617 --- /dev/null +++ b/contracts/governance/test/ZeroExVotesMalicious.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.19; + +import "../src/ZeroExVotes.sol"; + +contract ZeroExVotesMalicious is ZeroExVotes { + constructor(address _token) ZeroExVotes(_token) {} + + function writeCheckpointTotalSupplyBurn( + uint256 amount, + uint256 accountBalance + ) public virtual override onlyToken returns (bool) { + revert("I am evil"); + } +} diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index 724625afbe..e4bd8c62ed 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -19,6 +19,7 @@ pragma solidity ^0.8.19; import "./BaseTest.t.sol"; +import "./ZeroExVotesMalicious.sol"; import "../src/ZRXWrappedToken.sol"; import "@openzeppelin/token/ERC20/ERC20.sol"; @@ -45,6 +46,28 @@ contract ZeroExVotesTest is BaseTest { votes.initialize(quadraticThreshold); } + function testShouldNotBeAbleToStopBurn() public { + // wrap some token + vm.startPrank(account2); + token.approve(address(wToken), 1700000e18); + wToken.depositFor(account2, 1700000e18); + vm.stopPrank(); + assertEq(token.balanceOf(account2), 0); + assertEq(wToken.balanceOf(account2), 1700000e18); + + // malicious upgrade + vm.startPrank(account1); + IZeroExVotes maliciousImpl = new ZeroExVotesMalicious(votes.token()); + votes.upgradeTo(address(maliciousImpl)); + vm.stopPrank(); + + // try to withdraw withdraw + vm.prank(account2); + wToken.withdrawTo(account2, 1700000e18); + assertEq(token.balanceOf(account2), 1700000e18); + assertEq(wToken.balanceOf(account2), 0); + } + function testShouldBeAbleToReadCheckpoints() public { // Account 2 wraps ZRX and delegates voting power to account3 vm.startPrank(account2); From fb6e1f05586715895789ba54a309a37bff44ef9a Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Fri, 17 Mar 2023 17:43:59 +0200 Subject: [PATCH 099/106] Rename SecurityCouncil contract --- contracts/governance/src/IZeroExGovernor.sol | 4 ++-- .../src/{ISecurityCouncil.sol => SecurityCouncil.sol} | 2 +- contracts/governance/src/ZeroExProtocolGovernor.sol | 4 ++-- contracts/governance/src/ZeroExTreasuryGovernor.sol | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename contracts/governance/src/{ISecurityCouncil.sol => SecurityCouncil.sol} (98%) diff --git a/contracts/governance/src/IZeroExGovernor.sol b/contracts/governance/src/IZeroExGovernor.sol index 0e3110e855..bd27dcfcaf 100644 --- a/contracts/governance/src/IZeroExGovernor.sol +++ b/contracts/governance/src/IZeroExGovernor.sol @@ -18,11 +18,11 @@ */ pragma solidity ^0.8.19; -import "./ISecurityCouncil.sol"; +import "./SecurityCouncil.sol"; import "@openzeppelin/governance/IGovernor.sol"; import "@openzeppelin/governance/extensions/IGovernorTimelock.sol"; -abstract contract IZeroExGovernor is ISecurityCouncil, IGovernor, IGovernorTimelock { +abstract contract IZeroExGovernor is SecurityCouncil, IGovernor, IGovernorTimelock { function token() public virtual returns (address); function proposalThreshold() public view virtual returns (uint256); diff --git a/contracts/governance/src/ISecurityCouncil.sol b/contracts/governance/src/SecurityCouncil.sol similarity index 98% rename from contracts/governance/src/ISecurityCouncil.sol rename to contracts/governance/src/SecurityCouncil.sol index 13dd4d14cd..df62ef6269 100644 --- a/contracts/governance/src/ISecurityCouncil.sol +++ b/contracts/governance/src/SecurityCouncil.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.19; -abstract contract ISecurityCouncil { +abstract contract SecurityCouncil { address public securityCouncil; event SecurityCouncilAssigned(address securityCouncil); diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index bc76b2ee9c..0e4ef0f8dd 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -18,7 +18,7 @@ */ pragma solidity ^0.8.19; -import "./ISecurityCouncil.sol"; +import "./SecurityCouncil.sol"; import "./ZeroExTimelock.sol"; import "@openzeppelin/governance/Governor.sol"; import "@openzeppelin/governance/extensions/GovernorSettings.sol"; @@ -27,7 +27,7 @@ import "@openzeppelin/governance/extensions/GovernorVotes.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; contract ZeroExProtocolGovernor is - ISecurityCouncil, + SecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 0df4313995..2936029e89 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -26,10 +26,10 @@ import "@openzeppelin/governance/extensions/GovernorVotesQuorumFraction.sol"; import "@openzeppelin/governance/extensions/GovernorTimelockControl.sol"; import "./IZeroExVotes.sol"; -import "./ISecurityCouncil.sol"; +import "./SecurityCouncil.sol"; contract ZeroExTreasuryGovernor is - ISecurityCouncil, + SecurityCouncil, Governor, GovernorSettings, GovernorCountingSimple, From 4f799cab32cdb5aa2027eb87436dab0d621c0341 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 20 Mar 2023 11:51:37 +0200 Subject: [PATCH 100/106] Add timestamp to delegator balance updates --- contracts/governance/src/IZeroExVotes.sol | 4 ++ contracts/governance/src/ZRXWrappedToken.sol | 55 ++++++++++++++++--- contracts/governance/src/ZeroExVotes.sol | 2 + .../governance/test/ZRXWrappedTokenTest.t.sol | 53 ++++++++++++++++++ 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 4d2ed4d7fc..9bc7df5e06 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -112,6 +112,8 @@ interface IZeroExVotes { * @param dst the delegatee we are moving voting power to * @param srcBalance balance of the delegator whose delegatee is `src`. This is value _after_ the transfer. * @param dstBalance balance of the delegator whose delegatee is `dst`. This is value _after_ the transfer. + * @param srcBalanceLastUpdated timestamp when balance of `src` was last updated. + * @param dstBalanceLastUpdated timestamp when balance of `dst` was last updated. * @param amount The amount of tokens transferred from the source delegate to destination delegate. */ function moveVotingPower( @@ -119,6 +121,8 @@ interface IZeroExVotes { address dst, uint256 srcBalance, uint256 dstBalance, + uint96 srcBalanceLastUpdated, + uint96 dstBalanceLastUpdated, uint256 amount ) external returns (bool); diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index 8a572585a9..d57b468454 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -22,12 +22,18 @@ import "@openzeppelin/token/ERC20/ERC20.sol"; import "@openzeppelin/token/ERC20/extensions/draft-ERC20Permit.sol"; import "@openzeppelin/token/ERC20/extensions/ERC20Wrapper.sol"; import "@openzeppelin/governance/utils/IVotes.sol"; +import "@openzeppelin/utils/math/SafeCast.sol"; import "./IZeroExVotes.sol"; import "./CallWithGas.sol"; contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { using CallWithGas for address; + struct DelegateInfo { + address delegate; + uint96 balanceLastUpdated; + } + constructor( IERC20 wrappedToken, IZeroExVotes _zeroExVotes @@ -36,7 +42,7 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { } IZeroExVotes public immutable zeroExVotes; - mapping(address => address) private _delegates; + mapping(address => DelegateInfo) private _delegates; bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); @@ -55,10 +61,26 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) { super._afterTokenTransfer(from, to, amount); - uint256 fromBalance = delegates(from) == address(0) ? 0 : balanceOf(from) + amount; - uint256 toBalance = delegates(to) == address(0) ? 0 : balanceOf(to) - amount; + DelegateInfo memory fromDelegate = delegateInfo(from); + DelegateInfo memory toDelegate = delegateInfo(to); + + uint256 fromBalance = fromDelegate.delegate == address(0) ? 0 : balanceOf(from) + amount; + uint256 toBalance = toDelegate.delegate == address(0) ? 0 : balanceOf(to) - amount; - zeroExVotes.moveVotingPower(delegates(from), delegates(to), fromBalance, toBalance, amount); + if (fromDelegate.delegate != address(0)) + _delegates[from].balanceLastUpdated = SafeCast.toUint96(block.timestamp); + + if (toDelegate.delegate != address(0)) _delegates[to].balanceLastUpdated = SafeCast.toUint96(block.timestamp); + + zeroExVotes.moveVotingPower( + fromDelegate.delegate, + toDelegate.delegate, + fromBalance, + toBalance, + fromDelegate.balanceLastUpdated, + toDelegate.balanceLastUpdated, + amount + ); } function _mint(address account, uint256 amount) internal override(ERC20) { @@ -81,6 +103,14 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { * @dev Get the address `account` is currently delegating to. */ function delegates(address account) public view returns (address) { + return _delegates[account].delegate; + } + + function delegatorBalanceLastUpdated(address account) public view returns (uint96) { + return _delegates[account].balanceLastUpdated; + } + + function delegateInfo(address account) public view returns (DelegateInfo memory) { return _delegates[account]; } @@ -112,12 +142,21 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { * Emits events {DelegateChanged} and {IZeroExVotes-DelegateVotesChanged}. */ function _delegate(address delegator, address delegatee) internal virtual { - address currentDelegate = delegates(delegator); + DelegateInfo memory delegateInfo = delegateInfo(delegator); uint256 delegatorBalance = balanceOf(delegator); - _delegates[delegator] = delegatee; - emit DelegateChanged(delegator, currentDelegate, delegatee); + _delegates[delegator] = DelegateInfo(delegatee, SafeCast.toUint96(block.timestamp)); + + emit DelegateChanged(delegator, delegateInfo.delegate, delegatee); - zeroExVotes.moveVotingPower(currentDelegate, delegatee, delegatorBalance, 0, delegatorBalance); + zeroExVotes.moveVotingPower( + delegateInfo.delegate, + delegatee, + delegatorBalance, + 0, + delegateInfo.balanceLastUpdated, + 0, + delegatorBalance + ); } } diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 5e01e0c90f..1dfec72790 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -136,6 +136,8 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg address dst, uint256 srcBalance, uint256 dstBalance, + uint96 srcBalanceLastUpdated, + uint96 dstBalanceLastUpdated, uint256 amount ) public virtual onlyToken returns (bool) { if (src != dst) { diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 101c873239..1145c662f6 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -258,4 +258,57 @@ contract ZRXWrappedTokenTest is BaseTest { assertEq(votes.getVotes(account3), 0); assertEq(votes.getQuadraticVotes(account3), 0); } + + function testShouldUpdateVotingPowerWhenDepositing() public { + // Account 2 wraps ZRX and delegates voting power to itself + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 7e18); + wToken.delegate(account2); + + assertEq(votes.getVotes(account2), 7e18); + assertEq(votes.getQuadraticVotes(account2), 7e18); + + wToken.depositFor(account2, 2e18); + assertEq(votes.getVotes(account2), 9e18); + assertEq(votes.getQuadraticVotes(account2), 9e18); + } + + function testShouldUpdateVotingPowerWhenWithdrawing() public { + // Account 2 wraps ZRX and delegates voting power to itself + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + wToken.delegate(account2); + + assertEq(votes.getVotes(account2), 10e18); + assertEq(votes.getQuadraticVotes(account2), 10e18); + + wToken.withdrawTo(account2, 2e18); + assertEq(votes.getVotes(account2), 8e18); + assertEq(votes.getQuadraticVotes(account2), 8e18); + } + + function testShouldSetDelegateBalanceLastUpdatedOnTransfer() public { + ZRXWrappedToken.DelegateInfo memory account2DelegateInfo = wToken.delegateInfo(account2); + assertEq(account2DelegateInfo.delegate, address(0)); + assertEq(account2DelegateInfo.balanceLastUpdated, 0); + + // Account 2 wraps ZRX and delegates voting power to account3 + vm.startPrank(account2); + token.approve(address(wToken), 10e18); + wToken.depositFor(account2, 10e18); + wToken.delegate(account3); + + account2DelegateInfo = wToken.delegateInfo(account2); + assertEq(account2DelegateInfo.delegate, account3); + assertEq(account2DelegateInfo.balanceLastUpdated, block.timestamp); + + vm.warp(101); + wToken.transfer(account3, 3e18); + + account2DelegateInfo = wToken.delegateInfo(account2); + assertEq(account2DelegateInfo.delegate, account3); + assertEq(account2DelegateInfo.balanceLastUpdated, 101); + } } From 307027650a7561b58d37008185f26b4d8091f45b Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 20 Mar 2023 09:39:51 -0400 Subject: [PATCH 101/106] Make quadraticThreshold `immutable` for gas efficiency --- contracts/governance/src/ZeroExVotes.sol | 9 ++++----- contracts/governance/test/BaseTest.t.sol | 7 ++----- contracts/governance/test/ZeroExVotesMalicious.sol | 2 +- contracts/governance/test/ZeroExVotesTest.t.sol | 4 ++-- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 1dfec72790..7c8a70fd8e 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -29,14 +29,15 @@ import "./IZeroExVotes.sol"; contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpgradeable { address public immutable token; - uint256 public quadraticThreshold; + uint256 public immutable quadraticThreshold; mapping(address => Checkpoint[]) private _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; - constructor(address _token) { + constructor(address _token, uint256 _quadraticThreshold) { require(_token != address(0), "ZeroExVotes: token cannot be 0"); token = _token; + quadraticThreshold = _quadraticThreshold; _disableInitializers(); } @@ -47,11 +48,9 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _; } - function initialize(uint256 _quadraticThreshold) public onlyProxy initializer { + function initialize() public onlyProxy initializer { __Ownable_init(); __UUPSUpgradeable_init(); - - quadraticThreshold = _quadraticThreshold; } /** diff --git a/contracts/governance/test/BaseTest.t.sol b/contracts/governance/test/BaseTest.t.sol index 21504edc62..05644bf8cf 100644 --- a/contracts/governance/test/BaseTest.t.sol +++ b/contracts/governance/test/BaseTest.t.sol @@ -105,11 +105,8 @@ contract BaseTest is Test { zrxToken := create(0, add(_bytecode, 0x20), mload(_bytecode)) } address wTokenPrediction = predict(account1, vm.getNonce(account1) + 2); - ZeroExVotes votesImpl = new ZeroExVotes(wTokenPrediction); - ERC1967Proxy votesProxy = new ERC1967Proxy( - address(votesImpl), - abi.encodeCall(votesImpl.initialize, (quadraticThreshold)) - ); + ZeroExVotes votesImpl = new ZeroExVotes(wTokenPrediction, quadraticThreshold); + ERC1967Proxy votesProxy = new ERC1967Proxy(address(votesImpl), abi.encodeCall(votesImpl.initialize, ())); ZRXWrappedToken wToken = new ZRXWrappedToken(zrxToken, ZeroExVotes(address(votesProxy))); vm.stopPrank(); diff --git a/contracts/governance/test/ZeroExVotesMalicious.sol b/contracts/governance/test/ZeroExVotesMalicious.sol index 8381a7b617..bfbcc2b7d1 100644 --- a/contracts/governance/test/ZeroExVotesMalicious.sol +++ b/contracts/governance/test/ZeroExVotesMalicious.sol @@ -21,7 +21,7 @@ pragma solidity ^0.8.19; import "../src/ZeroExVotes.sol"; contract ZeroExVotesMalicious is ZeroExVotes { - constructor(address _token) ZeroExVotes(_token) {} + constructor(address _token, uint256 _quadraticThreshold) ZeroExVotes(_token, _quadraticThreshold) {} function writeCheckpointTotalSupplyBurn( uint256 amount, diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index e4bd8c62ed..d04e528b69 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -43,7 +43,7 @@ contract ZeroExVotesTest is BaseTest { function testShouldNotBeAbleToReinitialise() public { vm.expectRevert("Initializable: contract is already initialized"); - votes.initialize(quadraticThreshold); + votes.initialize(); } function testShouldNotBeAbleToStopBurn() public { @@ -57,7 +57,7 @@ contract ZeroExVotesTest is BaseTest { // malicious upgrade vm.startPrank(account1); - IZeroExVotes maliciousImpl = new ZeroExVotesMalicious(votes.token()); + IZeroExVotes maliciousImpl = new ZeroExVotesMalicious(votes.token(), votes.quadraticThreshold()); votes.upgradeTo(address(maliciousImpl)); vm.stopPrank(); From 538c61f9f34f366fa8253a23f27e09ca04478c4c Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Mon, 20 Mar 2023 19:46:00 +0200 Subject: [PATCH 102/106] Remove the logic for ejecting security council --- contracts/governance/src/ZeroExProtocolGovernor.sol | 4 ---- contracts/governance/src/ZeroExTreasuryGovernor.sol | 2 -- contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 4 +++- contracts/governance/test/ZeroExProtocolGovernor.t.sol | 7 +++++-- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/contracts/governance/src/ZeroExProtocolGovernor.sol b/contracts/governance/src/ZeroExProtocolGovernor.sol index 0e4ef0f8dd..7fe283b5ed 100644 --- a/contracts/governance/src/ZeroExProtocolGovernor.sol +++ b/contracts/governance/src/ZeroExProtocolGovernor.sol @@ -85,8 +85,6 @@ contract ZeroExProtocolGovernor is bytes32 descriptionHash ) public override onlySecurityCouncil { _cancel(targets, values, calldatas, descriptionHash); - - ejectSecurityCouncil(); } // Like the GovernorTimelockControl.queue function but without the proposal checks, @@ -103,8 +101,6 @@ contract ZeroExProtocolGovernor is // Execute the batch of rollbacks via the timelock controller ZeroExTimelock timelockController = ZeroExTimelock(payable(timelock())); timelockController.executeRollbackBatch(targets, values, calldatas, 0, descriptionHash); - - ejectSecurityCouncil(); } function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance { diff --git a/contracts/governance/src/ZeroExTreasuryGovernor.sol b/contracts/governance/src/ZeroExTreasuryGovernor.sol index 2936029e89..fca907e898 100644 --- a/contracts/governance/src/ZeroExTreasuryGovernor.sol +++ b/contracts/governance/src/ZeroExTreasuryGovernor.sol @@ -108,8 +108,6 @@ contract ZeroExTreasuryGovernor is bytes32 descriptionHash ) public override onlySecurityCouncil { _cancel(targets, values, calldatas, descriptionHash); - - ejectSecurityCouncil(); } function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance { diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 3698180cdd..82af6edf6e 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -177,7 +177,9 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { governor.assignSecurityCouncil(account1); } - function testSecurityCouncilAreEjectedAfterCancellingAProposal() public { + // This functionality is currently not enabled + // Leaving this test for potential future use. + function SecurityCouncilAreEjectedAfterCancellingAProposal() public { // Create a proposal address[] memory targets = new address[](1); targets[0] = address(callReceiverMock); diff --git a/contracts/governance/test/ZeroExProtocolGovernor.t.sol b/contracts/governance/test/ZeroExProtocolGovernor.t.sol index 6a863e6a4a..e6c34bcad1 100644 --- a/contracts/governance/test/ZeroExProtocolGovernor.t.sol +++ b/contracts/governance/test/ZeroExProtocolGovernor.t.sol @@ -123,8 +123,11 @@ contract ZeroExProtocolGovernorTest is ZeroExGovernorBaseTest { vm.expectEmit(true, true, true, true); emit CallExecuted(proposalId, 0, targets[0], values[0], calldatas[0]); - vm.expectEmit(true, false, false, false); - emit SecurityCouncilEjected(); + // This functionality is currently not enabled + // Leaving this test for potential future use. + // vm.expectEmit(true, false, false, false); + // emit SecurityCouncilEjected(); + protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback"))); } From 6410ffcc1c18006101a4b5141e260ac4f4907287 Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Tue, 21 Mar 2023 18:27:35 +0200 Subject: [PATCH 103/106] Switch balance timestamp to be a block number --- contracts/governance/src/IZeroExVotes.sol | 4 ++-- contracts/governance/src/ZRXWrappedToken.sol | 8 +++++--- contracts/governance/test/ZRXWrappedTokenTest.t.sol | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/contracts/governance/src/IZeroExVotes.sol b/contracts/governance/src/IZeroExVotes.sol index 9bc7df5e06..46242b1ed8 100644 --- a/contracts/governance/src/IZeroExVotes.sol +++ b/contracts/governance/src/IZeroExVotes.sol @@ -112,8 +112,8 @@ interface IZeroExVotes { * @param dst the delegatee we are moving voting power to * @param srcBalance balance of the delegator whose delegatee is `src`. This is value _after_ the transfer. * @param dstBalance balance of the delegator whose delegatee is `dst`. This is value _after_ the transfer. - * @param srcBalanceLastUpdated timestamp when balance of `src` was last updated. - * @param dstBalanceLastUpdated timestamp when balance of `dst` was last updated. + * @param srcBalanceLastUpdated block number when balance of `src` was last updated. + * @param dstBalanceLastUpdated block number when balance of `dst` was last updated. * @param amount The amount of tokens transferred from the source delegate to destination delegate. */ function moveVotingPower( diff --git a/contracts/governance/src/ZRXWrappedToken.sol b/contracts/governance/src/ZRXWrappedToken.sol index d57b468454..35731599f6 100644 --- a/contracts/governance/src/ZRXWrappedToken.sol +++ b/contracts/governance/src/ZRXWrappedToken.sol @@ -67,10 +67,9 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { uint256 fromBalance = fromDelegate.delegate == address(0) ? 0 : balanceOf(from) + amount; uint256 toBalance = toDelegate.delegate == address(0) ? 0 : balanceOf(to) - amount; - if (fromDelegate.delegate != address(0)) - _delegates[from].balanceLastUpdated = SafeCast.toUint96(block.timestamp); + if (fromDelegate.delegate != address(0)) _delegates[from].balanceLastUpdated = SafeCast.toUint96(block.number); - if (toDelegate.delegate != address(0)) _delegates[to].balanceLastUpdated = SafeCast.toUint96(block.timestamp); + if (toDelegate.delegate != address(0)) _delegates[to].balanceLastUpdated = SafeCast.toUint96(block.number); zeroExVotes.moveVotingPower( fromDelegate.delegate, @@ -106,6 +105,9 @@ contract ZRXWrappedToken is ERC20, ERC20Permit, ERC20Wrapper { return _delegates[account].delegate; } + /** + * @dev Get the last block number when `account`'s balance changed. + */ function delegatorBalanceLastUpdated(address account) public view returns (uint96) { return _delegates[account].balanceLastUpdated; } diff --git a/contracts/governance/test/ZRXWrappedTokenTest.t.sol b/contracts/governance/test/ZRXWrappedTokenTest.t.sol index 1145c662f6..a39dff9c31 100644 --- a/contracts/governance/test/ZRXWrappedTokenTest.t.sol +++ b/contracts/governance/test/ZRXWrappedTokenTest.t.sol @@ -302,13 +302,13 @@ contract ZRXWrappedTokenTest is BaseTest { account2DelegateInfo = wToken.delegateInfo(account2); assertEq(account2DelegateInfo.delegate, account3); - assertEq(account2DelegateInfo.balanceLastUpdated, block.timestamp); + assertEq(account2DelegateInfo.balanceLastUpdated, 1); // Set to the block.number - vm.warp(101); + vm.roll(3); wToken.transfer(account3, 3e18); account2DelegateInfo = wToken.delegateInfo(account2); assertEq(account2DelegateInfo.delegate, account3); - assertEq(account2DelegateInfo.balanceLastUpdated, 101); + assertEq(account2DelegateInfo.balanceLastUpdated, 3); } } From 62fe71bf83a54886027fed44a7851889672e1eba Mon Sep 17 00:00:00 2001 From: duncancmt <1207590+duncancmt@users.noreply.github.com> Date: Wed, 22 Mar 2023 05:53:45 -0400 Subject: [PATCH 104/106] Test votes migration for adding a new vote weight mechanism (#674) * Add Emacs files to .gitignore * Make some functions unproected to demonstrate a migration * Add example (broken) migration * Add migration test for voting logic * Try to simplify tests * Fix compilation errors * Fix underflow test with new logic * Flesh out migration test for voting * Replace cube root library * Fix stack too deep in coverage --------- Co-authored-by: elenadimitrova --- .gitignore | 3 + contracts/governance/src/ZeroExVotes.sol | 21 +- contracts/governance/test/CubeRoot.sol | 21 ++ .../governance/test/ZeroExVotesMigration.sol | 215 ++++++++++++++++++ .../governance/test/ZeroExVotesTest.t.sol | 63 ++++- 5 files changed, 315 insertions(+), 8 deletions(-) create mode 100644 contracts/governance/test/CubeRoot.sol create mode 100644 contracts/governance/test/ZeroExVotesMigration.sol diff --git a/.gitignore b/.gitignore index 8da666fbfb..565b6f4f58 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,9 @@ contracts/governance/out packages/*/docs/README.md .DS_Store +*~ +\#*\# +.\#* # the snapshot that gets built for migrations sure does have a ton of files packages/migrations/0x_ganache_snapshot* diff --git a/contracts/governance/src/ZeroExVotes.sol b/contracts/governance/src/ZeroExVotes.sol index 7c8a70fd8e..52689d836d 100644 --- a/contracts/governance/src/ZeroExVotes.sol +++ b/contracts/governance/src/ZeroExVotes.sol @@ -31,7 +31,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg address public immutable token; uint256 public immutable quadraticThreshold; - mapping(address => Checkpoint[]) private _checkpoints; + mapping(address => Checkpoint[]) internal _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; constructor(address _token, uint256 _quadraticThreshold) { @@ -48,7 +48,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _; } - function initialize() public onlyProxy initializer { + function initialize() public virtual onlyProxy initializer { __Ownable_init(); __UUPSUpgradeable_init(); } @@ -146,7 +146,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight - ) = _writeCheckpoint(_checkpoints[src], _subtract, srcBalance, amount); + ) = _writeCheckpoint(_checkpoints[src], _subtract, srcBalance, srcBalanceLastUpdated, amount); emit DelegateVotesChanged(src, oldWeight, newWeight); emit DelegateQuadraticVotesChanged(src, oldQuadraticWeight, newQuadraticWeight); @@ -158,7 +158,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight - ) = _writeCheckpoint(_checkpoints[dst], _add, dstBalance, amount); + ) = _writeCheckpoint(_checkpoints[dst], _add, dstBalance, dstBalanceLastUpdated, amount); emit DelegateVotesChanged(dst, oldWeight, newWeight); emit DelegateQuadraticVotesChanged(dst, oldQuadraticWeight, newQuadraticWeight); @@ -178,6 +178,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _totalSupplyCheckpoints, _add, accountBalance, + 0, amount ); @@ -196,6 +197,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg _totalSupplyCheckpoints, _subtract, accountBalance, + 0, amount ); @@ -211,7 +213,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg function _checkpointsLookup( Checkpoint[] storage ckpts, uint256 blockNumber - ) private view returns (Checkpoint memory) { + ) internal view returns (Checkpoint memory) { // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. // // Initially we check if the block is recent to narrow the search range. @@ -258,8 +260,13 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg Checkpoint[] storage ckpts, function(uint256, uint256) view returns (uint256) op, uint256 userBalance, + uint96 balanceLastUpdated, uint256 delta - ) private returns (uint256 oldWeight, uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight) { + ) + internal + virtual + returns (uint256 oldWeight, uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight) + { uint256 pos = ckpts.length; Checkpoint memory oldCkpt = pos == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, pos - 1); @@ -313,7 +320,7 @@ contract ZeroExVotes is IZeroExVotes, Initializable, OwnableUpgradeable, UUPSUpg * Implementation from openzeppelin/token/ERC20/extensions/ERC20Votes.sol * https://github.com/ethereum/solidity/issues/9117 */ - function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) private pure returns (Checkpoint storage result) { + function _unsafeAccess(Checkpoint[] storage ckpts, uint256 pos) internal pure returns (Checkpoint storage result) { assembly ("memory-safe") { mstore(0, ckpts.slot) result.slot := add(keccak256(0, 0x20), pos) diff --git a/contracts/governance/test/CubeRoot.sol b/contracts/governance/test/CubeRoot.sol new file mode 100644 index 0000000000..34ba9a8d3d --- /dev/null +++ b/contracts/governance/test/CubeRoot.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +library CubeRoot { + /// @dev Returns the cube root of `x`. + /// Credit to pleasemarkdarkly under MIT license + // Originaly from https://github.com/pleasemarkdarkly/fei-protocol-core-hh/blob/main/contracts/utils/Roots.sol + function cbrt(uint y) internal pure returns (uint z) { + // Newton's method https://en.wikipedia.org/wiki/Cube_root#Numerical_methods + if (y > 7) { + z = y; + uint x = y / 3 + 1; + while (x < z) { + z = x; + x = (y / (x * x) + (2 * x)) / 3; + } + } else if (y != 0) { + z = 1; + } + } +} diff --git a/contracts/governance/test/ZeroExVotesMigration.sol b/contracts/governance/test/ZeroExVotesMigration.sol new file mode 100644 index 0000000000..2dc2dc9cda --- /dev/null +++ b/contracts/governance/test/ZeroExVotesMigration.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + + Copyright 2023 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pragma solidity ^0.8.19; + +import {ZeroExVotes} from "../src/ZeroExVotes.sol"; +import {SafeCast} from "@openzeppelin/utils/math/SafeCast.sol"; +import {Math} from "@openzeppelin/utils/math/Math.sol"; +import {CubeRoot} from "./CubeRoot.sol"; + +contract ZeroExVotesMigration is ZeroExVotes { + uint32 public migrationBlock; + + constructor(address _token, uint256 _quadraticThreshold) ZeroExVotes(_token, _quadraticThreshold) {} + + function initialize() public virtual override onlyProxy reinitializer(2) { + migrationBlock = uint32(block.number); + } + + struct CheckpointMigration { + uint32 fromBlock; + uint96 votes; + uint96 quadraticVotes; + uint32 migratedVotes; + } + + function _toMigration(Checkpoint storage ckpt) internal pure returns (CheckpointMigration storage result) { + assembly { + result.slot := ckpt.slot + } + } + + function _toMigration(Checkpoint[] storage ckpt) internal pure returns (CheckpointMigration[] storage result) { + assembly { + result.slot := ckpt.slot + } + } + + function getMigratedVotes(address account) public view returns (uint256) { + uint256 pos = _checkpoints[account].length; + if (pos == 0) { + return 0; + } + Checkpoint storage ckpt = _unsafeAccess(_checkpoints[account], pos - 1); + if (ckpt.fromBlock <= migrationBlock) { + return 0; + } + return _toMigration(ckpt).migratedVotes; + } + + function getPastMigratedVotes(address account, uint256 blockNumber) public view returns (uint256) { + require(blockNumber < block.number, "ZeroExVotesMigration: block not yet mined"); + if (blockNumber <= migrationBlock) { + return 0; + } + + Checkpoint storage checkpoint = _checkpointsLookupStorage(_checkpoints[account], blockNumber); + if (checkpoint.fromBlock <= migrationBlock) { + return 0; + } + return _toMigration(checkpoint).migratedVotes; + } + + function _checkpointsLookupStorage( + Checkpoint[] storage ckpts, + uint256 blockNumber + ) internal view returns (Checkpoint storage result) { + // We run a binary search to look for the earliest checkpoint taken after `blockNumber`. + // + // Initially we check if the block is recent to narrow the search range. + // During the loop, the index of the wanted checkpoint remains in the range [low-1, high). + // With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the + // invariant. + // - If the middle checkpoint is after `blockNumber`, we look in [low, mid) + // - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high) + // Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not + // out of bounds (in which case we're looking too far in the past and the result is 0). + // Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is + // past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out + // the same. + uint256 length = ckpts.length; + + uint256 low = 0; + uint256 high = length; + + if (length > 5) { + uint256 mid = length - Math.sqrt(length); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + while (low < high) { + uint256 mid = Math.average(low, high); + if (_unsafeAccess(ckpts, mid).fromBlock > blockNumber) { + high = mid; + } else { + low = mid + 1; + } + } + + // Leaving here for posterity this is the original OZ implementation which we've replaced + // return high == 0 ? 0 : _unsafeAccess(ckpts, high - 1).votes; + // Checkpoint memory checkpoint = high == 0 ? Checkpoint(0, 0, 0) : _unsafeAccess(ckpts, high - 1); + // return checkpoint; + // TODO: bad. very bad. only works on accident + if (high > 0) { + result = _unsafeAccess(ckpts, high - 1); + } else { + // suppress compiler warning, which really shouldn't be suppressed + assembly { + result.slot := 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF + } + } + } + + // TODO: we're not handling totalSupply + + // TODO: need to return the migrated weight + function _writeCheckpoint( + Checkpoint[] storage ckpts, + function(uint256, uint256) view returns (uint256) op, + uint256 userBalance, + uint96 balanceLastUpdated, + uint256 delta + ) + internal + virtual + override + returns (uint256 oldWeight, uint256 newWeight, uint256 oldQuadraticWeight, uint256 newQuadraticWeight) + { + uint256 pos = ckpts.length; + + CheckpointMigration memory oldCkpt = pos == 0 + ? CheckpointMigration(0, 0, 0, 0) + : _toMigration(_unsafeAccess(ckpts, pos - 1)); + + oldWeight = oldCkpt.votes; + newWeight = op(oldWeight, delta); + + oldQuadraticWeight = oldCkpt.quadraticVotes; + + if (pos > 0) { + deductOldWeightFromCheckpoint(oldCkpt, userBalance, balanceLastUpdated); + } + + // if wallet > threshold, calculate quadratic power over the treshold only, below threshold is linear + uint256 newBalance = op(userBalance, delta); + uint256 newQuadraticBalance = newBalance <= quadraticThreshold + ? newBalance + : quadraticThreshold + Math.sqrt((newBalance - quadraticThreshold) * 1e18); + newQuadraticWeight = oldCkpt.quadraticVotes + newQuadraticBalance; + uint256 newMigratedWeight = oldCkpt.migratedVotes + CubeRoot.cbrt(newBalance); + + if (pos > 0 && oldCkpt.fromBlock == block.number) { + addCheckpoint(ckpts, pos, newWeight, newQuadraticWeight, newMigratedWeight); + } else { + _toMigration(ckpts).push( + CheckpointMigration({ + fromBlock: SafeCast.toUint32(block.number), + votes: SafeCast.toUint96(newWeight), + quadraticVotes: SafeCast.toUint96(newQuadraticWeight), + migratedVotes: SafeCast.toUint32(newMigratedWeight) + }) + ); + } + } + + function deductOldWeightFromCheckpoint( + CheckpointMigration memory oldCkpt, + uint256 userBalance, + uint96 balanceLastUpdated + ) internal { + // Remove the entire sqrt userBalance from quadratic voting power. + // Note that `userBalance` is value _after_ transfer. + uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold + ? userBalance + : quadraticThreshold + Math.sqrt((userBalance - quadraticThreshold) * 1e18); + oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower); + + if (oldCkpt.fromBlock > migrationBlock && balanceLastUpdated > migrationBlock) { + oldCkpt.migratedVotes -= SafeCast.toUint32(CubeRoot.cbrt(userBalance)); + } + } + + function addCheckpoint( + Checkpoint[] storage ckpts, + uint256 pos, + uint256 newWeight, + uint256 newQuadraticWeight, + uint256 newMigratedWeight + ) internal { + CheckpointMigration storage chpt = _toMigration(_unsafeAccess(ckpts, pos - 1)); + chpt.votes = SafeCast.toUint96(newWeight); + chpt.quadraticVotes = SafeCast.toUint96(newQuadraticWeight); + chpt.migratedVotes = SafeCast.toUint32(newMigratedWeight); + } +} diff --git a/contracts/governance/test/ZeroExVotesTest.t.sol b/contracts/governance/test/ZeroExVotesTest.t.sol index d04e528b69..97729f0c46 100644 --- a/contracts/governance/test/ZeroExVotesTest.t.sol +++ b/contracts/governance/test/ZeroExVotesTest.t.sol @@ -18,10 +18,11 @@ */ pragma solidity ^0.8.19; +import "@openzeppelin/token/ERC20/ERC20.sol"; import "./BaseTest.t.sol"; import "./ZeroExVotesMalicious.sol"; +import "./ZeroExVotesMigration.sol"; import "../src/ZRXWrappedToken.sol"; -import "@openzeppelin/token/ERC20/ERC20.sol"; contract ZeroExVotesTest is BaseTest { IERC20 private token; @@ -46,6 +47,66 @@ contract ZeroExVotesTest is BaseTest { votes.initialize(); } + function testShouldBeAbleToMigrate() public { + vm.roll(block.number + 1); + + vm.startPrank(account2); + token.approve(address(wToken), 100e18); + wToken.depositFor(account2, 100e18); + wToken.delegate(account3); + vm.stopPrank(); + + vm.startPrank(account3); + token.approve(address(wToken), 200e18); + wToken.depositFor(account3, 200e18); + wToken.delegate(account3); + vm.stopPrank(); + + assertEq(votes.getVotes(account3), 300e18); + assertEq(votes.getQuadraticVotes(account3), 300e18); + + vm.roll(block.number + 1); + + ZeroExVotesMigration newImpl = new ZeroExVotesMigration(address(wToken), quadraticThreshold); + assertFalse( + address( + uint160( + uint256(vm.load(address(votes), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)) + ) + ) == address(newImpl) + ); + vm.prank(account1); + votes.upgradeToAndCall(address(newImpl), abi.encodeWithSignature("initialize()")); + assertEq( + address( + uint160( + uint256(vm.load(address(votes), 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc)) + ) + ), + address(newImpl) + ); + + ZeroExVotesMigration upgradedVotes = ZeroExVotesMigration(address(votes)); + + assertEq(upgradedVotes.getVotes(account3), 300e18); + assertEq(upgradedVotes.getQuadraticVotes(account3), 300e18); + + vm.roll(block.number + 1); + + vm.prank(account2); + wToken.transfer(address(this), 50e18); + + assertEq(upgradedVotes.getVotes(account3), 250e18); + assertEq(upgradedVotes.getQuadraticVotes(account3), 250e18); + assertEq(upgradedVotes.getMigratedVotes(account3), CubeRoot.cbrt(50e18)); + + vm.prank(account3); + wToken.transfer(address(this), 100e18); + assertEq(upgradedVotes.getVotes(account3), 150e18); + assertEq(upgradedVotes.getQuadraticVotes(account3), 150e18); + assertEq(upgradedVotes.getMigratedVotes(account3), CubeRoot.cbrt(50e18) + CubeRoot.cbrt(100e18)); + } + function testShouldNotBeAbleToStopBurn() public { // wrap some token vm.startPrank(account2); From d6c0c029f1e56647dd64342b51dfbe565528faac Mon Sep 17 00:00:00 2001 From: elenadimitrova Date: Wed, 22 Mar 2023 13:09:39 +0200 Subject: [PATCH 105/106] Change test case to testFail --- contracts/governance/test/ZeroExGovernorBaseTest.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol index 82af6edf6e..05e82b5f17 100644 --- a/contracts/governance/test/ZeroExGovernorBaseTest.t.sol +++ b/contracts/governance/test/ZeroExGovernorBaseTest.t.sol @@ -179,7 +179,7 @@ abstract contract ZeroExGovernorBaseTest is BaseTest { // This functionality is currently not enabled // Leaving this test for potential future use. - function SecurityCouncilAreEjectedAfterCancellingAProposal() public { + function testFailSecurityCouncilAreEjectedAfterCancellingAProposal() public { // Create a proposal address[] memory targets = new address[](1); targets[0] = address(callReceiverMock); From 8d83419466666a1a8700a73375b34fba5c19548f Mon Sep 17 00:00:00 2001 From: Elena Date: Wed, 22 Mar 2023 14:44:31 +0200 Subject: [PATCH 106/106] Update contracts/governance/test/ZeroExVotesMigration.sol Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com> --- contracts/governance/test/ZeroExVotesMigration.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/governance/test/ZeroExVotesMigration.sol b/contracts/governance/test/ZeroExVotesMigration.sol index 2dc2dc9cda..318078d3fb 100644 --- a/contracts/governance/test/ZeroExVotesMigration.sol +++ b/contracts/governance/test/ZeroExVotesMigration.sol @@ -195,7 +195,7 @@ contract ZeroExVotesMigration is ZeroExVotes { : quadraticThreshold + Math.sqrt((userBalance - quadraticThreshold) * 1e18); oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower); - if (oldCkpt.fromBlock > migrationBlock && balanceLastUpdated > migrationBlock) { + if (balanceLastUpdated > migrationBlock) { oldCkpt.migratedVotes -= SafeCast.toUint32(CubeRoot.cbrt(userBalance)); } }