Compare commits

...

81 Commits

Author SHA1 Message Date
wonge97
b319a4dceb
[protocol] Add file for OP RPGF Round 4 Funding (#780) 2024-05-24 15:53:41 -04:00
github-actions[bot]
e66307ba31
Publish: CHANGELOG and Package Version Updates into development (#763)
* Updated CHANGELOGS & MD docs

* Publish

 - @0x/contracts-erc20@4.0.14
 - @0x/contracts-test-utils@5.4.60
 - @0x/contracts-treasury@1.4.54
 - @0x/contracts-utils@4.8.52
 - @0x/contracts-zero-ex@0.49.0
 - @0x/contract-addresses@8.13.0
 - @0x/contract-wrappers@13.23.8
 - @0x/protocol-utils@11.24.2

---------

Co-authored-by: Github Actions <github-actions@github.com>
2023-11-15 16:38:36 -08:00
Kyu
652d422622
Add Sepoia addresses (#761) 2023-11-15 15:24:11 -08:00
Kyu
5207565af8
Temporarily disable erc20 and governance in ci (#762) 2023-11-15 14:30:44 -08:00
wonge97
d982d75e2e
Update docs with missing chains that 0x protocol is deployed on (#760)
* Update docs with missing chains that 0x protocol is deployed on
2023-11-13 12:58:46 -06:00
David Walsh
c5650da703
[contract-addresses] Add ChainId type predicate (#756) 2023-11-02 16:30:05 -06:00
github-actions[bot]
b19e29e03d
Publish: CHANGELOG and Package Version Updates into development (#748)
* Updated CHANGELOGS & MD docs

* Publish

 - @0x/contracts-erc20@4.0.13
 - @0x/contracts-test-utils@5.4.59
 - @0x/contracts-treasury@1.4.53
 - @0x/contracts-utils@4.8.51
 - @0x/contracts-zero-ex@0.48.0
 - @0x/contract-addresses@8.12.0
 - @0x/contract-wrappers@13.23.7
 - @0x/protocol-utils@11.24.1

---------

Co-authored-by: Github Actions <github-actions@github.com>
2023-08-29 15:10:18 -07:00
Kyu
e5cbfd87bc
Add Velodrome V2 support on Base (#747) 2023-08-29 14:43:23 -07:00
Savarn Dontamsetti (Sav)
5a49be1322
Create PRs to reflect publish changes (#743)
* Create PRs to reflect publish changes

* Using gh instead of third party action

* Adding github token
2023-08-24 16:35:11 -04:00
Github Actions
ec336b92aa Publish
- @0x/contracts-erc20@4.0.12
 - @0x/contracts-test-utils@5.4.58
 - @0x/contracts-treasury@1.4.52
 - @0x/contracts-utils@4.8.50
 - @0x/contracts-zero-ex@0.47.0
 - @0x/contract-addresses@8.11.0
 - @0x/contract-wrappers@13.23.6
 - @0x/protocol-utils@11.24.0
2023-08-18 14:24:32 +00:00
Github Actions
62a530d7ec Updated CHANGELOGS & MD docs 2023-08-18 14:24:27 +00:00
Savarn Dontamsetti (Sav)
09d6fbce81
Rename Maverick to MaverickV1 and add tests (#742)
* Renaming Maverick and adding tests

* Update Base chain ID
2023-08-17 17:11:05 -04:00
Savarn Dontamsetti (Sav)
7f324dd75d
feat/Add Maverick V1 Support (#741)
* Add Maverick V1 Support for Ethereum, BSC, and Base
2023-08-17 12:59:38 -04:00
Github Actions
e1f9e107e9 Publish
- @0x/contracts-erc20@4.0.11
 - @0x/contracts-test-utils@5.4.57
 - @0x/contracts-treasury@1.4.51
 - @0x/contracts-utils@4.8.49
 - @0x/contracts-zero-ex@0.46.0
 - @0x/contract-addresses@8.10.0
 - @0x/contract-wrappers@13.23.5
 - @0x/protocol-utils@11.23.0
2023-08-09 21:43:27 +00:00
Github Actions
3c98225720 Updated CHANGELOGS & MD docs 2023-08-09 21:43:23 +00:00
Savarn Dontamsetti (Sav)
19e0c3364a
Add Velodrome V2 compatibility test to Optimism Bridge Adapter Test (#740)
* Add Velodrome V2 compatibility test to Optimism Bridge Adapter Test

* Replace Avalanche Bridge Adapter with Optimism Bridge Adapter
2023-08-09 10:37:13 -04:00
Savarn Dontamsetti (Sav)
43e3dce965
Adding support for Velodrome V2 on Optimism (#739)
* Adding support for Velodrome V2 for Optimism
2023-08-08 16:20:03 -04:00
Github Actions
33ece5aa99 Publish
- @0x/contracts-erc20@4.0.10
 - @0x/contracts-test-utils@5.4.56
 - @0x/contracts-treasury@1.4.50
 - @0x/contracts-utils@4.8.48
 - @0x/contracts-zero-ex@0.45.1
 - @0x/contract-addresses@8.9.0
 - @0x/contract-wrappers@13.23.4
 - @0x/protocol-utils@11.22.4
2023-07-21 21:28:47 +00:00
Github Actions
262d2a8089 Updated CHANGELOGS & MD docs 2023-07-21 21:28:43 +00:00
Kyu
51fa55badd
Update Base FillQuoteTransformer address (#738) 2023-07-21 13:58:04 -07:00
Github Actions
8c9917bf03 Publish
- @0x/contracts-erc20@4.0.9
 - @0x/contracts-test-utils@5.4.55
 - @0x/contracts-treasury@1.4.49
 - @0x/contracts-utils@4.8.47
 - @0x/contracts-zero-ex@0.45.0
 - @0x/contract-addresses@8.8.0
 - @0x/contract-wrappers@13.23.3
 - @0x/protocol-utils@11.22.3
2023-07-19 18:30:39 +00:00
Github Actions
b9e80db170 Updated CHANGELOGS & MD docs 2023-07-19 18:30:35 +00:00
Kyu
01e2690b2b
Check UniswapV3 support on Avalanche and BSC BridgeAdapters (#737) 2023-07-19 11:05:51 -07:00
Kyu
4835a1b1e7
Add UniswapV3 to Avalanche and BSC bridge adapters (#735)
* Add UniswapV3 to Avalanche and BSC

 * Support v2 router in MixinUniswapV3

* Update Avalanche and BSC FillQuoteTransformer addresses
2023-07-19 09:52:40 -04:00
Savarn Dontamsetti (Sav)
21c8477062
Fixing invalid CHANGELOG.json for contract-addresses (#736) 2023-07-18 18:25:10 -04:00
duncancmt
defdff460a
Base mainnet (#734)
* Add BaseGoerliBridgeAdapter

* Ignore Emacs files

* Remove Balancer V1; add Solidly to BaseBridgeAdapter

* Update addresses for both Base Mainnet and Goerli

* Remove Base Goerli testnet

* Prettier, CHANGELOG, and lowercasing addresses

---------

Co-authored-by: Savarn Dontamsetti <sav.dontamsetti@gmail.com>
2023-07-18 17:21:43 -04:00
wonge97
fd68edb239
Fix code block rendering (#729) 2023-06-20 11:22:54 -05:00
Kyu
e5e1393f0e
Offboard Shell and MStable (#726)
* Offboard Shell and MStable

* Update README
2023-06-06 11:49:44 +09:00
Andy
20961ef42e
Fix usage of expectEmit in MetaTransactionsFeatureV2 tests (#727)
* fix MetaTransactionsFeatureV2 tests

* pretty
2023-06-04 19:32:43 -04:00
Kyu
34febd728a
Remove Bancor v1 support from EthereumBridgeAdapter (#724) 2023-05-16 09:38:26 -07:00
Github Actions
c2aed76f2f Publish
- @0x/contracts-erc20@4.0.8
 - @0x/contracts-test-utils@5.4.54
 - @0x/contracts-treasury@1.4.48
 - @0x/contracts-utils@4.8.46
 - @0x/contracts-zero-ex@0.44.0
 - @0x/contract-addresses@8.7.0
 - @0x/contract-wrappers@13.23.2
 - @0x/protocol-utils@11.22.2
2023-05-10 20:03:48 +00:00
Github Actions
198d986fdc Updated CHANGELOGS & MD docs 2023-05-10 20:03:45 +00:00
Savarn Dontamsetti (Sav)
e2e1d1074c
feat: Add TraderJoeV2 MixIn to Arbitrum BridgeAdapter (#723)
* Add TraderJoeV2 MixIn to ArbitrumBridgeAdapter
2023-05-10 15:40:36 -04:00
wonge97
6a15dc3f13
update link (#721) 2023-05-08 17:25:34 -05:00
Github Actions
fefa3c13f6 Publish
- @0x/contracts-erc20@4.0.7
 - @0x/governance@1.0.5
 - @0x/contracts-test-utils@5.4.53
 - @0x/contracts-treasury@1.4.47
 - @0x/contracts-utils@4.8.45
 - @0x/contracts-zero-ex@0.43.0
 - @0x/contract-addresses@8.6.0
 - @0x/contract-wrappers@13.23.1
 - @0x/protocol-utils@11.22.1
2023-05-01 21:25:50 +00:00
Github Actions
f1d096a8af Updated CHANGELOGS & MD docs 2023-05-01 21:25:47 +00:00
Savarn Dontamsetti (Sav)
394ccbdc24
feat: Update Trader Joe V2 MixIn to support V2.1 router (#717)
* Add Trader Joe V2.1 Router MixIn

* Update changelog and FQT address

* lowercasing Avalanche FQT
2023-05-01 15:33:35 -04:00
Savarn Dontamsetti (Sav)
5a47f04ffc
fix: remove vm.stopPrank if vm.startPrank not present (#719)
* remove vm.stopPrank if vm.startPrank not present

* remove unnecessary vm.stopPrank from governance tests
2023-05-01 14:13:56 -04:00
wonge97
3c414c12e6
fix malformed table (#718)
table was missing one space :(
2023-04-27 13:19:52 -05:00
wonge97
d268a1136c
update bounty page (#716) 2023-04-27 11:51:55 -05:00
Github Actions
f8dd7168ee Publish
- @0x/contracts-treasury@1.4.46
 - @0x/contracts-zero-ex@0.42.1
 - @0x/contract-artifacts@3.19.0
 - @0x/contract-wrappers@13.23.0
 - @0x/protocol-utils@11.22.0
2023-04-24 11:12:31 +00:00
Github Actions
527ccf2ae5 Updated CHANGELOGS & MD docs 2023-04-24 11:12:28 +00:00
Andy
a7acf2951d
MetaTransaction V2 Utility Packages (#714)
* add utility class for MetaTransactionsV2, update artifacts, update wrappers

* fix exports

* update artifacts, update wrappers

* update changelogs
2023-04-24 06:03:23 -04:00
wonge97
e57fc60a76
add new audits to documentation. fix broken link (#715) 2023-04-20 18:30:29 -05:00
Github Actions
4379e15a4d Publish
- @0x/contracts-erc20@4.0.6
 - @0x/contracts-test-utils@5.4.52
 - @0x/contracts-treasury@1.4.45
 - @0x/contracts-utils@4.8.44
 - @0x/contracts-zero-ex@0.42.0
 - @0x/contract-addresses@8.5.0
 - @0x/contract-wrappers@13.22.21
 - @0x/protocol-utils@11.21.0
2023-04-20 05:41:33 +00:00
Github Actions
b0c37606a8 Updated CHANGELOGS & MD docs 2023-04-20 05:41:30 +00:00
Savarn Dontamsetti (Sav)
da8e43adfe
Remove Timestamps from Trader Joe V2 CHANGELOG entries (#713) 2023-04-20 14:10:41 +09:00
Savarn Dontamsetti (Sav)
b483805a22
Add Trader Joe V2 Mixin to Avalanche BridgeAdapter (#686)
* Add Trader Joe V2 Mixin to Avalanche BridgeAdapter

* Add TraderJoeV2 MixIn tests

* update forked function signatures

* Update Avalanche FQT address to reflect Trader Joe V2 MixIn

* Update CHANGELOG
2023-04-20 12:48:21 +09:00
Patrick Dowell
ff104e7505
feat: Multiplex + MetaTransaction integration and MetaTransaction Multi-Fee Support [RFQ-795] [LIT-870] (#665)
* MetaTransactionData changes

* MetaTransactionV2 creation and forge tests

* MetaTransactionData changes

* MetaTransactionV2 creation and forge tests

* add multiplexBatchSellTokenForToken, multiplexMultiHopSellTokenForToken, multiplex TokenForEth functions to metatransactions, add msgSender field to multiplex params

* Ran prettier to clean up

* More linting

* Fixing issues with EIP 712 signature, adding test case against MetaMask, and fixing lint issues

* Addressing suggestions from PR reviewers

* Complex rebase of test code based on changes in #655

* Fixing multiplex test failure

* add some tests for multiplex metatransactions

* prettier

* minor test fix

* cleaning up and adding batchExecuteMetaTransaction tests

* Removing ZERO_ADDRESS

* add multiHopBatchSellOtc to MultiplexFeature, fix _computeHopTarget for MultiplexSubcall.OTC [#667]

* fix _computeHopTarget for otc subcalls

* Fixing multiHopSellOtcOrder when params.useSelfBalance is true

* Making executeMetaTransactionV2 nonpayable and addressing a few other minor issues

* Forge update

* Add MetaTransactionsFeatureV2 to exported contracts

---------

Co-authored-by: abls <112491550+abls@users.noreply.github.com>
Co-authored-by: Duncan Townsend <git@duncancmt.com>
2023-04-19 18:20:28 -04:00
wonge97
8a2305c7b9
minor stylistic edits (#712) 2023-04-19 13:42:25 -05:00
wonge97
d7bc0b79df
Doc update (#675)
* Update existing protocol docs to point to protocol-specific resources

Remove documentation of outdated/irrelevant portions of the protocol

* pin jinja version to try to fixbuild failure. (#711)

---------

Co-authored-by: Eric Wong <ewong@Erics-MBP.fios-router.home>
2023-04-19 12:22:58 -05:00
Github Actions
ad2d41fd90 Publish
- @0x/contracts-erc20@4.0.5
 - @0x/governance@1.0.4
 - @0x/contracts-test-utils@5.4.51
 - @0x/contracts-treasury@1.4.44
 - @0x/contracts-utils@4.8.43
 - @0x/contracts-zero-ex@0.41.0
 - @0x/contract-addresses@8.4.0
 - @0x/contract-wrappers@13.22.20
 - @0x/protocol-utils@11.20.0
2023-04-17 18:29:25 +00:00
Github Actions
721b984bbc Updated CHANGELOGS & MD docs 2023-04-17 18:29:22 +00:00
Kyu
72433ea5b7
fix: Revert changelog commits from previous failed publish (#710)
* Revert "Publish"

This reverts commit f919e8ef02907f6d74ab01a995b362b0967efc69.

* Revert "Updated CHANGELOGS & MD docs"

This reverts commit dbcc7dd6f4cff6715d2c4308759c9551e0931a61.

* Bump contracts-erc20 and contracts-utils versions
2023-04-17 10:59:00 -07:00
Github Actions
f919e8ef02 Publish
- @0x/contracts-erc20@4.0.4
 - @0x/contracts-test-utils@5.4.51
 - @0x/contracts-treasury@1.4.44
 - @0x/contracts-utils@4.8.42
 - @0x/contracts-zero-ex@0.41.0
 - @0x/contract-addresses@8.4.0
 - @0x/contract-wrappers@13.22.20
 - @0x/protocol-utils@11.20.0
2023-04-17 03:09:17 +00:00
Github Actions
dbcc7dd6f4 Updated CHANGELOGS & MD docs 2023-04-17 03:09:13 +00:00
Kyu
33b7515734
Bump package versions for lerna (#709) 2023-04-16 19:42:57 -07:00
Github Actions
f96b944cce Updated CHANGELOGS & MD docs 2023-04-17 02:08:53 +00:00
Kyu
0fbdc1dccd
fix: Revert changelog commits from previous failed publish (#708)
* Revert "Publish"

This reverts commit 2b13dcfc14ead3aeb676cecb4c5e3814459212c7.

* Revert "Updated CHANGELOGS & MD docs"

This reverts commit d2bea4aa46f5e2860ebe05ccc5adae860aeda4b0.
2023-04-16 18:47:48 -07:00
Github Actions
484d9cafc4 Publish
- @0x/governance@1.0.3
2023-04-17 01:24:55 +00:00
Github Actions
e73885c9a0 Updated CHANGELOGS & MD docs 2023-04-17 01:24:52 +00:00
Kyu
948b17e2ed Add publishConfig in governance/package.json (#707)
* publishConfig is needed for lerna to publish governance as a public
  package
2023-04-16 18:03:23 -07:00
Github Actions
2b13dcfc14 Publish
- @0x/contracts-erc20@4.0.4
 - @0x/governance@1.0.2
 - @0x/contracts-test-utils@5.4.51
 - @0x/contracts-treasury@1.4.44
 - @0x/contracts-utils@4.8.42
 - @0x/contracts-zero-ex@0.40.0
 - @0x/contract-addresses@8.3.0
 - @0x/contract-wrappers@13.22.20
 - @0x/protocol-utils@11.19.0
2023-04-16 23:30:53 +00:00
Github Actions
d2bea4aa46 Updated CHANGELOGS & MD docs 2023-04-16 23:30:50 +00:00
Kyu
dc926a05b3
Update Ethereum FillQuoteTransformer address (#706) 2023-04-14 15:47:20 -07:00
Kyu
5cc53597de
feat: Add Barter support to EthereumBridgeAdapter [LIT-981] (#703) 2023-04-12 20:42:55 -07:00
wonge97
57ca2c07bb
Correct final audit document (#702) 2023-04-11 15:08:22 -04:00
Github Actions
b0d2dee84d Publish
- @0x/contracts-erc20@4.0.3
 - @0x/governance@1.0.1
 - @0x/contracts-test-utils@5.4.50
 - @0x/contracts-treasury@1.4.43
 - @0x/contracts-utils@4.8.41
 - @0x/contracts-zero-ex@0.39.2
 - @0x/contract-wrappers@13.22.19
 - @0x/protocol-utils@11.18.2
2023-04-10 20:05:52 +00:00
Github Actions
1422ad756d Updated CHANGELOGS & MD docs 2023-04-10 20:05:48 +00:00
David Walsh
9d216f9c1c
fix: add version in changelog for utils (#700) 2023-04-07 18:09:09 -06:00
David Walsh
ff74738178
chore: add forge to publish action (#699) 2023-04-07 17:15:00 -06:00
David Walsh
fe4fe48021
chore: update dependencies (#698) 2023-04-07 16:33:59 -06:00
Elena
356e74a645
Integration test for migrating 0x stake (#697)
* Add integration test for withdrawing 0x stake in catastrophic mode

* Add a test for delegating to an external account

* Use startPrank for all calls as all from delegator

* Better use of prank

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>

* Add the delegation test under normal operation mode'

---------

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>
2023-04-07 15:59:49 +03:00
duncancmt
70590e3e56
Actual script for governance deployment (#696)
* Renounce deployer timelock roles

* Add readme

* Remove obsoleted import

---------

Co-authored-by: elenadimitrova <elena@arenabg.com>
2023-04-06 19:01:43 +03:00
Elena
4e0b671bd8
Integration test catastrophic mode in staking v3 (#691)
* Integration test catastrophic mode

* Update test for withdrawals in catastrophic mode

* Test withdrawing delegator rewards work in catastrophic mode

* Test top stakers can withdraw

* Test more delegators rewards withdrawals

* Add claiming rewards for a pool owner

* Fix for forge coverage
2023-04-06 16:21:01 +03:00
Elena
ea2ca700c3
Add final report (#695) 2023-04-06 16:20:01 +03:00
Elena
3574ea5b27
Governance deployment scripts (#684)
* Add foundry deployment script and config

* Add generated by foundry build artifact for Treasury

* Add shortcut commands for goerli deployment

* Switch to a production version of predicting a deployment address

* Lower gas price

* Fix a copy-paste error

* Productionise and final test
2023-04-05 10:06:07 +03:00
Elena
b7bf5b5dfe
Governance integration testing (#683)
* Segregate tests and mocks and wire up integration test base

* Switch to a production version of predicting a deployment address

* Add integration test for exchange governor migration

* Add integration test for treassury migration

* Add integration test for migrating the treasury

* Add governance upgrade action to transfer ZRX tokens to new governor

* Add governance upgrade action to transfer wCELO tokens to new governor

* Add governance upgrade action to transfer WYV tokens to new governor

* Turn on verbose logging
2023-04-05 09:35:08 +03:00
Elena
cfbb9c6f6c
Governance audit and fixes (#690)
* Fix F-3: Explicit Variable Return audit report

* Reuse onlySecurityCouncil modifier and update its revert message

* Fix F-5: modifier Optimization audit report

* Fix F-7: Input Sanity Check audit report

* Add audit report

* Further gas optimise getting a checkpoint function by up to 100 gas

* Fix F-6: return Statement Optimization audit report

* Wrap logic in unchecked block to save 24 gas
in getVotes and getQuadraticVotes each

* Move audit doc into a dedicated folder
2023-04-04 18:06:38 +03:00
Elena
7e40dd1826
Remove offending test (#692) 2023-04-04 17:43:33 +03:00
Elena
bcbfbfa16c
Decentralised governance of 0x protocol and treasury (#641)
* Install open zeppelin contracts

* Init foundry in governance

* Add wrapped ZRX token

* Add governance contracts testing to CI

* Set optimizer runs to default

* Upgrade to patched version of openzeppelin/contracts

* Test stakingakng / unwrapping ZRX

* Init npm package

* Lint fix, removing lib from gitignore

* Add openzeppelin contracts git submodule for foundry

* Add vanilla governor contract

* Fix reference paths to imported packages

* Temporarily switch to using a mocked version of ZRX

* Ignore foundry's lib in link checker

* Fix a conflict in gitignore between forge lib adn built lib

* Upload governance code coverage report to coveralls

* Flesh out test scenarios for wrapping/unwrapping

* Add basic ERC20 name and symbol tests

* Wire in basic timelock controller and governor test setup

* Test basic governor properties

* Add basic voting power delegation tests

* Add proposal execution happy path test

* Split ERC20Votes logic between wrapped token
and ZeroExVotes contracts

* Exclude BaseTest from coverage in coveralls

* Add protocol specific governor with produciton governance settings

* Add a dedicated instance for the treasury governor
This is currently using the default 1 token 1 vote mechanism but will be migrated

* Add test for updating governance settings
for voting delay, voting period and proposal threshold

* Create seperate timelock contract instance for treasury and protocol

* Test updating the timlock min delay

* Set timelock delay to 2 days for protocol and 1 sec for treasury

* Remove timelock from treasury governor

* Refactor _checkpointsLookup to return entire Checkpoint
instad of just number of votes

* Update the totalSupply checkpoints updating logic

* Quadratic voting power transfers and delegations

* Fix workflow yaml

* Initialise ZeroExVotes behind a ERC1967Proxy
Test it cannot be reinitialised

* Remove obsoleted console.logs from test

* Storage pack Checkpoint enum

* Remove keeping track of total balances for voting

* Switch to using the foundry artifact in test

* Fix rebase issue

* Add timelock control over the treasury governor

* Add test for wrapped token transfer

* Emit separate events for changing linear and quadratic voting power

* Add the ability to cancel a proposal

* Limit the governors' cancel function to security council only

* Eject security council after a proposal is cancelled

* Add ability for governance to set the security council

* Merge the governors test suites into one reusable set of tests

* 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

* Security council can rollback protocol upgrades

* Upgrade to solidity 0.8.19

* Move IZeroExGovernor to src

* Abstract Security council interface into its own

* Emit events when assigning and ejecting the security council

* Use a cast to bytes4 instead of LibBytes

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>

* Writing total supply checkpoints and setup of
quorum percentage of quadratic total supply for treasure governor

* Add test for transferring tokens when delegating

* Rename IZeroExSecurityCouncil to ISecurityCouncil

* Add security council restrictions to governors

* Remove obsolete overflow check

* Improve test coverage

* Upgrade open-zeppelin contracts to 4.8.2

* Test delegation by signature

* Test non security council requests
to rollback protocol changes cannot be executed

* Better revert messages

* Test correct interfaces are supported

* Remove obsoleted funciton

* Further test delegation by signature scenario

* Split the delegation functionality tests

* Add test for initialisation of voting contract

* Add test for reading checkpoints

* Update code comments

* Fix compilation warnings

* Run smt checker

* Add checkpoint tests

* Rename parameter in moveEntireVotingPower to match the one in movePartialVotingPower

* Switch moveEntireVotingPower to a more generic moveVotingPower implementation
as in the open-zeppelin contracts

* Install foundry earlier in CI

* Switch movePartialVotingPower to the generic moveVotingPower implementation

* Write totalSupplyCheckpoints via the generic _writeCheckpoint

* Add threshold for quadratic voting power

* Remove autoinserted code by OZ

* Add openzeppelin/contracts-upgradable

* Add initializable base to Voting contract

* Fix terminogy error in natspec

* Fix code comment

* Remove obsoleted overrides and add a missing modifier to moveVotingPower

* Remove amount check

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>

* Fix a calculation error and clean tests

* Update thresholds for treasury governor

* Fix testShouldNotBeAbleToDelegateWithSignatureAfterExpiry

* 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>

* Add onlyProxy to initializer

* Fix quadratic voting weight base

* Rename voting parameter for clarity

* Make addresses immutable (#680)

* Make addresses immutable

* Fix linting issues

---------

Co-authored-by: elenadimitrova <elena@arenabg.com>

* 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 <elena@arenabg.com>

* Rename SecurityCouncil contract

* Add timestamp to delegator balance updates

* Make quadraticThreshold `immutable` for gas efficiency

* Remove the logic for ejecting security council

* Switch balance timestamp to be a block number

* 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 <elena@arenabg.com>

* Change test case to testFail

* Update contracts/governance/test/ZeroExVotesMigration.sol

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>

---------

Co-authored-by: duncancmt <1207590+duncancmt@users.noreply.github.com>
Co-authored-by: Duncan Townsend <git@duncancmt.com>
2023-03-22 15:03:05 +02:00
169 changed files with 12437 additions and 2456 deletions

View File

@ -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,33 +83,30 @@ jobs:
-p @0x/order-utils \
-m --serial -c test:ci
- name: Add foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
# NOTE: disabled as ZRXToken.sol did not compile with the latest forge.
# TODO: re-enable once the issue is resolved.
- name: Run Forge build for erc20
working-directory: contracts/erc20
run: |
forge --version
forge build --sizes
forge build --sizes --skip ZRXToken
- name: Run Forge tests for erc20
working-directory: contracts/erc20
run: |
forge test -vvv --gas-report
# - name: Run Forge tests for erc20
# working-directory: contracts/erc20
# run: |
# forge test -vvv --gas-report
- name: Run Forge coverage for erc20
working-directory: contracts/erc20
run: |
forge coverage --report summary --report lcov
# - name: Run Forge coverage for erc20
# working-directory: contracts/erc20
# run: |
# forge coverage --report summary --report lcov
- name: Upload the coverage report to Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
base-path: ./contracts/erc20/
path-to-lcov: ./contracts/erc20/lcov.info
# - name: Upload the coverage report to Coveralls
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# base-path: ./contracts/erc20/
# path-to-lcov: ./contracts/erc20/lcov.info
- name: Run Forge build for zero-ex
working-directory: contracts/zero-ex
@ -135,3 +137,27 @@ 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
# TODO: re-enable once the issue is resolved.
# - 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
# - 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

View File

@ -1,19 +1,21 @@
name: publish
on:
workflow_dispatch:
inputs:
ci_status:
description: 'required CI status'
default: 'success'
required: true
prerelease:
description: 'prerelease name'
required: false
workflow_dispatch:
inputs:
ci_status:
description: 'required CI status'
default: 'success'
required: true
prerelease:
description: 'prerelease name'
required: false
jobs:
publish:
runs-on: ubuntu-latest
env:
PublishBranch: publish/${{github.ref_name }}-${{ github.run_id }}-${{ github.run_number }}
steps:
- name: 'check successful status'
run: |
@ -22,18 +24,26 @@ jobs:
| jq .state)
[[ "${REF_STATUS}" == '"${{ github.event.inputs.ci_status }}"' ]] || \
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
- name: Add foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
- uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
fetch-depth: 0
ref: ${{ github.ref }}
fetch-depth: 0
- uses: actions/setup-node@v1
with:
node-version: 16
node-version: 16
- uses: actions/setup-python@v2
- name: 'configure git'
run: |
git config --global user.email "github-actions@github.com"
git config --global user.name "Github Actions"
- name: 'Checkout new branch'
run: |
git checkout -b $PublishBranch
git push -u origin $PublishBranch
- name: 'install dependencies'
run: |
yarn -D
@ -45,9 +55,23 @@ jobs:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ github.token }}
PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }}
- name: 'merge into main branch'
if: github.event.inputs.prerelease == '' # unless it's a prerelease
- name: 'Create PR to merge into ref branch'
run: |
git checkout main && \
git merge ${{ github.ref }} && \
git push
gh pr create \
-B ${{ github.ref_name }} \
-H $PublishBranch \
--title "Publish: CHANGELOG and Package Version Updates into ${{ github.ref_name }}" \
--body "Syncing CHANGELOG and package version updates from publish action ${{github.run_id}}-${{github.run_number}} into ${{ github.ref_name}} branch" \
--reviewer ${{ github.actor }}
env:
GITHUB_TOKEN: ${{ github.token }}
- name: 'Create PR to merge ref branch into main'
run: |
gh pr create \
-B main \
-H ${{ github.ref_name }} \
--title "Publish: Sync ${{ github.ref_name }} into main " \
--body "Syncing ${{ github.ref_name }} back into main after publish action. NOTE: this PR should be merged after CHANGELOG and package version updates have been merged into ${{ github.ref_name }}" \
--reviewer ${{ github.actor }}
env:
GITHUB_TOKEN: ${{ github.token }}

22
.gitignore vendored
View File

@ -63,7 +63,16 @@ 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
@ -75,6 +84,9 @@ TODO.md
# IDE file
.vscode
.idea
*~
.\#*
\#*\#
# generated contract artifacts/
generated-artifacts/
@ -90,6 +102,7 @@ foundry-artifacts/
# foundry cache
cache/
foundry-cache/
#foundry output artifacts
out/
@ -97,10 +110,17 @@ out/
# typechain wrappers
contracts/zero-ex/typechain-wrappers/
# foundry packages
contracts/governance/cache
contracts/governance/out
# Doc README copy
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*

11
.gitmodules vendored
View File

@ -3,4 +3,15 @@
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
[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

View File

@ -1,4 +1,112 @@
[
{
"timestamp": 1700094997,
"version": "4.0.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1693346928,
"version": "4.0.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1692368658,
"version": "4.0.12",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1691617396,
"version": "4.0.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689974915,
"version": "4.0.10",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689791426,
"version": "4.0.9",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1683749017,
"version": "4.0.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1682976338,
"version": "4.0.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681969282,
"version": "4.0.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.0.5",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1681756154
},
{
"timestamp": 1681697326,
"version": "4.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681157139,
"version": "4.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1678410794,
"version": "4.0.2",

View File

@ -5,6 +5,54 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.0.14 - _November 16, 2023_
* Dependencies updated
## v4.0.13 - _August 29, 2023_
* Dependencies updated
## v4.0.12 - _August 18, 2023_
* Dependencies updated
## v4.0.11 - _August 9, 2023_
* Dependencies updated
## v4.0.10 - _July 21, 2023_
* Dependencies updated
## v4.0.9 - _July 19, 2023_
* Dependencies updated
## v4.0.8 - _May 10, 2023_
* Dependencies updated
## v4.0.7 - _May 1, 2023_
* Dependencies updated
## v4.0.6 - _April 20, 2023_
* Dependencies updated
## v4.0.5 - _April 17, 2023_
* Dependencies updated
## v4.0.4 - _April 17, 2023_
* Dependencies updated
## v4.0.3 - _April 10, 2023_
* Dependencies updated
## v4.0.2 - _March 10, 2023_
* Dependencies updated

@ -1 +1 @@
Subproject commit a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df
Subproject commit fc560fa34fa12a335a50c35d92e55a6628ca467c

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "4.0.2",
"version": "4.0.14",
"engines": {
"node": ">=6.12"
},
@ -24,7 +24,7 @@
},
"homepage": "https://github.com/0xProject/protocol",
"devDependencies": {
"@0x/contracts-utils": "^4.8.40",
"@0x/contracts-utils": "^4.8.52",
"@0x/ts-doc-gen": "^0.0.28",
"typedoc": "~0.16.11"
},

View File

@ -39,7 +39,6 @@ contract WETH9V06Test is Test {
function testShouldConvertDepositedETHToWrappedETH() public {
vm.prank(user);
etherToken.deposit{value: 1e20}();
vm.stopPrank();
assertEq(etherToken.balanceOf(user), 1e20);
assertEq(address(etherToken).balance, 1e20);
@ -58,7 +57,6 @@ contract WETH9V06Test is Test {
etherToken.deposit{value: 1e20}();
vm.prank(user);
etherToken.withdraw(100);
vm.stopPrank();
assertEq(etherToken.balanceOf(user), 1e20 - 100);
assertEq(address(etherToken).balance, 1e20 - 100);
@ -68,7 +66,6 @@ contract WETH9V06Test is Test {
function testShouldConvertSentETHToWrappedETH() public {
vm.prank(user);
address(etherToken).call{value: 1e20}(new bytes(0));
vm.stopPrank();
assertEq(etherToken.balanceOf(user), 1e20);
assertEq(address(etherToken).balance, 1e20);

View File

@ -34,7 +34,6 @@ contract ZRXTokenTest is Test {
assembly {
_address := create(0, add(_bytecode, 0x20), mload(_bytecode))
}
vm.stopPrank();
zrxToken = IERC20Token(address(_address));
}
@ -73,7 +72,6 @@ contract ZRXTokenTest is Test {
function testShouldReturnFalseIfSenderHasInsufficientBalance() public {
vm.prank(owner);
zrxToken.approve(user, totalSupply + 1);
vm.stopPrank();
bool success = zrxToken.transferFrom(owner, user, totalSupply + 1);
assertEq(success, false);
@ -82,7 +80,6 @@ contract ZRXTokenTest is Test {
function testShouldReturnFalseIfRecipientHasInsufficientAllowance() public {
vm.prank(owner);
zrxToken.approve(user, totalSupply - 1);
vm.stopPrank();
bool success = zrxToken.transferFrom(owner, user, totalSupply);
assertEq(success, false);
@ -97,7 +94,6 @@ contract ZRXTokenTest is Test {
function testShouldNotModifySenderAllowanceIfSetToUINT256Max() public {
vm.prank(owner);
zrxToken.approve(user, type(uint256).max);
vm.stopPrank();
zrxToken.transferFrom(owner, user, 100);
assertEq(zrxToken.allowance(owner, user), type(uint256).max);
@ -106,7 +102,6 @@ contract ZRXTokenTest is Test {
function testShouldTransferCorrectlyWhenSufficientAllowance() public {
vm.prank(owner);
zrxToken.approve(user, 1000 * 1e18);
vm.stopPrank();
vm.prank(user);
zrxToken.transferFrom(owner, user, 100 * 1e18);

View File

@ -0,0 +1,47 @@
[
{
"timestamp": 1682976338,
"version": "1.0.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681756154,
"version": "1.0.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681694690,
"version": "1.0.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681687842,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681157139,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
}
]

View File

@ -0,0 +1,26 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v1.0.5 - _May 1, 2023_
* Dependencies updated
## v1.0.4 - _April 17, 2023_
* Dependencies updated
## v1.0.3 - _April 17, 2023_
* Dependencies updated
## v1.0.2 - _April 16, 2023_
* Dependencies updated
## v1.0.1 - _April 10, 2023_
* Dependencies updated

View File

@ -0,0 +1,74 @@
## Governance
This package contains contracts for the ZeroEx governance of 0x Protocol and Treasury.
## Production deployment
`ZRXWrappedToken` 0xfcfaf7834f134f5146dbb3274bab9bed4bafa917
`ZeroExVotesProxy` 0x9c766e51b46cbc1fa4f8b6718ed4a60ac9d591fb
`ZeroExVotes` 0x8d208c5514b98c5b9ceed650b02df2aeb1c73e6f
Protocol `ZeroExTimelock` 0xb6a1f58c5df9f13312639cddda0d128bf28cdd87
`ZeroExProtocolGovernor` 0xc256035fe8533f9ce362012a6ae0aefed4df30f4
Treasury `ZeroExTimelock` 0x0dcfb77a581bc8fe432e904643a5480cc183f38d
`ZeroExTreasuryGovernor` 0x4822cfc1e7699bdb9551bdfd3a838ee414bc2008
Security council 0x979BDb496e5f0A00af078b7a45F1E9E6bcff170F
## Design
This implementation fully decentralises governance of 0x Protocol and Treasury. This is enabled via a wrapped ZRX token and a Compound-like governors design. There are two separate governors for Protocol - `ZeroExProtocolGovernor` and Treasury - `ZeroExTreasuryGovernor` respectively working with two separate Timelock instances of the same contract implementation - `ZeroExTimelock`.
### Upgradability
`ZRXWrappedToken` , `ZeroExProtocolGovernor` and `ZeroExTreasuryGovernor` governors are non-upgradable by design. However the voting implementation the governors use - `ZeroExVotes` is upgradable and using the OZ `ERC1967Proxy`.
### Wrapped ZRX
wZRX will be issued 1-to-1 for ZRX. No locking/vesting mechanisms will exist between wZRX and ZRX and the two will be freely interchangeable. The ZRX token is non-upgradable and same will be valid for its wrapped equivalent.
The token supports delegation which allows a user to delegate their entire voting power to another account (which doesn't necessarily need to be a token holder). This is modelled on the standard OpenZeppelin `ERC20Votes` implementation. We have added logic for block number stamping delegators' balance changes stored in the `DelegateInfo.balanceLastUpdated` property. This block number information is sent in calls to `ZeroExVotes.moveVotingPower` in order to provide support for future upgrades to the vote power calculation.
Note that for consistency `block.number` is used for the governor settings, voting checkpoints and this delegators' balance last updated property while timelock logic for the governor uses block.timestamp.
### Governors' settings
Changing governors' settings for `votingDelay`, `votingPeriod` and `proposalThreshold` can be done via the normal proposal mechanism. Governors are deployed with the following initial settings:
| | voting delay | voting period | proposal threshold |
|-------------------|--------------|---------------|--------------------|
| Protocol governor | 2 days | 7 days | 1000000e18 |
| Treasury governor | 2 days | 7 days | 250000e18 |
This is using standard openzeppelin `GovernorSettings` implementation.
### Quorum
Quorum for Protocol is fixed at 10m (10000000e18) while for Treasury this is calculated as 10% of voting power of the total supply (see voting strategies below for quadratic voting power implementation specifics). The quorum calculations for Treasury are using OpenZeppelin's `GovernorVotesQuorumFraction`.
Note that in-place updates to the quorum are not supported and will need to go through a governance upgrade. Reasoning behind this can be found in this discussion https://forum.openzeppelin.com/t/quorum-default-behaviour-on-governors/34560.
### Voting strategies
The voting strategy will be linear 1-token-1-vote for Protocol and quadratic with threshold of 1000000e18 for Treasury (i.e. voting weight is linear up to 1m tokens and balance above that threshold is quadratic).
Worth noting is the `Checkpoint` struct design. For packing every `Checkpoint` into a single storage slot we are using the minimum uint type size for `votes` and `quadraticVotes` members, e.g.
```
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
uint96 quadraticVotes;
}
```
since the maximum token supply is 1bn we can have maximum value for:
`votes` : 1bn *10^18 => can be stored in 90 bits
`quadraticVotes` : due to the likelihood of threshold changing and potentially bringing it closer to a linear vote, we are preemptively keeping this to the same size as linear votes slot.
### Time locks
Governance proposals are subject to a 3 days delay for Protocol and 2 days for Treasury. This delay allows Security Council time to review passed proposals and take action where needed.
### Security Council
The concept of a Security council is introduced which allows a multisig of security council members to cancel a proposal on the treasury or protocol governors and also rollback the protocol to an earlier version.
When no security council is assigned the following apply:
- ongoing proposals can still be voted on
- new proposals cannot be created - except assignSecurityCouncil
- expired proposals that are successful cannot be queued - excepted assignSecurityCouncil
There is a provision for the governors to have different security council set although initially these are the same.

View File

@ -0,0 +1,505 @@
{
"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"
}
],
"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": {}
},
"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": {}
},
"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"
},
"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-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
},
"compilationTarget": {
"contracts/erc20/src/ZRXToken.sol": "ZRXToken"
},
"libraries": {}
},
"sources": {
"contracts/erc20/src/ZRXToken.sol": {
"keccak256": "0x8582c06b20f8b7d3d603b485b5d26f840e01d1986381b334a856833edcff0d47",
"urls": ["bzzr://ef728dddbaa1e26baa6cc9fe0f83de5055bc0b17dfe488018f4ee59d68ccb5dd"],
"license": null
}
},
"version": 1
},
"id": 0
}

View File

@ -0,0 +1,37 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib', "../utils/contracts/src/"]
match_path = "test/unit/*.sol"
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'
optimizer_runs = 20_000
via_ir = true
[profile.integration]
match_path = "test/integration/*.sol"
gas_price = 31_000_000_000
[rpc_endpoints]
goerli = "${GOERLI_RPC_URL}"
mainnet = "${MAINNET_RPC_URL}"
[etherscan]
goerli = { key = "${ETHERSCAN_API_KEY}" }
[profile.smt.model_checker]
engine = 'chc'
timeout = 10_000
targets = [
'assert',
'constantCondition',
'divByZero',
'outOfBounds',
'underflow'
]
contracts = { 'src/ZeroExProtocolGovernor.sol' = [ 'ZeroExProtocolGovernor' ] }

@ -0,0 +1 @@
Subproject commit eb980e1d4f0e8173ec27da77297ae411840c8ccb

@ -0,0 +1 @@
Subproject commit d00acef4059807535af0bd0dd0ddf619747a044b

@ -0,0 +1 @@
Subproject commit f6c4c9c4ec601665ca74d2c9dddf547fc425658c

View File

@ -0,0 +1,27 @@
{
"name": "@0x/governance",
"version": "1.0.5",
"description": "Governance implementation for the 0x protocol and treasury",
"main": "index.js",
"directories": {
"lib": "lib",
"test": "test"
},
"scripts": {
"test": "forge test",
"build": "forge build",
"build:smt": "FOUNDRY_PROFILE=smt forge build",
"test:integration": "source .env && FOUNDRY_PROFILE=integration forge test --fork-url $MAINNET_RPC_URL --fork-block-number 16884148 -vvv",
"goerli:deploy:zrxtoken": "source .env && forge script script/DeployToken.s.sol:Deploy --rpc-url $GOERLI_RPC_URL --broadcast --slow -vvvv",
"goerli:deploy:governance": "source .env && forge script script/DeployGovernance.s.sol:Deploy --rpc-url $GOERLI_RPC_URL --broadcast --slow -vvvv",
"mainnet:deploy:governance": "source .env && forge script script/DeployGovernance.s.sol:Deploy --rpc-url $MAINNET_RPC_URL --broadcast --slow -vvvv"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/protocol.git"
},
"publishConfig": {
"access": "public"
},
"license": "Apache-2.0"
}

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import "forge-std/console.sol";
import "forge-std/console2.sol";
import "@openzeppelin/token/ERC20/IERC20.sol";
import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";
import "../src/ZRXWrappedToken.sol";
import "../src/ZeroExVotes.sol";
import "../src/ZeroExTimelock.sol";
import "../src/ZeroExProtocolGovernor.sol";
import "../src/ZeroExTreasuryGovernor.sol";
contract Deploy is Script {
address internal constant ZRX_TOKEN = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;
address internal constant TREASURY = 0x0bB1810061C2f5b2088054eE184E6C79e1591101;
address internal constant EXCHANGE = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
address payable internal constant SECURITY_COUNCIL = payable(0x979BDb496e5f0A00af078b7a45F1E9E6bcff170F);
uint256 internal constant QUADRATIC_THRESHOLD = 1000000e18;
function setUp() public {}
function run() external {
address deployer = vm.envAddress("DEPLOYER");
vm.startBroadcast(deployer);
console2.log("Zrx Token", ZRX_TOKEN);
address wTokenPrediction = predict(deployer, vm.getNonce(deployer) + 2);
ZeroExVotes votesImpl = new ZeroExVotes(wTokenPrediction, QUADRATIC_THRESHOLD);
ERC1967Proxy votesProxy = new ERC1967Proxy(address(votesImpl), abi.encodeCall(votesImpl.initialize, ()));
ZRXWrappedToken wToken = new ZRXWrappedToken(IERC20(ZRX_TOKEN), ZeroExVotes(address(votesProxy)));
assert(address(wToken) == wTokenPrediction);
console2.log("Wrapped Token", address(wToken));
ZeroExVotes votes = ZeroExVotes(address(votesProxy));
console2.log("Votes", address(votes));
address[] memory proposers = new address[](0);
address[] memory executors = new address[](0);
ZeroExTimelock protocolTimelock = new ZeroExTimelock(3 days, proposers, executors, deployer);
console2.log("Protocol timelock", address(protocolTimelock));
ZeroExProtocolGovernor protocolGovernor = new ZeroExProtocolGovernor(
IVotes(address(votes)),
protocolTimelock,
SECURITY_COUNCIL
);
protocolTimelock.grantRole(protocolTimelock.PROPOSER_ROLE(), address(protocolGovernor));
protocolTimelock.grantRole(protocolTimelock.EXECUTOR_ROLE(), address(protocolGovernor));
protocolTimelock.grantRole(protocolTimelock.CANCELLER_ROLE(), address(protocolGovernor));
protocolTimelock.renounceRole(protocolTimelock.TIMELOCK_ADMIN_ROLE(), deployer);
console2.log("Protocol governor", address(protocolGovernor));
ZeroExTimelock treasuryTimelock = new ZeroExTimelock(2 days, proposers, executors, deployer);
console2.log("Treasury timelock", address(treasuryTimelock));
ZeroExTreasuryGovernor treasuryGovernor = new ZeroExTreasuryGovernor(
IVotes(address(votes)),
treasuryTimelock,
SECURITY_COUNCIL
);
treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor));
treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor));
treasuryTimelock.grantRole(treasuryTimelock.CANCELLER_ROLE(), address(treasuryGovernor));
treasuryTimelock.renounceRole(treasuryTimelock.TIMELOCK_ADMIN_ROLE(), deployer);
console2.log("Treasury governor", address(treasuryGovernor));
console2.log(unicode"0x governance deployed successfully 🎉");
vm.stopBroadcast();
}
function predict(address deployer, uint256 nonce) internal pure returns (address) {
require(nonce > 0 && nonce < 128, "Invalid nonce");
return address(uint160(uint256(keccak256(abi.encodePacked(bytes2(0xd694), deployer, bytes1(uint8(nonce)))))));
}
}

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
import "forge-std/Script.sol";
import "forge-std/console2.sol";
contract Deploy is Script {
function setUp() public {}
function run() external {
vm.startBroadcast(vm.envAddress("DEPLOYER"));
bytes memory _bytecode = vm.getCode("./ZRXToken.json");
address zrxToken;
assembly {
zrxToken := create(0, add(_bytecode, 0x20), mload(_bytecode))
}
console2.log("Zrx Token", zrxToken);
console2.log(unicode"Zrx Token deployed successfully 🎉");
vm.stopBroadcast();
}
}

View File

@ -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)))
}
}
}
}

View File

@ -0,0 +1,39 @@
// 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 "./SecurityCouncil.sol";
import "@openzeppelin/governance/IGovernor.sol";
import "@openzeppelin/governance/extensions/IGovernorTimelock.sol";
abstract contract IZeroExGovernor is SecurityCouncil, IGovernor, IGovernorTimelock {
function token() public virtual returns (address);
function proposalThreshold() public view virtual returns (uint256);
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);
}

View File

@ -0,0 +1,132 @@
// 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 IZeroExVotes {
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
uint96 quadraticVotes;
}
/**
* @dev Emitted when a token transfer or delegate change,
* results in changes to a delegate's quadratic number of votes.
*/
event DelegateQuadraticVotesChanged(
address indexed delegate,
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 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 Get the `pos`-th checkpoint for `account`.
*/
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 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`.
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
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!
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
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 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 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 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(
address src,
address dst,
uint256 srcBalance,
uint256 dstBalance,
uint96 srcBalanceLastUpdated,
uint96 dstBalanceLastUpdated,
uint256 amount
) external returns (bool);
function writeCheckpointTotalSupplyMint(uint256 accountBalance, uint256 amount) external returns (bool);
function writeCheckpointTotalSupplyBurn(uint256 accountBalance, uint256 amount) external returns (bool);
}

View File

@ -0,0 +1,85 @@
// 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;
abstract contract SecurityCouncil {
address public securityCouncil;
event SecurityCouncilAssigned(address securityCouncil);
event SecurityCouncilEjected();
modifier onlySecurityCouncil() {
_checkSenderIsSecurityCouncil();
_;
}
/**
* @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");
}
_;
}
/**
* @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,
bytes[] memory calldatas,
bytes32 descriptionHash
) public virtual;
function _payloadIsAssignSecurityCouncil(bytes[] memory payloads) private pure returns (bool) {
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)
// = bytes4(keccak256("assignSecurityCouncil(address)"))
// = 0x2761c3cd
if (bytes4(payload) == bytes4(0x2761c3cd)) return true;
else return false;
}
function _checkSenderIsSecurityCouncil() private view {
require(msg.sender == securityCouncil, "SecurityCouncil: only security council allowed");
}
}

View File

@ -0,0 +1,164 @@
// 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 "@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
) ERC20("Wrapped ZRX", "wZRX") ERC20Permit("Wrapped ZRX") ERC20Wrapper(wrappedToken) {
zeroExVotes = _zeroExVotes;
}
IZeroExVotes public immutable zeroExVotes;
mapping(address => DelegateInfo) 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
function decimals() public pure override(ERC20, ERC20Wrapper) returns (uint8) {
return 18;
}
function _afterTokenTransfer(address from, address to, uint256 amount) internal override(ERC20) {
super._afterTokenTransfer(from, 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;
if (fromDelegate.delegate != address(0)) _delegates[from].balanceLastUpdated = SafeCast.toUint96(block.number);
if (toDelegate.delegate != address(0)) _delegates[to].balanceLastUpdated = SafeCast.toUint96(block.number);
zeroExVotes.moveVotingPower(
fromDelegate.delegate,
toDelegate.delegate,
fromBalance,
toBalance,
fromDelegate.balanceLastUpdated,
toDelegate.balanceLastUpdated,
amount
);
}
function _mint(address account, uint256 amount) internal override(ERC20) {
super._mint(account, amount);
zeroExVotes.writeCheckpointTotalSupplyMint(balanceOf(account) - amount, amount);
}
function _burn(address account, uint256 amount) internal override(ERC20) {
super._burn(account, amount);
address(zeroExVotes).functionCallWithGas(
abi.encodeCall(zeroExVotes.writeCheckpointTotalSupplyBurn, (balanceOf(account) + amount, amount)),
500_000,
32
);
}
/**
* @dev Get the address `account` is currently delegating to.
*/
function delegates(address account) public view returns (address) {
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;
}
function delegateInfo(address account) public view returns (DelegateInfo memory) {
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 {
DelegateInfo memory delegateInfo = delegateInfo(delegator);
uint256 delegatorBalance = balanceOf(delegator);
_delegates[delegator] = DelegateInfo(delegatee, SafeCast.toUint96(block.timestamp));
emit DelegateChanged(delegator, delegateInfo.delegate, delegatee);
zeroExVotes.moveVotingPower(
delegateInfo.delegate,
delegatee,
delegatorBalance,
0,
delegateInfo.balanceLastUpdated,
0,
delegatorBalance
);
}
}

View File

@ -0,0 +1,145 @@
// 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 "./SecurityCouncil.sol";
import "./ZeroExTimelock.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/GovernorTimelockControl.sol";
contract ZeroExProtocolGovernor is
SecurityCouncil,
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorTimelockControl
{
constructor(
IVotes _votes,
ZeroExTimelock _timelock,
address _securityCouncil
)
Governor("ZeroExProtocolGovernor")
GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 1000000e18)
GovernorVotes(_votes)
GovernorTimelockControl(TimelockController(payable(_timelock)))
{
securityCouncil = _securityCouncil;
}
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) {
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) securityCouncilAssigned(calldatas) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function proposalThreshold() public view override(Governor, GovernorSettings) returns (uint256) {
return super.proposalThreshold();
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public override onlySecurityCouncil {
_cancel(targets, values, calldatas, descriptionHash);
}
// 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 onlySecurityCouncil {
// Execute the batch of rollbacks via the timelock controller
ZeroExTimelock timelockController = ZeroExTimelock(payable(timelock()));
timelockController.executeRollbackBatch(targets, values, calldatas, 0, descriptionHash);
}
function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance {
super.assignSecurityCouncil(_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(
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);
}
}

View File

@ -0,0 +1,70 @@
// 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 "@openzeppelin/governance/TimelockController.sol";
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,
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 > 0, "ZeroExTimelock: empty targets");
require(targets.length == values.length, "ZeroExTimelock: length mismatch");
require(targets.length == payloads.length, "ZeroExTimelock: 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(bytes4(payload) == bytes4(0x9db64a40), "ZeroExTimelock: not rollback");
_execute(target, value, payload);
emit CallExecuted(id, i, target, value, payload);
}
}
}

View File

@ -0,0 +1,154 @@
// 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 "@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";
import "./SecurityCouncil.sol";
contract ZeroExTreasuryGovernor is
SecurityCouncil,
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
constructor(
IVotes votes,
TimelockController _timelock,
address _securityCouncil
)
Governor("ZeroExTreasuryGovernor")
GovernorSettings(14400 /* 2 days */, 50400 /* 7 days */, 250000e18)
GovernorVotes(votes)
GovernorVotesQuorumFraction(10)
GovernorTimelockControl(_timelock)
{
securityCouncil = _securityCouncil;
}
/**
* @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));
return (votes.getPastQuadraticTotalSupply(blockNumber) * quorumNumerator(blockNumber)) / quorumDenominator();
}
// 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 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);
}
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) securityCouncilAssigned(calldatas) returns (uint256) {
return super.propose(targets, values, calldatas, description);
}
function cancel(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public override onlySecurityCouncil {
_cancel(targets, values, calldatas, descriptionHash);
}
function assignSecurityCouncil(address _securityCouncil) public override onlyGovernance {
super.assignSecurityCouncil(_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(
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);
}
}

View File

@ -0,0 +1,336 @@
// 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 "@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, Initializable, OwnableUpgradeable, UUPSUpgradeable {
address public immutable token;
uint256 public immutable quadraticThreshold;
mapping(address => Checkpoint[]) internal _checkpoints;
Checkpoint[] private _totalSupplyCheckpoints;
constructor(address _token, uint256 _quadraticThreshold) {
require(_token != address(0), "ZeroExVotes: token cannot be 0");
token = _token;
quadraticThreshold = _quadraticThreshold;
_disableInitializers();
}
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
modifier onlyToken() {
_checkSenderIsToken();
_;
}
function initialize() public virtual onlyProxy initializer {
__Ownable_init();
__UUPSUpgradeable_init();
}
/**
* @inheritdoc IZeroExVotes
*/
function checkpoints(address account, uint32 pos) public view returns (Checkpoint memory) {
return _checkpoints[account][pos];
}
/**
* @inheritdoc IZeroExVotes
*/
function numCheckpoints(address account) public view returns (uint32) {
return SafeCast.toUint32(_checkpoints[account].length);
}
/**
* @inheritdoc IZeroExVotes
*/
function getVotes(address account) public view returns (uint256) {
unchecked {
uint256 pos = _checkpoints[account].length;
return pos == 0 ? 0 : _unsafeAccess(_checkpoints[account], pos - 1).votes;
}
}
/**
* @inheritdoc IZeroExVotes
*/
function getQuadraticVotes(address account) public view returns (uint256) {
unchecked {
uint256 pos = _checkpoints[account].length;
return pos == 0 ? 0 : _unsafeAccess(_checkpoints[account], pos - 1).quadraticVotes;
}
}
/**
* @inheritdoc IZeroExVotes
*/
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);
return checkpoint.votes;
}
/**
* @inheritdoc IZeroExVotes
*/
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);
return checkpoint.quadraticVotes;
}
/**
* @inheritdoc IZeroExVotes
*/
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
// this always returns 0.
Checkpoint memory checkpoint = _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
return checkpoint.votes;
}
/**
* @inheritdoc IZeroExVotes
*/
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
// this always returns 0.
Checkpoint memory checkpoint = _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
return checkpoint.quadraticVotes;
}
/**
* @inheritdoc IZeroExVotes
*/
function moveVotingPower(
address src,
address dst,
uint256 srcBalance,
uint256 dstBalance,
uint96 srcBalanceLastUpdated,
uint96 dstBalanceLastUpdated,
uint256 amount
) public virtual onlyToken returns (bool) {
if (src != dst) {
if (src != address(0)) {
(
uint256 oldWeight,
uint256 newWeight,
uint256 oldQuadraticWeight,
uint256 newQuadraticWeight
) = _writeCheckpoint(_checkpoints[src], _subtract, srcBalance, srcBalanceLastUpdated, 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, dstBalance, dstBalanceLastUpdated, amount);
emit DelegateVotesChanged(dst, oldWeight, newWeight);
emit DelegateQuadraticVotesChanged(dst, oldQuadraticWeight, newQuadraticWeight);
}
}
return true;
}
/**
* @inheritdoc IZeroExVotes
*/
function writeCheckpointTotalSupplyMint(
uint256 accountBalance,
uint256 amount
) public virtual onlyToken returns (bool) {
(, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint(
_totalSupplyCheckpoints,
_add,
accountBalance,
0,
amount
);
emit TotalSupplyChanged(newWeight, newQuadraticWeight);
return true;
}
/**
* @inheritdoc IZeroExVotes
*/
function writeCheckpointTotalSupplyBurn(
uint256 accountBalance,
uint256 amount
) public virtual onlyToken returns (bool) {
(, uint256 newWeight, , uint256 newQuadraticWeight) = _writeCheckpoint(
_totalSupplyCheckpoints,
_subtract,
accountBalance,
0,
amount
);
emit TotalSupplyChanged(newWeight, newQuadraticWeight);
return true;
}
/**
* @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
) internal view returns (Checkpoint memory checkpoint) {
// 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;
if (high != 0) checkpoint = _unsafeAccess(ckpts, high - 1);
}
function _writeCheckpoint(
Checkpoint[] storage ckpts,
function(uint256, uint256) view returns (uint256) op,
uint256 userBalance,
uint96 balanceLastUpdated,
uint256 delta
)
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);
oldWeight = oldCkpt.votes;
newWeight = op(oldWeight, delta);
oldQuadraticWeight = oldCkpt.quadraticVotes;
// Remove the entire sqrt userBalance from quadratic voting power.
// Note that `userBalance` is value _after_ transfer.
if (pos > 0) {
uint256 oldQuadraticVotingPower = userBalance <= quadraticThreshold
? userBalance
: quadraticThreshold + Math.sqrt((userBalance - quadraticThreshold) * 1e18);
oldCkpt.quadraticVotes -= SafeCast.toUint96(oldQuadraticVotingPower);
}
// 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;
if (pos > 0 && oldCkpt.fromBlock == block.number) {
Checkpoint storage chpt = _unsafeAccess(ckpts, pos - 1);
chpt.votes = SafeCast.toUint96(newWeight);
chpt.quadraticVotes = SafeCast.toUint96(newQuadraticWeight);
} else {
ckpts.push(
Checkpoint({
fromBlock: SafeCast.toUint32(block.number),
votes: SafeCast.toUint96(newWeight),
quadraticVotes: SafeCast.toUint96(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
* https://github.com/ethereum/solidity/issues/9117
*/
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)
}
}
function _checkSenderIsToken() private {
require(msg.sender == token, "ZeroExVotes: only token allowed");
}
}

View File

@ -0,0 +1,167 @@
// 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 "forge-std/Test.sol";
import "forge-std/console.sol";
import "@openzeppelin/token/ERC20/ERC20.sol";
import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";
import "./mocks/ZRXMock.sol";
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));
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));
uint256 internal quadraticThreshold = 1000000e18;
bytes32 internal constant DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
constructor() {
vm.deal(account1, 1e20);
vm.deal(account2, 1e20);
vm.deal(account3, 1e20);
vm.deal(account4, 1e20);
vm.deal(securityCouncil, 1e20);
}
function setupGovernance(
IERC20 zrxToken
) internal returns (ZRXWrappedToken, ZeroExVotes, ZeroExTimelock, ZeroExTimelock, address, address) {
(ZRXWrappedToken token, ZeroExVotes votes) = setupZRXWrappedToken(zrxToken);
vm.startPrank(account1);
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,
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,
securityCouncil
);
treasuryTimelock.grantRole(treasuryTimelock.PROPOSER_ROLE(), address(treasuryGovernor));
treasuryTimelock.grantRole(treasuryTimelock.EXECUTOR_ROLE(), address(treasuryGovernor));
treasuryTimelock.grantRole(treasuryTimelock.CANCELLER_ROLE(), address(treasuryGovernor));
vm.stopPrank();
return (token, votes, protocolTimelock, treasuryTimelock, address(protocolGovernor), address(treasuryGovernor));
}
function setupZRXWrappedToken(IERC20 zrxToken) internal returns (ZRXWrappedToken, ZeroExVotes) {
vm.startPrank(account1);
address wTokenPrediction = predictAddress(account1, vm.getNonce(account1) + 2);
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();
assert(address(wToken) == wTokenPrediction);
return (wToken, ZeroExVotes(address(votesProxy)));
}
function mockZRXToken() internal returns (IERC20 zrxToken) {
vm.startPrank(account1);
bytes memory _bytecode = vm.getCode("./ZRXToken.json");
assembly {
zrxToken := create(0, add(_bytecode, 0x20), mload(_bytecode))
}
vm.stopPrank();
}
// Sourced from https://github.com/grappafinance/core/blob/master/src/test/utils/Utilities.sol
function predictAddress(address _origin, uint256 _nonce) public pure returns (address) {
if (_nonce == 0x00) {
return
address(
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80)))))
);
}
if (_nonce <= 0x7f) {
return
address(
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, uint8(_nonce)))))
);
}
if (_nonce <= 0xff) {
return
address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xd7), bytes1(0x94), _origin, bytes1(0x81), uint8(_nonce))
)
)
)
);
}
if (_nonce <= 0xffff) {
return
address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xd8), bytes1(0x94), _origin, bytes1(0x82), uint16(_nonce))
)
)
)
);
}
if (_nonce <= 0xffffff) {
return
address(
uint160(
uint256(
keccak256(
abi.encodePacked(bytes1(0xd9), bytes1(0x94), _origin, bytes1(0x83), uint24(_nonce))
)
)
)
);
}
return
address(
uint160(
uint256(
keccak256(abi.encodePacked(bytes1(0xda), bytes1(0x94), _origin, bytes1(0x84), uint32(_nonce)))
)
)
);
}
}

View File

@ -0,0 +1,454 @@
// 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 "@openzeppelin/token/ERC20/IERC20.sol";
import "../mocks/IZeroExMock.sol";
import "../mocks/IZrxTreasuryMock.sol";
import "../mocks/IStakingMock.sol";
import "../mocks/IZrxVaultMock.sol";
import "../BaseTest.t.sol";
import "../../src/ZRXWrappedToken.sol";
import "../../src/ZeroExVotes.sol";
import "../../src/ZeroExTimelock.sol";
import "../../src/ZeroExProtocolGovernor.sol";
import "../../src/ZeroExTreasuryGovernor.sol";
contract GovernanceE2ETest is BaseTest {
uint256 internal mainnetFork;
string internal MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL");
struct DelegatorPool {
address delegator;
bytes32 pool;
}
address internal constant ZRX_TOKEN = 0xE41d2489571d322189246DaFA5ebDe1F4699F498;
address internal constant MATIC_TOKEN = 0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0;
address internal constant WCELO_TOKEN = 0xE452E6Ea2dDeB012e20dB73bf5d3863A3Ac8d77a;
address internal constant WYV_TOKEN = 0x056017c55aE7AE32d12AeF7C679dF83A85ca75Ff;
address internal constant EXCHANGE_PROXY = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF;
address internal constant EXCHANGE_GOVERNOR = 0x618F9C67CE7Bf1a50afa1E7e0238422601b0ff6e;
address internal constant TREASURY = 0x0bB1810061C2f5b2088054eE184E6C79e1591101;
address internal constant STAKING = 0xa26e80e7Dea86279c6d778D702Cc413E6CFfA777; // Holds ~$262K in WETH for rewards
address internal constant ZRX_VAULT = 0xBa7f8b5fB1b19c1211c5d49550fcD149177A5Eaf; // Holds ~$10m in staked ZRX
address internal constant STAKING_AND_VAULT_OWNER = 0x7D3455421BbC5Ed534a83c88FD80387dc8271392;
address internal staker1 = 0x885c327cAD2aebb969dfaAb4c928B73CA17e3887;
address internal staker2 = 0x03c823e96F6964076C118395F08a2D7edF0f8a8C;
address[] internal topStakers = [
0x5775afA796818ADA27b09FaF5c90d101f04eF600,
0xE1bdcd3B70e077D2d66ADcbe78be3941F0BF380B,
0xcCa71809E8870AFEB72c4720d0fe50d5C3230e05,
0x828FD91d3e3a9FFa6305e78B9aE2Cfbc5B5D9f6B,
0x4A36C3DA5d367B148d17265e7d7feafcf8fb4a21,
0xEeff6fd32DeaFe1a9d3258A51c7F952F9FF0B2Ce,
0x1D0738b927dFCBFBD59A9F0944BbD1860d3B9248,
0x0C073E7248C1b548a08b27dD3af5D0f39c774280,
0xA178FF321335BB777A7E21A56376592F69556b9c,
0xD06CfBb59d2e8918F84D99d981039d7706DCA288
];
// voting power 1500000e18
address internal voter1 = 0x292c6DAE7417B3D31d8B6e1d2EeA0258d14C4C4b;
bytes32 internal voter1Pool = 0x0000000000000000000000000000000000000000000000000000000000000030;
bytes32[] internal voter1_operated_poolIds = [voter1Pool];
// voting power 1500000.5e18
address internal voter2 = 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08;
bytes32 internal voter2Pool = 0x0000000000000000000000000000000000000000000000000000000000000031;
bytes32[] internal voter2_operated_poolIds = [voter2Pool];
// voting power 1500000e18
address internal voter3 = 0x5265Bde27F57E738bE6c1F6AB3544e82cdc92a8f;
bytes32 internal voter3Pool = 0x0000000000000000000000000000000000000000000000000000000000000032;
bytes32[] internal voter3_operated_poolIds = [voter3Pool];
// voting power 1500000e18
address internal voter4 = 0xcA9F5049c1Ea8FC78574f94B7Cf5bE5fEE354C31;
bytes32 internal voter4Pool = 0x0000000000000000000000000000000000000000000000000000000000000034;
bytes32[] internal voter4_operated_poolIds = [voter4Pool];
// voting power 1500000e18
address internal voter5 = 0xDBB5664a9DBCB98F6365804880e5b277B3155422;
bytes32 internal voter5Pool = 0x0000000000000000000000000000000000000000000000000000000000000035;
bytes32[] internal voter5_operated_poolIds = [voter5Pool];
// voting power 2291490.952353335e18
address internal voter6 = 0x9a4Eb1101C0c053505Bd71d2fFa27Ed902DEaD85;
bytes32 internal voter6Pool = 0x0000000000000000000000000000000000000000000000000000000000000029;
bytes32[] internal voter6_operated_poolIds = [voter6Pool];
// voting power 4575984.325e18
address internal voter7 = 0x9564177EC8052C92752a488a71769F710aA0A41D;
bytes32 internal voter7Pool = 0x0000000000000000000000000000000000000000000000000000000000000025;
bytes32[] internal voter7_operated_poolIds = [voter7Pool];
IERC20 internal token;
IERC20 internal maticToken;
IERC20 internal wceloToken;
IERC20 internal wyvToken;
IZeroExMock internal exchange;
IZrxTreasuryMock internal treasury;
IZrxVaultMock internal vault;
IStakingMock internal staking;
IERC20 internal weth;
ZRXWrappedToken internal wToken;
ZeroExVotes internal votes;
ZeroExTimelock internal protocolTimelock;
ZeroExTimelock internal treasuryTimelock;
ZeroExProtocolGovernor internal protocolGovernor;
ZeroExTreasuryGovernor internal treasuryGovernor;
function setUp() public {
mainnetFork = vm.createFork(MAINNET_RPC_URL);
vm.selectFork(mainnetFork);
token = IERC20(ZRX_TOKEN);
maticToken = IERC20(MATIC_TOKEN);
wceloToken = IERC20(WCELO_TOKEN);
wyvToken = IERC20(WYV_TOKEN);
exchange = IZeroExMock(payable(EXCHANGE_PROXY));
treasury = IZrxTreasuryMock(TREASURY);
vault = IZrxVaultMock(ZRX_VAULT);
staking = IStakingMock(STAKING);
weth = IERC20(staking.getWethContract());
address protocolGovernorAddress;
address treasuryGovernorAddress;
(
wToken,
votes,
protocolTimelock,
treasuryTimelock,
protocolGovernorAddress,
treasuryGovernorAddress
) = setupGovernance(token);
protocolGovernor = ZeroExProtocolGovernor(payable(protocolGovernorAddress));
treasuryGovernor = ZeroExTreasuryGovernor(payable(treasuryGovernorAddress));
}
function testProtocolGovernanceMigration() public {
// initially the zrx exchange is owned by the legacy exchange governor
assertEq(exchange.owner(), EXCHANGE_GOVERNOR);
// transfer ownership to new protocol governor
vm.prank(EXCHANGE_GOVERNOR);
exchange.transferOwnership(address(protocolGovernor));
assertEq(exchange.owner(), address(protocolGovernor));
}
function testTreasuryGovernanceMigration() public {
// Create a proposal to migrate to new governor
uint256 currentEpoch = staking.currentEpoch();
uint256 executionEpoch = currentEpoch + 2;
vm.startPrank(voter3);
IZrxTreasuryMock.ProposedAction[] memory actions = new IZrxTreasuryMock.ProposedAction[](4);
// Transfer MATIC
uint256 maticBalance = maticToken.balanceOf(address(treasury));
actions[0] = IZrxTreasuryMock.ProposedAction({
target: MATIC_TOKEN,
data: abi.encodeCall(maticToken.transfer, (address(treasuryGovernor), maticBalance)),
value: 0
});
// Transfer ZRX
uint256 zrxBalance = token.balanceOf(address(treasury));
actions[1] = IZrxTreasuryMock.ProposedAction({
target: ZRX_TOKEN,
data: abi.encodeCall(token.transfer, (address(treasuryGovernor), zrxBalance)),
value: 0
});
// Transfer wCELO
uint256 wceloBalance = wceloToken.balanceOf(address(treasury));
actions[2] = IZrxTreasuryMock.ProposedAction({
target: WCELO_TOKEN,
data: abi.encodeCall(wceloToken.transfer, (address(treasuryGovernor), wceloBalance)),
value: 0
});
// Transfer WYV
uint256 wyvBalance = wyvToken.balanceOf(address(treasury));
actions[3] = IZrxTreasuryMock.ProposedAction({
target: WYV_TOKEN,
data: abi.encodeCall(wyvToken.transfer, (address(treasuryGovernor), wyvBalance)),
value: 0
});
uint256 proposalId = treasury.propose(
actions,
executionEpoch,
"Z-5 Migrate to new treasury governor",
voter3_operated_poolIds
);
// Once a proposal is created, it becomes open for voting at the epoch after next (currentEpoch + 2)
// and is open for the voting period (currently set to 3 days).
uint256 epochDurationInSeconds = staking.epochDurationInSeconds(); // Currently set to 604800 seconds = 7 days
uint256 currentEpochEndTime = staking.currentEpochStartTimeInSeconds() + epochDurationInSeconds;
vm.warp(currentEpochEndTime + 1);
staking.endEpoch();
vm.warp(block.timestamp + epochDurationInSeconds + 1);
staking.endEpoch();
vm.stopPrank();
// quorum is 10,000,000e18 so reach that via the following votes
vm.prank(voter1);
treasury.castVote(proposalId, true, voter1_operated_poolIds);
vm.stopPrank();
vm.prank(voter2);
treasury.castVote(proposalId, true, voter2_operated_poolIds);
vm.stopPrank();
vm.prank(voter3);
treasury.castVote(proposalId, true, voter3_operated_poolIds);
vm.stopPrank();
vm.prank(voter4);
treasury.castVote(proposalId, true, voter4_operated_poolIds);
vm.stopPrank();
vm.prank(voter5);
treasury.castVote(proposalId, true, voter5_operated_poolIds);
vm.stopPrank();
vm.prank(voter6);
treasury.castVote(proposalId, true, voter6_operated_poolIds);
vm.stopPrank();
vm.prank(voter7);
treasury.castVote(proposalId, true, voter7_operated_poolIds);
vm.stopPrank();
vm.warp(block.timestamp + 3 days + 1);
// Execute proposal
treasury.execute(proposalId, actions);
// Assert value of treasury has correctly transferred
uint256 maticBalanceNewTreasury = maticToken.balanceOf(address(treasuryGovernor));
assertEq(maticBalanceNewTreasury, maticBalance);
uint256 zrxBalanceNewTreasury = token.balanceOf(address(treasuryGovernor));
assertEq(zrxBalanceNewTreasury, zrxBalance);
uint256 wceloBalanceNewTreasury = wceloToken.balanceOf(address(treasuryGovernor));
assertEq(wceloBalanceNewTreasury, wceloBalance);
uint256 wyvBalanceNewTreasury = wyvToken.balanceOf(address(treasuryGovernor));
assertEq(wyvBalanceNewTreasury, wyvBalance);
}
// Test entering catastrophic failure mode on the zrx vault to decomission v3 staking
function testCatastrophicFailureModeOnStaking() public {
DelegatorPool[5] memory delegatorPools = [
DelegatorPool(
0x0ee1F33A2EB0da738FdF035C48d62d75e996a3bd,
0x0000000000000000000000000000000000000000000000000000000000000016
),
DelegatorPool(
0xcAb3d8cBBb3dA1bDabfB003B9C828B27a821717f,
0x0000000000000000000000000000000000000000000000000000000000000017
),
DelegatorPool(
0x7f88b00Db27a500fBfA7EbC9c3CaA2Dea6F59d5b,
0x0000000000000000000000000000000000000000000000000000000000000014
),
DelegatorPool(
0xcE266E6123B682f7A7388097e2155b5379D9AC78,
0x0000000000000000000000000000000000000000000000000000000000000014
),
DelegatorPool(
0xBa4f44E774158408E2DC6c5cb65BC995F0a89180, // pool operator
0x0000000000000000000000000000000000000000000000000000000000000017
)
];
// Enter catastrophic failure mode on the zrx vault
vm.prank(STAKING_AND_VAULT_OWNER);
vault.enterCatastrophicFailure();
vm.stopPrank();
// Stakes can still be withdrawn
// staker1 withdraws
uint256 stake1 = vault.balanceOf(staker1);
uint256 balance1 = token.balanceOf(staker1);
assertGt(stake1, 0);
vm.prank(staker1);
vault.withdrawAllFrom(staker1);
vm.stopPrank();
assertEq(vault.balanceOf(staker1), 0);
assertEq(token.balanceOf(staker1), stake1 + balance1);
// staker2 withdraws
uint256 stake2 = vault.balanceOf(staker2);
uint256 balance2 = token.balanceOf(staker2);
assertGt(stake2, 0);
vm.prank(staker2);
vault.withdrawAllFrom(staker2);
vm.stopPrank();
assertEq(vault.balanceOf(staker2), 0);
assertEq(token.balanceOf(staker2), stake2 + balance2);
// Test top stakers can withdraw
for (uint256 i = 0; i < topStakers.length; i++) {
address staker = topStakers[i];
uint256 stake = vault.balanceOf(staker);
uint256 balance = token.balanceOf(staker);
assertGt(stake, 0);
vm.prank(staker);
vault.withdrawAllFrom(staker);
vm.stopPrank();
assertEq(vault.balanceOf(staker), 0);
assertEq(token.balanceOf(staker), stake + balance);
}
// Delegator can withdraw rewards
for (uint256 i = 0; i < delegatorPools.length; i++) {
address delegator = delegatorPools[i].delegator;
bytes32 pool = delegatorPools[i].pool;
uint256 reward = staking.computeRewardBalanceOfDelegator(pool, delegator);
assertGt(reward, 0);
uint256 balanceBeforeReward = weth.balanceOf(delegator);
vm.prank(delegator);
staking.withdrawDelegatorRewards(pool);
vm.stopPrank();
assertEq(weth.balanceOf(delegator), balanceBeforeReward + reward);
}
}
function testSwitchDelegationInCatastrophicMode() public {
// Enter catastrophic failure mode on the zrx vault
vm.prank(STAKING_AND_VAULT_OWNER);
vault.enterCatastrophicFailure();
// 0x delegator
address delegator = 0x5775afA796818ADA27b09FaF5c90d101f04eF600;
uint256 stake = vault.balanceOf(delegator);
uint256 balance = token.balanceOf(delegator);
assertGt(stake, 0);
// Withdraw stake all at once
vm.startPrank(delegator);
vault.withdrawAllFrom(delegator);
assertEq(vault.balanceOf(delegator), 0);
assertEq(token.balanceOf(delegator), stake + balance);
// delegate 1M ZRX to 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08
vm.roll(block.number + 1);
address delegate = 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08;
uint256 amountToDelegate = 1000000e18;
// Approve the wrapped token and deposit 1m ZRX
token.approve(address(wToken), amountToDelegate);
wToken.depositFor(delegator, amountToDelegate);
assertEq(wToken.balanceOf(delegator), amountToDelegate);
vm.roll(block.number + 1);
wToken.delegate(delegate);
vm.stopPrank();
assertEq(votes.getVotes(delegate), amountToDelegate);
assertEq(votes.getQuadraticVotes(delegate), amountToDelegate);
}
function testSwitchDelegationInNormalOperationMode() public {
// 0x delegator
address delegator = 0x5775afA796818ADA27b09FaF5c90d101f04eF600;
uint256 balance = token.balanceOf(delegator);
// Undelegate stake from pool 0x35
vm.startPrank(delegator);
staking.moveStake(
IStructs.StakeInfo(
IStructs.StakeStatus.DELEGATED,
0x0000000000000000000000000000000000000000000000000000000000000035
),
IStructs.StakeInfo(IStructs.StakeStatus.UNDELEGATED, bytes32(0)),
3000000000000000000000000
);
// Undelegate stake from pool 0x38
IStructs.StoredBalance memory storedBalance38 = staking.getStakeDelegatedToPoolByOwner(
delegator,
0x0000000000000000000000000000000000000000000000000000000000000038
);
staking.moveStake(
IStructs.StakeInfo(
IStructs.StakeStatus.DELEGATED,
0x0000000000000000000000000000000000000000000000000000000000000038
),
IStructs.StakeInfo(IStructs.StakeStatus.UNDELEGATED, bytes32(0)),
storedBalance38.currentEpochBalance
);
// Warp past an epochs and unstake
uint256 epochEndTime = staking.getCurrentEpochEarliestEndTimeInSeconds();
vm.warp(epochEndTime + 1);
staking.endEpoch();
staking.unstake(6000000000000000000000000);
vm.stopPrank();
assertEq(token.balanceOf(delegator), balance + 2 * 3000000000000000000000000);
// delegate 1M ZRX to 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08
vm.roll(block.number + 1);
address delegate = 0x4990cE223209FCEc4ec4c1ff6E0E81eebD8Cca08;
uint256 amountToDelegate = 1000000e18;
// Approve the wrapped token and deposit 1m ZRX
token.approve(address(wToken), amountToDelegate);
wToken.depositFor(delegator, amountToDelegate);
assertEq(wToken.balanceOf(delegator), amountToDelegate);
vm.roll(block.number + 1);
wToken.delegate(delegate);
vm.stopPrank();
assertEq(votes.getVotes(delegate), amountToDelegate);
assertEq(votes.getQuadraticVotes(delegate), amountToDelegate);
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,36 @@
// 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 "@0x/contracts-utils/contracts/src/v08/interfaces/IOwnableV08.sol";
/// @dev Owner management and migration features.
interface IOwnableFeature is IOwnableV08 {
/// @dev Emitted when `migrate()` is called.
/// @param caller The caller of `migrate()`.
/// @param migrator The migration contract.
/// @param newOwner The address of the new owner.
event Migrated(address caller, address migrator, address newOwner);
/// @dev Execute a migration function in the context of the ZeroEx contract.
/// The result of the function being called should be the magic bytes
/// 0x2c64c5ef (`keccack('MIGRATE_SUCCESS')`). Only callable by the owner.
/// The owner will be temporarily set to `address(this)` inside the call.
/// Before returning, the owner will be set to `newOwner`.
/// @param target The migrator contract address.
/// @param newOwner The address of the new owner.
/// @param data The call data.
function migrate(address target, bytes calldata data, address newOwner) external;
}

View File

@ -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.19;
pragma experimental ABIEncoderV2;
/// @dev Basic registry management features.
interface ISimpleFunctionRegistryFeature {
/// @dev A function implementation was updated via `extend()` or `rollback()`.
/// @param selector The function selector.
/// @param oldImpl The implementation contract address being replaced.
/// @param newImpl The replacement implementation contract address.
event ProxyFunctionUpdated(bytes4 indexed selector, address oldImpl, address newImpl);
/// @dev Roll back to a prior implementation of a function.
/// @param selector The function selector.
/// @param targetImpl The address of an older implementation of the function.
function rollback(bytes4 selector, address targetImpl) external;
/// @dev Register or replace a function.
/// @param selector The function selector.
/// @param impl The implementation contract for the function.
function extend(bytes4 selector, address impl) external;
/// @dev Retrieve the length of the rollback history for a function.
/// @param selector The function selector.
/// @return rollbackLength The number of items in the rollback history for
/// the function.
function getRollbackLength(bytes4 selector) external view returns (uint256 rollbackLength);
/// @dev Retrieve an entry in the rollback history for a function.
/// @param selector The function selector.
/// @param idx The index in the rollback history.
/// @return impl An implementation address for the function at
/// index `idx`.
function getRollbackEntryAtIndex(bytes4 selector, uint256 idx) external view returns (address impl);
}

View File

@ -0,0 +1,196 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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.
*/
import "./IZrxVaultMock.sol";
import "./IStructs.sol";
import "./IStorageMock.sol";
pragma solidity ^0.8.19;
interface IStakingMock is IStorageMock {
/// @dev Adds a new exchange address
/// @param addr Address of exchange contract to add
function addExchangeAddress(address addr) external;
/// @dev Create a new staking pool. The sender will be the operator of this pool.
/// Note that an operator must be payable.
/// @param operatorShare Portion of rewards owned by the operator, in ppm.
/// @param addOperatorAsMaker Adds operator to the created pool as a maker for convenience iff true.
/// @return poolId The unique pool id generated for this pool.
function createStakingPool(uint32 operatorShare, bool addOperatorAsMaker) external returns (bytes32 poolId);
/// @dev Decreases the operator share for the given pool (i.e. increases pool rewards for members).
/// @param poolId Unique Id of pool.
/// @param newOperatorShare The newly decreased percentage of any rewards owned by the operator.
function decreaseStakingPoolOperatorShare(bytes32 poolId, uint32 newOperatorShare) external;
/// @dev Begins a new epoch, preparing the prior one for finalization.
/// Throws if not enough time has passed between epochs or if the
/// previous epoch was not fully finalized.
/// @return numPoolsToFinalize The number of unfinalized pools.
function endEpoch() external returns (uint256);
/// @dev Instantly finalizes a single pool that earned rewards in the previous
/// epoch, crediting it rewards for members and withdrawing operator's
/// rewards as WETH. This can be called by internal functions that need
/// to finalize a pool immediately. Does nothing if the pool is already
/// finalized or did not earn rewards in the previous epoch.
/// @param poolId The pool ID to finalize.
function finalizePool(bytes32 poolId) external;
/// @dev Initialize storage owned by this contract.
/// This function should not be called directly.
/// The StakingProxy contract will call it in `attachStakingContract()`.
function init() external;
/// @dev Allows caller to join a staking pool as a maker.
/// @param poolId Unique id of pool.
function joinStakingPoolAsMaker(bytes32 poolId) external;
/// @dev Moves stake between statuses: 'undelegated' or 'delegated'.
/// Delegated stake can also be moved between pools.
/// This change comes into effect next epoch.
/// @param from status to move stake out of.
/// @param to status to move stake into.
/// @param amount of stake to move.
function moveStake(IStructs.StakeInfo calldata from, IStructs.StakeInfo calldata to, uint256 amount) external;
/// @dev Pays a protocol fee in ETH.
/// @param makerAddress The address of the order's maker.
/// @param payerAddress The address that is responsible for paying the protocol fee.
/// @param protocolFee The amount of protocol fees that should be paid.
function payProtocolFee(address makerAddress, address payerAddress, uint256 protocolFee) external payable;
/// @dev Removes an existing exchange address
/// @param addr Address of exchange contract to remove
function removeExchangeAddress(address addr) external;
/// @dev Set all configurable parameters at once.
/// @param _epochDurationInSeconds Minimum seconds between epochs.
/// @param _rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
/// @param _minimumPoolStake Minimum amount of stake required in a pool to collect rewards.
/// @param _cobbDouglasAlphaNumerator Numerator for cobb douglas alpha factor.
/// @param _cobbDouglasAlphaDenominator Denominator for cobb douglas alpha factor.
function setParams(
uint256 _epochDurationInSeconds,
uint32 _rewardDelegatedStakeWeight,
uint256 _minimumPoolStake,
uint32 _cobbDouglasAlphaNumerator,
uint32 _cobbDouglasAlphaDenominator
) external;
/// @dev Stake ZRX tokens. Tokens are deposited into the ZRX Vault.
/// Unstake to retrieve the ZRX. Stake is in the 'Active' status.
/// @param amount of ZRX to stake.
function stake(uint256 amount) external;
/// @dev Unstake. Tokens are withdrawn from the ZRX Vault and returned to
/// the staker. Stake must be in the 'undelegated' status in both the
/// current and next epoch in order to be unstaked.
/// @param amount of ZRX to unstake.
function unstake(uint256 amount) external;
/// @dev Withdraws the caller's WETH rewards that have accumulated
/// until the last epoch.
/// @param poolId Unique id of pool.
function withdrawDelegatorRewards(bytes32 poolId) external;
/// @dev Computes the reward balance in ETH of a specific member of a pool.
/// @param poolId Unique id of pool.
/// @param member The member of the pool.
/// @return reward Balance in ETH.
function computeRewardBalanceOfDelegator(bytes32 poolId, address member) external view returns (uint256 reward);
/// @dev Computes the reward balance in ETH of the operator of a pool.
/// @param poolId Unique id of pool.
/// @return reward Balance in ETH.
function computeRewardBalanceOfOperator(bytes32 poolId) external view returns (uint256 reward);
/// @dev Returns the earliest end time in seconds of this epoch.
/// The next epoch can begin once this time is reached.
/// Epoch period = [startTimeInSeconds..endTimeInSeconds)
/// @return Time in seconds.
function getCurrentEpochEarliestEndTimeInSeconds() external view returns (uint256);
/// @dev Gets global stake for a given status.
/// @param stakeStatus UNDELEGATED or DELEGATED
/// @return balance Global stake for given status.
function getGlobalStakeByStatus(
IStructs.StakeStatus stakeStatus
) external view returns (IStructs.StoredBalance memory balance);
/// @dev Gets an owner's stake balances by status.
/// @param staker Owner of stake.
/// @param stakeStatus UNDELEGATED or DELEGATED
/// @return balance Owner's stake balances for given status.
function getOwnerStakeByStatus(
address staker,
IStructs.StakeStatus stakeStatus
) external view returns (IStructs.StoredBalance memory balance);
/// @dev Retrieves all configurable parameter values.
/// @return _epochDurationInSeconds Minimum seconds between epochs.
/// @return _rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm.
/// @return _minimumPoolStake Minimum amount of stake required in a pool to collect rewards.
/// @return _cobbDouglasAlphaNumerator Numerator for cobb douglas alpha factor.
/// @return _cobbDouglasAlphaDenominator Denominator for cobb douglas alpha factor.
function getParams()
external
view
returns (
uint256 _epochDurationInSeconds,
uint32 _rewardDelegatedStakeWeight,
uint256 _minimumPoolStake,
uint32 _cobbDouglasAlphaNumerator,
uint32 _cobbDouglasAlphaDenominator
);
/// @param staker of stake.
/// @param poolId Unique Id of pool.
/// @return balance Stake delegated to pool by staker.
function getStakeDelegatedToPoolByOwner(
address staker,
bytes32 poolId
) external view returns (IStructs.StoredBalance memory balance);
/// @dev Returns a staking pool
/// @param poolId Unique id of pool.
function getStakingPool(bytes32 poolId) external view returns (IStructs.Pool memory);
/// @dev Get stats on a staking pool in this epoch.
/// @param poolId Pool Id to query.
/// @return PoolStats struct for pool id.
function getStakingPoolStatsThisEpoch(bytes32 poolId) external view returns (IStructs.PoolStats memory);
/// @dev Returns the total stake delegated to a specific staking pool,
/// across all members.
/// @param poolId Unique Id of pool.
/// @return balance Total stake delegated to pool.
function getTotalStakeDelegatedToPool(bytes32 poolId) external view returns (IStructs.StoredBalance memory balance);
/// @dev An overridable way to access the deployed WETH contract.
/// Must be view to allow overrides to access state.
/// @return wethContract The WETH contract instance.
function getWethContract() external view returns (address wethContract);
/// @dev An overridable way to access the deployed zrxVault.
/// Must be view to allow overrides to access state.
/// @return zrxVault The zrxVault contract.
function getZrxVault() external view returns (IZrxVaultMock zrxVault);
}

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 "./IZrxVaultMock.sol";
import "./IStructs.sol";
interface IStorageMock {
function stakingContract() external view returns (address);
function lastPoolId() external view returns (bytes32);
function numMakersByPoolId(bytes32 poolId) external view returns (uint256);
function currentEpoch() external view returns (uint256);
function currentEpochStartTimeInSeconds() external view returns (uint256);
function protocolFeesThisEpochByPool(bytes32 poolId) external view returns (uint256);
function validExchanges(address exchangeAddress) external view returns (bool);
function epochDurationInSeconds() external view returns (uint256);
function rewardDelegatedStakeWeight() external view returns (uint32);
function minimumPoolStake() external view returns (uint256);
function cobbDouglasAlphaNumerator() external view returns (uint32);
function cobbDouglasAlphaDenominator() external view returns (uint32);
}

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 IStructs {
/// @dev Stats for a pool that earned rewards.
/// @param feesCollected Fees collected in ETH by this pool.
/// @param weightedStake Amount of weighted stake in the pool.
/// @param membersStake Amount of non-operator stake in the pool.
struct PoolStats {
uint256 feesCollected;
uint256 weightedStake;
uint256 membersStake;
}
/// @dev Holds stats aggregated across a set of pools.
/// @param rewardsAvailable Rewards (ETH) available to the epoch
/// being finalized (the previous epoch). This is simply the balance
/// of the contract at the end of the epoch.
/// @param numPoolsToFinalize The number of pools that have yet to be finalized through `finalizePools()`.
/// @param totalFeesCollected The total fees collected for the epoch being finalized.
/// @param totalWeightedStake The total fees collected for the epoch being finalized.
/// @param totalRewardsFinalized Amount of rewards that have been paid during finalization.
struct AggregatedStats {
uint256 rewardsAvailable;
uint256 numPoolsToFinalize;
uint256 totalFeesCollected;
uint256 totalWeightedStake;
uint256 totalRewardsFinalized;
}
/// @dev Encapsulates a balance for the current and next epochs.
/// Note that these balances may be stale if the current epoch
/// is greater than `currentEpoch`.
/// @param currentEpoch the current epoch
/// @param currentEpochBalance balance in the current epoch.
/// @param nextEpochBalance balance in `currentEpoch+1`.
struct StoredBalance {
uint64 currentEpoch;
uint96 currentEpochBalance;
uint96 nextEpochBalance;
}
/// @dev Statuses that stake can exist in.
/// Any stake can be (re)delegated effective at the next epoch
/// Undelegated stake can be withdrawn if it is available in both the current and next epoch
enum StakeStatus {
UNDELEGATED,
DELEGATED
}
/// @dev Info used to describe a status.
/// @param status of the stake.
/// @param poolId Unique Id of pool. This is set when status=DELEGATED.
struct StakeInfo {
StakeStatus status;
bytes32 poolId;
}
/// @dev Struct to represent a fraction.
/// @param numerator of fraction.
/// @param denominator of fraction.
struct Fraction {
uint256 numerator;
uint256 denominator;
}
/// @dev Holds the metadata for a staking pool.
/// @param operator of the pool.
/// @param operatorShare Fraction of the total balance owned by the operator, in ppm.
struct Pool {
address operator;
uint32 operatorShare;
}
}

View File

@ -0,0 +1,24 @@
// 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 "./IOwnableFeature.sol";
import "./ISimpleFunctionRegistryFeature.sol";
/// @dev Minimal viable Exchange Proxy interface for governance use.
interface IZeroExMock is IOwnableFeature, ISimpleFunctionRegistryFeature {
/// @dev Fallback for just receiving ether.
receive() external payable;
}

View File

@ -0,0 +1,159 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 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 "./IStakingMock.sol";
/// @dev Minimal viable Treasury interface for governance use.
interface IZrxTreasuryMock {
struct TreasuryParameters {
uint256 votingPeriod;
uint256 proposalThreshold;
uint256 quorumThreshold;
bytes32 defaultPoolId;
}
struct ProposedAction {
address target;
bytes data;
uint256 value;
}
struct Proposal {
bytes32 actionsHash;
uint256 executionEpoch;
uint256 voteEpoch;
uint256 votesFor;
uint256 votesAgainst;
bool executed;
}
event ProposalCreated(
address proposer,
bytes32[] operatedPoolIds,
uint256 proposalId,
ProposedAction[] actions,
uint256 executionEpoch,
string description
);
event VoteCast(address voter, bytes32[] operatedPoolIds, uint256 proposalId, bool support, uint256 votingPower);
event ProposalExecuted(uint256 proposalId);
function stakingProxy() external view returns (IStakingMock);
function defaultPoolId() external view returns (bytes32);
function votingPeriod() external view returns (uint256);
function proposalThreshold() external view returns (uint256);
function quorumThreshold() external view returns (uint256);
/// @dev Updates the proposal and quorum thresholds to the given
/// values. Note that this function is only callable by the
/// treasury contract itself, so the threshold can only be
/// updated via a successful treasury proposal.
/// @param newProposalThreshold The new value for the proposal threshold.
/// @param newQuorumThreshold The new value for the quorum threshold.
function updateThresholds(uint256 newProposalThreshold, uint256 newQuorumThreshold) external;
/// @dev Creates a proposal to send ZRX from this treasury on the
/// the given actions. Must have at least `proposalThreshold`
/// of voting power to call this function. See `getVotingPower`
/// for how voting power is computed. If a proposal is successfully
/// created, voting starts at the epoch after next (currentEpoch + 2).
/// If the vote passes, the proposal is executable during the
/// `executionEpoch`. See `hasProposalPassed` for the passing criteria.
/// @param actions The proposed ZRX actions. An action specifies a
/// contract call.
/// @param executionEpoch The epoch during which the proposal is to
/// be executed if it passes. Must be at least two epochs
/// from the current epoch.
/// @param description A text description for the proposal.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return proposalId The ID of the newly created proposal.
function propose(
ProposedAction[] calldata actions,
uint256 executionEpoch,
string calldata description,
bytes32[] calldata operatedPoolIds
) external returns (uint256 proposalId);
/// @dev Casts a vote for the given proposal. Only callable
/// during the voting period for that proposal.
/// One address can only vote once.
/// See `getVotingPower` for how voting power is computed.
/// @param proposalId The ID of the proposal to vote on.
/// @param support Whether to support the proposal or not.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
function castVote(uint256 proposalId, bool support, bytes32[] calldata operatedPoolIds) external;
/// @dev Casts a vote for the given proposal, by signature.
/// Only callable during the voting period for that proposal.
/// One address/voter can only vote once.
/// See `getVotingPower` for how voting power is computed.
/// @param proposalId The ID of the proposal to vote on.
/// @param support Whether to support the proposal or not.
/// @param operatedPoolIds The pools operated by the signer. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @param v the v field of the signature
/// @param r the r field of the signature
/// @param s the s field of the signature
function castVoteBySignature(
uint256 proposalId,
bool support,
bytes32[] memory operatedPoolIds,
uint8 v,
bytes32 r,
bytes32 s
) external;
/// @dev Executes a proposal that has passed and is
/// currently executable.
/// @param proposalId The ID of the proposal to execute.
/// @param actions Actions associated with the proposal to execute.
function execute(uint256 proposalId, ProposedAction[] memory actions) external payable;
/// @dev Returns the total number of proposals.
/// @return count The number of proposals.
function proposalCount() external view returns (uint256 count);
/// @dev Computes the current voting power of the given account.
/// Voting power is equal to:
/// (ZRX delegated to the default pool) +
/// 0.5 * (ZRX delegated to other pools) +
/// 0.5 * (ZRX delegated to pools operated by account)
/// @param account The address of the account.
/// @param operatedPoolIds The pools operated by `account`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return votingPower The current voting power of the given account.
function getVotingPower(
address account,
bytes32[] calldata operatedPoolIds
) external view returns (uint256 votingPower);
}

View File

@ -0,0 +1,83 @@
/*
Copyright 2019 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 IZrxVaultMock {
/// @dev Emmitted whenever a StakingProxy is set in a vault.
event StakingProxySet(address stakingProxyAddress);
/// @dev Emitted when the Staking contract is put into Catastrophic Failure Mode
/// @param sender Address of sender (`msg.sender`)
event InCatastrophicFailureMode(address sender);
/// @dev Emitted when Zrx Tokens are deposited into the vault.
/// @param staker of Zrx Tokens.
/// @param amount of Zrx Tokens deposited.
event Deposit(address indexed staker, uint256 amount);
/// @dev Emitted when Zrx Tokens are withdrawn from the vault.
/// @param staker of Zrx Tokens.
/// @param amount of Zrx Tokens withdrawn.
event Withdraw(address indexed staker, uint256 amount);
/// @dev Emitted whenever the ZRX AssetProxy is set.
event ZrxProxySet(address zrxProxyAddress);
/// @dev Sets the address of the StakingProxy contract.
/// Note that only the contract staker can call this function.
/// @param _stakingProxyAddress Address of Staking proxy contract.
function setStakingProxy(address _stakingProxyAddress) external;
/// @dev Vault enters into Catastrophic Failure Mode.
/// *** WARNING - ONCE IN CATOSTROPHIC FAILURE MODE, YOU CAN NEVER GO BACK! ***
/// Note that only the contract staker can call this function.
function enterCatastrophicFailure() external;
/// @dev Sets the Zrx proxy.
/// Note that only the contract staker can call this.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param zrxProxyAddress Address of the 0x Zrx Proxy.
function setZrxProxy(address zrxProxyAddress) external;
/// @dev Deposit an `amount` of Zrx Tokens from `staker` into the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param staker of Zrx Tokens.
/// @param amount of Zrx Tokens to deposit.
function depositFrom(address staker, uint256 amount) external;
/// @dev Withdraw an `amount` of Zrx Tokens to `staker` from the vault.
/// Note that only the Staking contract can call this.
/// Note that this can only be called when *not* in Catastrophic Failure mode.
/// @param staker of Zrx Tokens.
/// @param amount of Zrx Tokens to withdraw.
function withdrawFrom(address staker, uint256 amount) external;
/// @dev Withdraw ALL Zrx Tokens to `staker` from the vault.
/// Note that this can only be called when *in* Catastrophic Failure mode.
/// @param staker of Zrx Tokens.
function withdrawAllFrom(address staker) external returns (uint256);
/// @dev Returns the balance in Zrx Tokens of the `staker`
/// @return Balance in Zrx.
function balanceOf(address staker) external view returns (uint256);
/// @dev Returns the entire balance of Zrx tokens in the vault.
function balanceOfZrxVault() external view returns (uint256);
}

View File

@ -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.19;
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);
}
}

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract ZeroExMock {
mapping(bytes4 => address) public implementations;
function rollback(bytes4 selector, address targetImpl) public {
implementations[selector] = targetImpl;
}
}

View File

@ -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, uint256 _quadraticThreshold) ZeroExVotes(_token, _quadraticThreshold) {}
function writeCheckpointTotalSupplyBurn(
uint256 amount,
uint256 accountBalance
) public virtual override onlyToken returns (bool) {
revert("I am evil");
}
}

View File

@ -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 (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);
}
}

View File

@ -0,0 +1,315 @@
// 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 ZRXWrappedTokenTest is BaseTest {
IERC20 private token;
ZRXWrappedToken private wToken;
ZeroExVotes private votes;
function setUp() public {
token = mockZRXToken();
(wToken, votes, , , , ) = setupGovernance(token);
vm.startPrank(account1);
token.transfer(account2, 100e18);
token.transfer(account3, 200e18);
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 testShouldReturnCorrectNumberOfDecimals() public {
uint8 wZRXDecimals = wToken.decimals();
assertEq(wZRXDecimals, 18);
}
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 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);
}
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 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, 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, 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, 5e18 + 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, 1e18 + 2e18);
}
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 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);
assertEq(wToken.balanceOf(account2), 7e18);
assertEq(wToken.balanceOf(account3), 3e18);
assertEq(votes.getVotes(account2), 7e18);
assertEq(votes.getQuadraticVotes(account2), 7e18);
// Since account3 is not delegating to anyone, they should have no voting power
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, 1); // Set to the block.number
vm.roll(3);
wToken.transfer(account3, 3e18);
account2DelegateInfo = wToken.delegateInfo(account2);
assertEq(account2DelegateInfo.delegate, account3);
assertEq(account2DelegateInfo.balanceLastUpdated, 3);
}
}

View File

@ -0,0 +1,453 @@
// 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.19;
import "../BaseTest.t.sol";
import "../../src/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;
event SecurityCouncilAssigned(address securityCouncil);
event SecurityCouncilEjected();
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 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"
// 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"));
assertEq(governor.securityCouncil(), council);
}
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 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"
// 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
vm.expectEmit(true, false, false, false);
emit SecurityCouncilAssigned(account1);
governor.execute(targets, values, calldatas, keccak256("Assign new security council"));
assertEq(governor.securityCouncil(), account1);
}
function testCannotAssignSecurityCouncilOutsideOfGovernance() public {
vm.expectRevert("Governor: onlyGovernance");
governor.assignSecurityCouncil(account1);
}
// This functionality is currently not enabled
// Leaving this test for potential future use.
function testFailSecurityCouncilAreEjectedAfterCancellingAProposal() 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);
vm.expectEmit(true, false, false, false);
emit SecurityCouncilEjected();
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 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"
// 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 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: more than 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);
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"
// 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"));
assertEq(governor.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"
// 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"));
assertEq(governor.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"
// 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"));
assertEq(governor.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"
// 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"));
assertEq(timelock.getMinDelay(), 7 days);
}
function testSupportsGovernanceInterfaces() public {
assertTrue(governor.supportsInterface(type(IGovernorTimelock).interfaceId));
assertTrue(governor.supportsInterface(type(IGovernor).interfaceId));
assertTrue(governor.supportsInterface(type(IERC1155Receiver).interfaceId));
}
}

View File

@ -0,0 +1,168 @@
// 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 "./ZeroExGovernorBaseTest.t.sol";
import "../mocks/ZeroExMock.sol";
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 {
governorName = "ZeroExProtocolGovernor";
proposalThreshold = 1000000e18;
quorum = 10000000e18;
address governorAddress;
token = mockZRXToken();
(wToken, votes, timelock, , governorAddress, ) = setupGovernance(token);
governor = IZeroExGovernor(governorAddress);
protocolGovernor = ZeroExProtocolGovernor(payable(governorAddress));
zeroExMock = new ZeroExMock();
initialiseAccounts();
}
function testShouldReturnCorrectQuorum() public {
assertEq(governor.quorum(block.number), quorum);
}
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.prank(account3);
governor.castVote(proposalId, 0); // Vote "against"
vm.prank(account4);
governor.castVote(proposalId, 2); // Vote "abstain"
// 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));
}
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(true, true, true, true);
emit CallExecuted(proposalId, 0, targets[0], values[0], calldatas[0]);
// 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")));
}
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()");
vm.startPrank(securityCouncil);
vm.expectRevert("ZeroExTimelock: not rollback");
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);
vm.startPrank(account2);
vm.expectRevert("SecurityCouncil: only security council allowed");
protocolGovernor.executeRollback(targets, values, calldatas, keccak256(bytes("Emergency rollback")));
}
}

View File

@ -0,0 +1,96 @@
// 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 "./ZeroExGovernorBaseTest.t.sol";
contract ZeroExTreasuryGovernorTest is ZeroExGovernorBaseTest {
function setUp() public {
governorName = "ZeroExTreasuryGovernor";
proposalThreshold = 250000e18;
address governorAddress;
token = mockZRXToken();
(wToken, votes, , timelock, , governorAddress) = setupGovernance(token);
governor = IZeroExGovernor(governorAddress);
initialiseAccounts();
}
function testShouldReturnCorrectQuorum() public {
vm.roll(3);
uint256 totalSupplyQuadraticVotes = quadraticThreshold *
3 +
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);
}
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.prank(account3);
governor.castVote(proposalId, 0); // Vote "against"
vm.prank(account4);
governor.castVote(proposalId, 2); // Vote "abstain"
// 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, (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));
// 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));
}
}

View File

@ -0,0 +1,435 @@
// 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 "@openzeppelin/token/ERC20/ERC20.sol";
import "../BaseTest.t.sol";
import "../mocks/ZeroExVotesMalicious.sol";
import "../mocks/ZeroExVotesMigration.sol";
import "../../src/ZRXWrappedToken.sol";
import "../../src/ZeroExVotes.sol";
contract ZeroExVotesTest is BaseTest {
IERC20 internal token;
ZRXWrappedToken internal wToken;
ZeroExVotes internal votes;
function setUp() public {
token = mockZRXToken();
(wToken, votes) = setupZRXWrappedToken(token);
vm.startPrank(account1);
token.transfer(account2, 1700000e18);
token.transfer(account3, 1600000e18);
token.transfer(account4, 1000000e18);
vm.stopPrank();
}
function testShouldCorrectlyInitialiseToken() public {
assertEq(votes.token(), address(wToken));
}
function testShouldNotBeAbleToReinitialise() public {
vm.expectRevert("Initializable: contract is already initialized");
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);
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.quadraticThreshold());
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);
token.approve(address(wToken), 1700000e18);
wToken.depositFor(account2, 1700000e18);
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, 1700000e18);
assertEq(checkpoint.quadraticVotes, quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18));
}
function testShouldBeAbleToSelfDelegateVotingPower() public {
// Check voting power initially is 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), 1700000e18);
wToken.depositFor(account2, 1700000e18);
wToken.delegate(account2);
// Check voting power
assertEq(votes.getVotes(account2), 1700000e18);
assertEq(
votes.getQuadraticVotes(account2),
quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18)
);
}
function testShouldBeAbleToDelegateVotingPowerToAnotherAccount() public {
// Check voting power initially is 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), 1700000e18);
wToken.depositFor(account2, 1700000e18);
wToken.delegate(account3);
// Check voting power
assertEq(votes.getVotes(account3), 1700000e18);
assertEq(
votes.getQuadraticVotes(account3),
quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18)
);
}
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), 1700000e18);
wToken.depositFor(account2, 1700000e18);
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), 1700000e18);
assertEq(
votes.getQuadraticVotes(account3),
quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18)
);
}
function testShouldNotBeAbleToDelegateWithSignatureAfterExpiry() public {
uint256 nonce = 0;
uint256 expiry = block.timestamp - 1;
uint256 privateKey = 2;
// Account 2 wraps ZRX and delegates voting power to account3
vm.startPrank(account2);
token.approve(address(wToken), 1700000e18);
wToken.depositFor(account2, 1700000e18);
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.expectRevert("ERC20Votes: signature expired");
wToken.delegateBySig(account3, nonce, expiry, v, r, s);
}
function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnSameBlock() public {
// Check account4 voting power initially is 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), 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), 1600000e18);
wToken.depositFor(account3, 1600000e18);
wToken.delegate(account4);
vm.stopPrank();
// Check voting power
assertEq(votes.getVotes(account4), 3300000e18);
assertEq(
votes.getQuadraticVotes(account4),
quadraticThreshold *
2 +
Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) +
Math.sqrt((1600000e18 - quadraticThreshold) * 1e18)
);
}
function testMultipleAccountsShouldBeAbleToDelegateVotingPowerToAccountWithNoTokensOnDifferentBlock() public {
// Check account4 voting power initially is 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), 1700000e18);
wToken.depositFor(account2, 1700000e18);
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), 1600000e18);
wToken.depositFor(account3, 1600000e18);
wToken.delegate(account4);
vm.stopPrank();
// Check voting power
assertEq(votes.getVotes(account4), 3300000e18);
assertEq(
votes.getQuadraticVotes(account4),
quadraticThreshold *
2 +
Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) +
Math.sqrt((1600000e18 - quadraticThreshold) * 1e18)
);
}
function testComplexDelegationScenario() public {
// Account 2 wraps ZRX and delegates to itself
vm.startPrank(account2);
token.approve(address(wToken), 1700000e18);
wToken.depositFor(account2, 1000000e18);
wToken.delegate(account2);
vm.stopPrank();
assertEq(votes.getVotes(account2), 1000000e18);
assertEq(votes.getQuadraticVotes(account2), 1000000e18);
// Account 3 wraps ZRX and delegates to account4
vm.startPrank(account3);
token.approve(address(wToken), 500000e18);
wToken.depositFor(account3, 500000e18);
wToken.delegate(account4);
vm.stopPrank();
assertEq(votes.getVotes(account4), 500000e18);
assertEq(votes.getQuadraticVotes(account4), 500000e18);
// Voting power distribution now is as follows
// account2 -> account2 1000000e18 | 1000000e18
// account3 -> account4 500000e18 | 500000e18
// Account 2 deposits the remaining 700000e18 and delegates to account3
vm.startPrank(account2);
wToken.depositFor(account2, 700000e18);
wToken.delegate(account3);
vm.stopPrank();
assertEq(votes.getVotes(account3), 1700000e18);
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) * 1e18)
// account3 -> account4 500000e18 | 500000e18
// Account 3 delegates to itself
vm.startPrank(account3);
wToken.delegate(account3);
vm.stopPrank();
assertEq(votes.getVotes(account3), 2200000e18);
assertEq(
votes.getQuadraticVotes(account3),
quadraticThreshold + Math.sqrt((1700000e18 - quadraticThreshold) * 1e18) + 500000e18
);
// Voting power distribution now is as follows
// 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);
assertEq(votes.getQuadraticVotes(account2), 0);
assertEq(votes.getVotes(account4), 0);
assertEq(votes.getQuadraticVotes(account4), 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();
assertEq(votes.numCheckpoints(account2), 1);
IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0);
assertEq(checkpoint1Account2.fromBlock, 1);
assertEq(checkpoint1Account2.votes, 20e18);
assertEq(checkpoint1Account2.quadraticVotes, 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();
assertEq(votes.numCheckpoints(account2), 1);
checkpoint1Account2 = votes.checkpoints(account2, 0);
assertEq(checkpoint1Account2.fromBlock, 1);
assertEq(checkpoint1Account2.votes, 30e18);
assertEq(checkpoint1Account2.quadraticVotes, 20e18 + 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();
assertEq(votes.numCheckpoints(account2), 1);
IZeroExVotes.Checkpoint memory checkpoint1Account2 = votes.checkpoints(account2, 0);
assertEq(checkpoint1Account2.fromBlock, 1);
assertEq(checkpoint1Account2.votes, 20e18);
assertEq(checkpoint1Account2.quadraticVotes, 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();
assertEq(votes.numCheckpoints(account2), 2);
IZeroExVotes.Checkpoint memory checkpoint2Account2 = votes.checkpoints(account2, 1);
assertEq(checkpoint2Account2.fromBlock, 2);
assertEq(checkpoint2Account2.votes, 30e18);
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, 20e18);
}
}

View File

@ -1,4 +1,103 @@
[
{
"timestamp": 1700094997,
"version": "5.4.60",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1693346928,
"version": "5.4.59",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1692368658,
"version": "5.4.58",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1691617396,
"version": "5.4.57",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689974915,
"version": "5.4.56",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689791426,
"version": "5.4.55",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1683749017,
"version": "5.4.54",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1682976338,
"version": "5.4.53",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681969282,
"version": "5.4.52",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681697326,
"version": "5.4.51",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "5.4.50",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1681157139
},
{
"timestamp": 1678410794,
"version": "5.4.49",

View File

@ -5,6 +5,50 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.60 - _November 16, 2023_
* Dependencies updated
## v5.4.59 - _August 29, 2023_
* Dependencies updated
## v5.4.58 - _August 18, 2023_
* Dependencies updated
## v5.4.57 - _August 9, 2023_
* Dependencies updated
## v5.4.56 - _July 21, 2023_
* Dependencies updated
## v5.4.55 - _July 19, 2023_
* Dependencies updated
## v5.4.54 - _May 10, 2023_
* Dependencies updated
## v5.4.53 - _May 1, 2023_
* Dependencies updated
## v5.4.52 - _April 20, 2023_
* Dependencies updated
## v5.4.51 - _April 17, 2023_
* Dependencies updated
## v5.4.50 - _April 10, 2023_
* Dependencies updated
## v5.4.49 - _March 10, 2023_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.49",
"version": "5.4.60",
"engines": {
"node": ">=6.12"
},
@ -39,19 +39,19 @@
"typescript": "4.6.3"
},
"dependencies": {
"@0x/assert": "^3.0.35",
"@0x/assert": "^3.0.36",
"@0x/base-contract": "^7.0.0",
"@0x/contract-addresses": "^8.2.0",
"@0x/dev-utils": "^5.0.0",
"@0x/contract-addresses": "^8.13.0",
"@0x/dev-utils": "^5.0.2",
"@0x/json-schemas": "^6.4.4",
"@0x/order-utils": "^10.4.28",
"@0x/sol-profiler": "^4.1.36",
"@0x/sol-trace": "^3.0.46",
"@0x/subproviders": "^7.0.0",
"@0x/types": "^3.3.6",
"@0x/subproviders": "^8.0.1",
"@0x/types": "^3.3.7",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^7.0.0",
"@0x/web3-wrapper": "^8.0.0",
"@0x/web3-wrapper": "^8.0.1",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",

View File

@ -1,12 +1,9 @@
import { devConstants, env, EnvVars, Web3Config, web3Factory } from '@0x/dev-utils';
import { prependSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { logUtils } from '@0x/utils';
import { Web3ProviderEngine } from '@0x/subproviders';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { constants } from './constants';
import { profiler } from './profiler';
import { revertTrace } from './revert_trace';
export const txDefaults = {
from: devConstants.TESTRPC_FIRST_ADDRESS,
@ -31,26 +28,9 @@ export const providerConfigs: Web3Config = {
export const provider: Web3ProviderEngine = web3Factory.getRpcProvider(providerConfigs);
provider.stop();
const isCoverageEnabled = env.parseBoolean(EnvVars.SolidityCoverage);
const isProfilerEnabled = env.parseBoolean(EnvVars.SolidityProfiler);
const isRevertTraceEnabled = env.parseBoolean(EnvVars.SolidityRevertTrace);
const enabledSubproviderCount = _.filter(
[isCoverageEnabled, isProfilerEnabled, isRevertTraceEnabled],
_.identity.bind(_),
).length;
const enabledSubproviderCount = _.filter([isCoverageEnabled], _.identity.bind(_)).length;
if (enabledSubproviderCount > 1) {
throw new Error(`Only one of profiler or revert trace subproviders can be enabled at a time`);
}
if (isProfilerEnabled) {
const profilerSubprovider = profiler.getProfilerSubproviderSingleton();
logUtils.log(
"By default profilerSubprovider is stopped so that you don't get noise from setup code. Don't forget to start it before the code you want to profile and stop it afterwards",
);
profilerSubprovider.stop();
prependSubprovider(provider, profilerSubprovider);
}
if (isRevertTraceEnabled) {
const revertTraceSubprovider = revertTrace.getRevertTraceSubproviderSingleton();
prependSubprovider(provider, revertTraceSubprovider);
}
export const web3Wrapper = new Web3Wrapper(provider);

View File

@ -1,4 +1,112 @@
[
{
"timestamp": 1700094997,
"version": "1.4.54",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1693346928,
"version": "1.4.53",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1692368658,
"version": "1.4.52",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1691617396,
"version": "1.4.51",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689974915,
"version": "1.4.50",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689791426,
"version": "1.4.49",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1683749017,
"version": "1.4.48",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1682976338,
"version": "1.4.47",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1682334742,
"version": "1.4.46",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681969282,
"version": "1.4.45",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681697326,
"version": "1.4.44",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.4.43",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1681157139
},
{
"timestamp": 1678410794,
"version": "1.4.42",

View File

@ -5,6 +5,54 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.4.54 - _November 16, 2023_
* Dependencies updated
## v1.4.53 - _August 29, 2023_
* Dependencies updated
## v1.4.52 - _August 18, 2023_
* Dependencies updated
## v1.4.51 - _August 9, 2023_
* Dependencies updated
## v1.4.50 - _July 21, 2023_
* Dependencies updated
## v1.4.49 - _July 19, 2023_
* Dependencies updated
## v1.4.48 - _May 10, 2023_
* Dependencies updated
## v1.4.47 - _May 1, 2023_
* Dependencies updated
## v1.4.46 - _April 24, 2023_
* Dependencies updated
## v1.4.45 - _April 20, 2023_
* Dependencies updated
## v1.4.44 - _April 17, 2023_
* Dependencies updated
## v1.4.43 - _April 10, 2023_
* Dependencies updated
## v1.4.42 - _March 10, 2023_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.4.42",
"version": "1.4.54",
"engines": {
"node": ">=6.12"
},
@ -45,13 +45,13 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.8.1",
"@0x/contract-addresses": "^8.2.0",
"@0x/abi-gen": "^5.8.5",
"@0x/contract-addresses": "^8.13.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "3.3.57",
"@0x/contracts-gen": "^2.0.48",
"@0x/contracts-gen": "^2.0.50",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-test-utils": "^5.4.49",
"@0x/contracts-test-utils": "^5.4.60",
"@0x/sol-compiler": "^4.8.2",
"@0x/ts-doc-gen": "^0.0.28",
"@types/isomorphic-fetch": "^0.0.35",
@ -73,12 +73,12 @@
},
"dependencies": {
"@0x/base-contract": "^7.0.0",
"@0x/protocol-utils": "^11.18.1",
"@0x/subproviders": "^7.0.0",
"@0x/types": "^3.3.6",
"@0x/protocol-utils": "^11.24.2",
"@0x/subproviders": "^8.0.1",
"@0x/types": "^3.3.7",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^7.0.0",
"@0x/web3-wrapper": "^8.0.0",
"@0x/web3-wrapper": "^8.0.1",
"ethereum-types": "^3.7.1",
"ethereumjs-util": "^7.0.10"
},

View File

@ -1,4 +1,112 @@
[
{
"timestamp": 1700094997,
"version": "4.8.52",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1693346928,
"version": "4.8.51",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1692368658,
"version": "4.8.50",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1691617396,
"version": "4.8.49",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689974915,
"version": "4.8.48",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1689791426,
"version": "4.8.47",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1683749017,
"version": "4.8.46",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1682976338,
"version": "4.8.45",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1681969282,
"version": "4.8.44",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.8.43",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1681756154
},
{
"timestamp": 1681697326,
"version": "4.8.42",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.8.41",
"changes": [
{
"note": "Dependencies updated"
}
],
"timestamp": 1681157139
},
{
"timestamp": 1678410794,
"version": "4.8.40",

View File

@ -5,6 +5,54 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.8.52 - _November 16, 2023_
* Dependencies updated
## v4.8.51 - _August 29, 2023_
* Dependencies updated
## v4.8.50 - _August 18, 2023_
* Dependencies updated
## v4.8.49 - _August 9, 2023_
* Dependencies updated
## v4.8.48 - _July 21, 2023_
* Dependencies updated
## v4.8.47 - _July 19, 2023_
* Dependencies updated
## v4.8.46 - _May 10, 2023_
* Dependencies updated
## v4.8.45 - _May 1, 2023_
* Dependencies updated
## v4.8.44 - _April 20, 2023_
* Dependencies updated
## v4.8.43 - _April 17, 2023_
* Dependencies updated
## v4.8.42 - _April 17, 2023_
* Dependencies updated
## v4.8.41 - _April 10, 2023_
* Dependencies updated
## v4.8.40 - _March 10, 2023_
* Dependencies updated

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.8.40",
"version": "4.8.52",
"engines": {
"node": ">=6.12"
},
@ -15,7 +15,6 @@
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
@ -44,14 +43,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.8.1",
"@0x/contracts-gen": "^2.0.48",
"@0x/contracts-test-utils": "^5.4.49",
"@0x/dev-utils": "^5.0.0",
"@0x/abi-gen": "^5.8.5",
"@0x/contracts-gen": "^2.0.50",
"@0x/contracts-test-utils": "^5.4.60",
"@0x/dev-utils": "^5.0.2",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.8.2",
"@0x/types": "^3.3.6",
"@0x/web3-wrapper": "^8.0.0",
"@0x/types": "^3.3.7",
"@0x/web3-wrapper": "^8.0.1",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",

View File

@ -1,4 +1,130 @@
[
{
"version": "0.49.0",
"changes": [
{
"note": "Allowlist Sepolia in AbstractBridgeAdapter"
}
],
"timestamp": 1700094997
},
{
"version": "0.48.0",
"changes": [
{
"note": "Add VelodromeV2 support on Base"
}
],
"timestamp": 1693346928
},
{
"version": "0.47.0",
"changes": [
{
"note": "Add MaverickV1 support on Ethereum, BSC, and Base"
}
],
"timestamp": 1692368658
},
{
"version": "0.46.0",
"changes": [
{
"note": "Add VelodromeV2 support on Optimism"
}
],
"timestamp": 1691617396
},
{
"timestamp": 1689974915,
"version": "0.45.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.45.0",
"changes": [
{
"note": "Remove Bancor V1 support"
},
{
"note": "Remove Shell and MStable support"
},
{
"note": "Add Base Mainnet and Goerli BridgeAdapters"
},
{
"note": "Add Uniswap V3 support on Avalanche and BSC"
}
],
"timestamp": 1689791426
},
{
"version": "0.44.0",
"changes": [
{
"note": "Add Trader Joe V2 MixIn to Arbitrum"
}
],
"timestamp": 1683749017
},
{
"version": "0.43.0",
"changes": [
{
"note": "Add Trader Joe V2.1 Router Support for MixIn"
}
],
"timestamp": 1682976338
},
{
"timestamp": 1682334742,
"version": "0.42.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.42.0",
"changes": [
{
"note": "Add Trader Joe V2 support on Avalanche"
}
],
"timestamp": 1681969282
},
{
"version": "0.41.0",
"changes": [
{
"note": "Add Barter support on Ethereum"
}
],
"timestamp": 1681756154
},
{
"version": "0.40.0",
"changes": [
{
"note": "Add Barter support on Ethereum"
}
],
"timestamp": 1681697326
},
{
"version": "0.39.2",
"changes": [
{
"note": "Uprgade dependencies"
}
],
"timestamp": 1681157139
},
{
"version": "0.39.1",
"changes": [

View File

@ -5,6 +5,61 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.49.0 - _November 16, 2023_
* Allowlist Sepolia in AbstractBridgeAdapter
## v0.48.0 - _August 29, 2023_
* Add VelodromeV2 support on Base
## v0.47.0 - _August 18, 2023_
* Add MaverickV1 support on Ethereum, BSC, and Base
## v0.46.0 - _August 9, 2023_
* Add VelodromeV2 support on Optimism
## v0.45.1 - _July 21, 2023_
* Dependencies updated
## v0.45.0 - _July 19, 2023_
* Remove Bancor V1 support
* Remove Shell and MStable support
* Add Base Mainnet and Goerli BridgeAdapters
* Add Uniswap V3 support on Avalanche and BSC
## v0.44.0 - _May 10, 2023_
* Add Trader Joe V2 MixIn to Arbitrum
## v0.43.0 - _May 1, 2023_
* Add Trader Joe V2.1 Router Support for MixIn
## v0.42.1 - _April 24, 2023_
* Dependencies updated
## v0.42.0 - _April 20, 2023_
* Add Trader Joe V2 support on Avalanche
## v0.41.0 - _April 17, 2023_
* Add Barter support on Ethereum
## v0.40.0 - _April 17, 2023_
* Add Barter support on Ethereum
## v0.39.2 - _April 10, 2023_
* Uprgade dependencies
## v0.39.1 - _March 10, 2023_
* Add KyberElastic mixin for Optimism and BSC

View File

@ -31,6 +31,7 @@
"./contracts/src/features/FundRecoveryFeature.sol",
"./contracts/src/features/LiquidityProviderFeature.sol",
"./contracts/src/features/MetaTransactionsFeature.sol",
"./contracts/src/features/MetaTransactionsFeatureV2.sol",
"./contracts/src/features/NativeOrdersFeature.sol",
"./contracts/src/features/OtcOrdersFeature.sol",
"./contracts/src/features/OwnableFeature.sol",
@ -48,6 +49,7 @@
"./contracts/src/features/interfaces/IFundRecoveryFeature.sol",
"./contracts/src/features/interfaces/ILiquidityProviderFeature.sol",
"./contracts/src/features/interfaces/IMetaTransactionsFeature.sol",
"./contracts/src/features/interfaces/IMetaTransactionsFeatureV2.sol",
"./contracts/src/features/interfaces/IMultiplexFeature.sol",
"./contracts/src/features/interfaces/INativeOrdersEvents.sol",
"./contracts/src/features/interfaces/INativeOrdersFeature.sol",
@ -113,6 +115,8 @@
"./contracts/src/transformers/bridges/ArbitrumBridgeAdapter.sol",
"./contracts/src/transformers/bridges/AvalancheBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BSCBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BaseBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BaseGoerliBridgeAdapter.sol",
"./contracts/src/transformers/bridges/BridgeProtocols.sol",
"./contracts/src/transformers/bridges/CeloBridgeAdapter.sol",
"./contracts/src/transformers/bridges/EthereumBridgeAdapter.sol",
@ -123,7 +127,6 @@
"./contracts/src/transformers/bridges/mixins/MixinAaveV2.sol",
"./contracts/src/transformers/bridges/mixins/MixinBalancer.sol",
"./contracts/src/transformers/bridges/mixins/MixinBalancerV2Batch.sol",
"./contracts/src/transformers/bridges/mixins/MixinBancor.sol",
"./contracts/src/transformers/bridges/mixins/MixinBancorV3.sol",
"./contracts/src/transformers/bridges/mixins/MixinCompound.sol",
"./contracts/src/transformers/bridges/mixins/MixinCryptoCom.sol",
@ -134,12 +137,10 @@
"./contracts/src/transformers/bridges/mixins/MixinGMX.sol",
"./contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol",
"./contracts/src/transformers/bridges/mixins/MixinLido.sol",
"./contracts/src/transformers/bridges/mixins/MixinMStable.sol",
"./contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol",
"./contracts/src/transformers/bridges/mixins/MixinMooniswap.sol",
"./contracts/src/transformers/bridges/mixins/MixinNerve.sol",
"./contracts/src/transformers/bridges/mixins/MixinPlatypus.sol",
"./contracts/src/transformers/bridges/mixins/MixinShell.sol",
"./contracts/src/transformers/bridges/mixins/MixinSolidly.sol",
"./contracts/src/transformers/bridges/mixins/MixinSynthetix.sol",
"./contracts/src/transformers/bridges/mixins/MixinUniswap.sol",

@ -1 +1 @@
Subproject commit a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df
Subproject commit fc560fa34fa12a335a50c35d92e55a6628ca467c

View File

@ -20,6 +20,7 @@ import "./features/interfaces/ISimpleFunctionRegistryFeature.sol";
import "./features/interfaces/ITokenSpenderFeature.sol";
import "./features/interfaces/ITransformERC20Feature.sol";
import "./features/interfaces/IMetaTransactionsFeature.sol";
import "./features/interfaces/IMetaTransactionsFeatureV2.sol";
import "./features/interfaces/IUniswapFeature.sol";
import "./features/interfaces/IUniswapV3Feature.sol";
import "./features/interfaces/IPancakeSwapFeature.sol";
@ -39,6 +40,7 @@ interface IZeroEx is
ISimpleFunctionRegistryFeature,
ITransformERC20Feature,
IMetaTransactionsFeature,
IMetaTransactionsFeatureV2,
IUniswapFeature,
IUniswapV3Feature,
IPancakeSwapFeature,

View File

@ -0,0 +1,616 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/IEtherToken.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../errors/LibMetaTransactionsRichErrors.sol";
import "../fixins/FixinCommon.sol";
import "../fixins/FixinReentrancyGuard.sol";
import "../fixins/FixinTokenSpender.sol";
import "../fixins/FixinEIP712.sol";
import "../migrations/LibMigrate.sol";
import "../storage/LibMetaTransactionsV2Storage.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IMetaTransactionsFeatureV2.sol";
import "./interfaces/IMultiplexFeature.sol";
import "./interfaces/INativeOrdersFeature.sol";
import "./interfaces/ITransformERC20Feature.sol";
import "./libs/LibSignature.sol";
/// @dev MetaTransactions feature.
contract MetaTransactionsFeatureV2 is
IFeature,
IMetaTransactionsFeatureV2,
FixinCommon,
FixinReentrancyGuard,
FixinEIP712,
FixinTokenSpender
{
using LibBytesV06 for bytes;
using LibRichErrorsV06 for bytes;
/// @dev Describes the state of a meta transaction.
struct ExecuteState {
// Sender of the meta-transaction.
address sender;
// Hash of the meta-transaction data.
bytes32 hash;
// The meta-transaction data.
MetaTransactionDataV2 mtx;
// The meta-transaction signature (by `mtx.signer`).
LibSignature.Signature signature;
// The selector of the function being called.
bytes4 selector;
// The ETH balance of this contract before performing the call.
uint256 selfBalance;
// The block number at which the meta-transaction was executed.
uint256 executedBlockNumber;
}
/// @dev Arguments for a `TransformERC20.transformERC20()` call.
struct ExternalTransformERC20Args {
IERC20Token inputToken;
IERC20Token outputToken;
uint256 inputTokenAmount;
uint256 minOutputTokenAmount;
ITransformERC20Feature.Transformation[] transformations;
}
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "MetaTransactionsV2";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
bytes32 public immutable MTX_EIP712_TYPEHASH =
keccak256(
"MetaTransactionDataV2("
"address signer,"
"address sender,"
"uint256 expirationTimeSeconds,"
"uint256 salt,"
"bytes callData,"
"address feeToken,"
"MetaTransactionFeeData[] fees"
")"
"MetaTransactionFeeData("
"address recipient,"
"uint256 amount"
")"
);
bytes32 public immutable MTX_FEE_TYPEHASH =
keccak256(
"MetaTransactionFeeData("
"address recipient,"
"uint256 amount"
")"
);
/// @dev The WETH token contract.
IEtherToken private immutable WETH;
/// @dev Ensures that the ETH balance of `this` does not go below the
/// initial ETH balance before the call (excluding ETH attached to the call).
modifier doesNotReduceEthBalance() {
uint256 initialBalance = address(this).balance;
_;
require(initialBalance <= address(this).balance, "MetaTransactionsFeatureV2/ETH_LEAK");
}
constructor(address zeroExAddress, IEtherToken weth) public FixinCommon() FixinEIP712(zeroExAddress) {
WETH = weth;
}
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.
/// @return success `LibMigrate.SUCCESS` on success.
function migrate() external returns (bytes4 success) {
_registerFeatureFunction(this.executeMetaTransactionV2.selector);
_registerFeatureFunction(this.batchExecuteMetaTransactionsV2.selector);
_registerFeatureFunction(this.getMetaTransactionV2ExecutedBlock.selector);
_registerFeatureFunction(this.getMetaTransactionV2HashExecutedBlock.selector);
_registerFeatureFunction(this.getMetaTransactionV2Hash.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Execute a single meta-transaction.
/// @param mtx The meta-transaction.
/// @param signature The signature by `mtx.signer`.
/// @return returnResult The ABI-encoded result of the underlying call.
function executeMetaTransactionV2(
MetaTransactionDataV2 memory mtx,
LibSignature.Signature memory signature
) public override nonReentrant(REENTRANCY_MTX) doesNotReduceEthBalance returns (bytes memory returnResult) {
ExecuteState memory state;
state.sender = msg.sender;
state.mtx = mtx;
state.hash = getMetaTransactionV2Hash(mtx);
state.signature = signature;
returnResult = _executeMetaTransactionPrivate(state);
}
/// @dev Execute multiple meta-transactions.
/// @param mtxs The meta-transactions.
/// @param signatures The signature by each respective `mtx.signer`.
/// @return returnResults The ABI-encoded results of the underlying calls.
function batchExecuteMetaTransactionsV2(
MetaTransactionDataV2[] memory mtxs,
LibSignature.Signature[] memory signatures
) public override nonReentrant(REENTRANCY_MTX) doesNotReduceEthBalance returns (bytes[] memory returnResults) {
if (mtxs.length != signatures.length) {
LibMetaTransactionsRichErrors
.InvalidMetaTransactionsArrayLengthsError(mtxs.length, signatures.length)
.rrevert();
}
returnResults = new bytes[](mtxs.length);
for (uint256 i = 0; i < mtxs.length; ++i) {
ExecuteState memory state;
state.sender = msg.sender;
state.mtx = mtxs[i];
state.hash = getMetaTransactionV2Hash(mtxs[i]);
state.signature = signatures[i];
returnResults[i] = _executeMetaTransactionPrivate(state);
}
}
/// @dev Get the block at which a meta-transaction has been executed.
/// @param mtx The meta-transaction.
/// @return blockNumber The block height when the meta-transactioin was executed.
function getMetaTransactionV2ExecutedBlock(
MetaTransactionDataV2 memory mtx
) public view override returns (uint256 blockNumber) {
return getMetaTransactionV2HashExecutedBlock(getMetaTransactionV2Hash(mtx));
}
/// @dev Get the block at which a meta-transaction hash has been executed.
/// @param mtxHash The meta-transaction hash.
/// @return blockNumber The block height when the meta-transactioin was executed.
function getMetaTransactionV2HashExecutedBlock(bytes32 mtxHash) public view override returns (uint256 blockNumber) {
return LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[mtxHash];
}
/// @dev Get the EIP712 hash of a meta-transaction.
/// @param mtx The meta-transaction.
/// @return mtxHash The EIP712 hash of `mtx`.
function getMetaTransactionV2Hash(MetaTransactionDataV2 memory mtx) public view override returns (bytes32 mtxHash) {
bytes32[] memory feeHashes = new bytes32[](mtx.fees.length);
for (uint256 i = 0; i < mtx.fees.length; ++i) {
feeHashes[i] = keccak256(abi.encode(MTX_FEE_TYPEHASH, mtx.fees[i]));
}
return
_getEIP712Hash(
keccak256(
abi.encode(
MTX_EIP712_TYPEHASH,
mtx.signer,
mtx.sender,
mtx.expirationTimeSeconds,
mtx.salt,
keccak256(mtx.callData),
mtx.feeToken,
keccak256(abi.encodePacked(feeHashes))
)
)
);
}
/// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant.
/// @param state The `ExecuteState` for this metatransaction, with `sender`,
/// `hash`, `mtx`, and `signature` fields filled.
/// @return returnResult The ABI-encoded result of the underlying call.
function _executeMetaTransactionPrivate(ExecuteState memory state) private returns (bytes memory returnResult) {
_validateMetaTransaction(state);
// Mark the transaction executed by storing the block at which it was executed.
// Currently the block number just indicates that the mtx was executed and
// serves no other purpose from within this contract.
LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[state.hash] = block.number;
// Pay the fees to the fee recipients.
for (uint256 i = 0; i < state.mtx.fees.length; ++i) {
_transferERC20TokensFrom(
state.mtx.feeToken,
state.mtx.signer,
state.mtx.fees[i].recipient,
state.mtx.fees[i].amount
);
}
// Execute the call based on the selector.
state.selector = state.mtx.callData.readBytes4(0);
if (state.selector == ITransformERC20Feature.transformERC20.selector) {
returnResult = _executeTransformERC20Call(state);
} else if (state.selector == INativeOrdersFeature.fillLimitOrder.selector) {
returnResult = _executeFillLimitOrderCall(state);
} else if (state.selector == INativeOrdersFeature.fillRfqOrder.selector) {
returnResult = _executeFillRfqOrderCall(state);
} else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForToken.selector) {
returnResult = _executeMultiplexBatchSellTokenForTokenCall(state);
} else if (state.selector == IMultiplexFeature.multiplexBatchSellTokenForEth.selector) {
returnResult = _executeMultiplexBatchSellTokenForEthCall(state);
} else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForToken.selector) {
returnResult = _executeMultiplexMultiHopSellTokenForTokenCall(state);
} else if (state.selector == IMultiplexFeature.multiplexMultiHopSellTokenForEth.selector) {
returnResult = _executeMultiplexMultiHopSellTokenForEthCall(state);
} else {
LibMetaTransactionsRichErrors.MetaTransactionUnsupportedFunctionError(state.hash, state.selector).rrevert();
}
emit MetaTransactionExecuted(state.hash, state.selector, state.mtx.signer, state.mtx.sender);
}
/// @dev Validate that a meta-transaction is executable.
function _validateMetaTransaction(ExecuteState memory state) private view {
// Must be from the required sender, if set.
if (state.mtx.sender != address(0) && state.mtx.sender != state.sender) {
LibMetaTransactionsRichErrors
.MetaTransactionWrongSenderError(state.hash, state.sender, state.mtx.sender)
.rrevert();
}
// Must not be expired.
if (state.mtx.expirationTimeSeconds <= block.timestamp) {
LibMetaTransactionsRichErrors
.MetaTransactionExpiredError(state.hash, block.timestamp, state.mtx.expirationTimeSeconds)
.rrevert();
}
if (LibSignature.getSignerOfHash(state.hash, state.signature) != state.mtx.signer) {
LibSignatureRichErrors
.SignatureValidationError(
LibSignatureRichErrors.SignatureValidationErrorCodes.WRONG_SIGNER,
state.hash,
state.mtx.signer,
// TODO: Remove this field from SignatureValidationError
// when rich reverts are part of the protocol repo.
""
)
.rrevert();
}
// Transaction must not have been already executed.
state.executedBlockNumber = LibMetaTransactionsV2Storage.getStorage().mtxHashToExecutedBlockNumber[state.hash];
if (state.executedBlockNumber != 0) {
LibMetaTransactionsRichErrors
.MetaTransactionAlreadyExecutedError(state.hash, state.executedBlockNumber)
.rrevert();
}
}
/// @dev Execute a `ITransformERC20Feature.transformERC20()` meta-transaction call
/// by decoding the call args and translating the call to the internal
/// `ITransformERC20Feature._transformERC20()` variant, where we can override
/// the taker address.
function _executeTransformERC20Call(ExecuteState memory state) private returns (bytes memory returnResult) {
// HACK(dorothy-zbornak): `abi.decode()` with the individual args
// will cause a stack overflow. But we can prefix the call data with an
// offset to transform it into the encoding for the equivalent single struct arg,
// since decoding a single struct arg consumes far less stack space than
// decoding multiple struct args.
// Where the encoding for multiple args (with the selector ommitted)
// would typically look like:
// | argument | offset |
// |--------------------------|---------|
// | inputToken | 0 |
// | outputToken | 32 |
// | inputTokenAmount | 64 |
// | minOutputTokenAmount | 96 |
// | transformations (offset) | 128 | = 32
// | transformations (data) | 160 |
// We will ABI-decode a single struct arg copy with the layout:
// | argument | offset |
// |--------------------------|---------|
// | (arg 1 offset) | 0 | = 32
// | inputToken | 32 |
// | outputToken | 64 |
// | inputTokenAmount | 96 |
// | minOutputTokenAmount | 128 |
// | transformations (offset) | 160 | = 32
// | transformations (data) | 192 |
ExternalTransformERC20Args memory args;
{
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
// Copy the args data from the original, after the new struct offset prefix.
bytes memory fromCallData = state.mtx.callData;
assert(fromCallData.length >= 160);
uint256 fromMem;
uint256 toMem;
assembly {
// Prefix the calldata with a struct offset,
// which points to just one word over.
mstore(add(encodedStructArgs, 32), 32)
// Copy everything after the selector.
fromMem := add(fromCallData, 36)
// Start copying after the struct offset.
toMem := add(encodedStructArgs, 64)
}
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
// Decode call args for `ITransformERC20Feature.transformERC20()` as a struct.
args = abi.decode(encodedStructArgs, (ExternalTransformERC20Args));
}
// Call `ITransformERC20Feature._transformERC20()` (internal variant).
return
_callSelf(
state.hash,
abi.encodeWithSelector(
ITransformERC20Feature._transformERC20.selector,
ITransformERC20Feature.TransformERC20Args({
taker: state.mtx.signer, // taker is mtx signer
inputToken: args.inputToken,
outputToken: args.outputToken,
inputTokenAmount: args.inputTokenAmount,
minOutputTokenAmount: args.minOutputTokenAmount,
transformations: args.transformations,
useSelfBalance: false,
recipient: state.mtx.signer
})
)
);
}
/// @dev Extract arguments from call data by copying everything after the
/// 4-byte selector into a new byte array.
/// @param callData The call data from which arguments are to be extracted.
/// @return args The extracted arguments as a byte array.
function _extractArgumentsFromCallData(bytes memory callData) private pure returns (bytes memory args) {
args = new bytes(callData.length - 4);
uint256 fromMem;
uint256 toMem;
assembly {
fromMem := add(callData, 36) // skip length and 4-byte selector
toMem := add(args, 32) // write after length prefix
}
LibBytesV06.memCopy(toMem, fromMem, args.length);
return args;
}
/// @dev Execute a `INativeOrdersFeature.fillLimitOrder()` meta-transaction call
/// by decoding the call args and translating the call to the internal
/// `INativeOrdersFeature._fillLimitOrder()` variant, where we can override
/// the taker address.
function _executeFillLimitOrderCall(ExecuteState memory state) private returns (bytes memory returnResult) {
LibNativeOrder.LimitOrder memory order;
LibSignature.Signature memory signature;
uint128 takerTokenFillAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(order, signature, takerTokenFillAmount) = abi.decode(
args,
(LibNativeOrder.LimitOrder, LibSignature.Signature, uint128)
);
return
_callSelf(
state.hash,
abi.encodeWithSelector(
INativeOrdersFeature._fillLimitOrder.selector,
order,
signature,
takerTokenFillAmount,
state.mtx.signer, // taker is mtx signer
msg.sender
)
);
}
/// @dev Execute a `INativeOrdersFeature.fillRfqOrder()` meta-transaction call
/// by decoding the call args and translating the call to the internal
/// `INativeOrdersFeature._fillRfqOrder()` variant, where we can override
/// the taker address.
function _executeFillRfqOrderCall(ExecuteState memory state) private returns (bytes memory returnResult) {
LibNativeOrder.RfqOrder memory order;
LibSignature.Signature memory signature;
uint128 takerTokenFillAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(order, signature, takerTokenFillAmount) = abi.decode(
args,
(LibNativeOrder.RfqOrder, LibSignature.Signature, uint128)
);
return
_callSelf(
state.hash,
abi.encodeWithSelector(
INativeOrdersFeature._fillRfqOrder.selector,
order,
signature,
takerTokenFillAmount,
state.mtx.signer, // taker is mtx signer
false,
state.mtx.signer
)
);
}
/// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForToken()` meta-transaction
/// call by decoding the call args and translating the call to the internal
/// `IMultiplexFeature._multiplexBatchSell()` variant, where we can override the
/// payer address.
function _executeMultiplexBatchSellTokenForTokenCall(
ExecuteState memory state
) private returns (bytes memory returnResult) {
IERC20Token inputToken;
IERC20Token outputToken;
IMultiplexFeature.BatchSellSubcall[] memory calls;
uint256 sellAmount;
uint256 minBuyAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(inputToken, outputToken, calls, sellAmount, minBuyAmount) = abi.decode(
args,
(IERC20Token, IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256)
);
return
_callSelf(
state.hash,
abi.encodeWithSelector(
IMultiplexFeature._multiplexBatchSell.selector,
IMultiplexFeature.BatchSellParams({
inputToken: inputToken,
outputToken: outputToken,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: state.mtx.signer,
payer: state.mtx.signer
}),
minBuyAmount
)
);
}
/// @dev Execute a `IMultiplexFeature.multiplexBatchSellTokenForEth()` meta-transaction
/// call by decoding the call args and translating the call to the internal
/// `IMultiplexFeature._multiplexBatchSellTokenForEth()` variant, where we can override the
/// payer address.
function _executeMultiplexBatchSellTokenForEthCall(
ExecuteState memory state
) private returns (bytes memory returnResult) {
IERC20Token inputToken;
IMultiplexFeature.BatchSellSubcall[] memory calls;
uint256 sellAmount;
uint256 minBuyAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(inputToken, calls, sellAmount, minBuyAmount) = abi.decode(
args,
(IERC20Token, IMultiplexFeature.BatchSellSubcall[], uint256, uint256)
);
returnResult = _callSelf(
state.hash,
abi.encodeWithSelector(
IMultiplexFeature._multiplexBatchSell.selector,
IMultiplexFeature.BatchSellParams({
inputToken: inputToken,
outputToken: IERC20Token(WETH),
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: address(this),
payer: state.mtx.signer
}),
minBuyAmount
)
);
// Unwrap and transfer WETH
uint256 boughtAmount = abi.decode(returnResult, (uint256));
WETH.withdraw(boughtAmount);
_transferEth(state.mtx.signer, boughtAmount);
}
/// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForToken()` meta-transaction
/// call by decoding the call args and translating the call to the internal
/// `IMultiplexFeature._multiplexMultiHopSell()` variant, where we can override the
/// payer address.
function _executeMultiplexMultiHopSellTokenForTokenCall(
ExecuteState memory state
) private returns (bytes memory returnResult) {
address[] memory tokens;
IMultiplexFeature.MultiHopSellSubcall[] memory calls;
uint256 sellAmount;
uint256 minBuyAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(tokens, calls, sellAmount, minBuyAmount) = abi.decode(
args,
(address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256)
);
return
_callSelf(
state.hash,
abi.encodeWithSelector(
IMultiplexFeature._multiplexMultiHopSell.selector,
IMultiplexFeature.MultiHopSellParams({
tokens: tokens,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: state.mtx.signer,
payer: state.mtx.signer
}),
minBuyAmount
)
);
}
/// @dev Execute a `IMultiplexFeature.multiplexMultiHopSellTokenForEth()` meta-transaction
/// call by decoding the call args and translating the call to the internal
/// `IMultiplexFeature._multiplexMultiHopSellTokenForEth()` variant, where we can override the
/// payer address.
function _executeMultiplexMultiHopSellTokenForEthCall(
ExecuteState memory state
) private returns (bytes memory returnResult) {
address[] memory tokens;
IMultiplexFeature.MultiHopSellSubcall[] memory calls;
uint256 sellAmount;
uint256 minBuyAmount;
bytes memory args = _extractArgumentsFromCallData(state.mtx.callData);
(tokens, calls, sellAmount, minBuyAmount) = abi.decode(
args,
(address[], IMultiplexFeature.MultiHopSellSubcall[], uint256, uint256)
);
require(
tokens[tokens.length - 1] == address(WETH),
"MetaTransactionsFeature::multiplexMultiHopSellTokenForEth/NOT_WETH"
);
returnResult = _callSelf(
state.hash,
abi.encodeWithSelector(
IMultiplexFeature._multiplexMultiHopSell.selector,
IMultiplexFeature.MultiHopSellParams({
tokens: tokens,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: address(this),
payer: state.mtx.signer
}),
minBuyAmount
)
);
// Unwrap and transfer WETH
uint256 boughtAmount = abi.decode(returnResult, (uint256));
WETH.withdraw(boughtAmount);
_transferEth(state.mtx.signer, boughtAmount);
}
/// @dev Make an arbitrary internal, meta-transaction call.
/// Warning: Do not let unadulterated `callData` into this function.
function _callSelf(bytes32 hash, bytes memory callData) private returns (bytes memory returnResult) {
bool success;
(success, returnResult) = address(this).call(callData);
if (!success) {
LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(hash, callData, returnResult).rrevert();
}
}
}

View File

@ -70,6 +70,7 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
_registerFeatureFunction(this.sellEthForTokenToUniswapV3.selector);
_registerFeatureFunction(this.sellTokenForEthToUniswapV3.selector);
_registerFeatureFunction(this.sellTokenForTokenToUniswapV3.selector);
_registerFeatureFunction(this._sellTokenForTokenToUniswapV3.selector);
_registerFeatureFunction(this._sellHeldTokenForTokenToUniswapV3.selector);
_registerFeatureFunction(this.uniswapV3SwapCallback.selector);
return LibMigrate.MIGRATE_SUCCESS;
@ -139,6 +140,23 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, msg.sender, _normalizeRecipient(recipient));
}
/// @dev Sell a token for another token directly against uniswap v3. Internal variant.
/// @param encodedPath Uniswap-encoded path.
/// @param sellAmount amount of the first token in the path to sell.
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
/// @param recipient The recipient of the bought tokens. Can be zero for payer.
/// @param payer The address to pull the sold tokens from.
/// @return buyAmount Amount of the last token in the path bought.
function _sellTokenForTokenToUniswapV3(
bytes memory encodedPath,
uint256 sellAmount,
uint256 minBuyAmount,
address recipient,
address payer
) public override onlySelf returns (uint256 buyAmount) {
buyAmount = _swap(encodedPath, sellAmount, minBuyAmount, payer, _normalizeRecipient(recipient, payer));
}
/// @dev Sell a token for another token directly against uniswap v3.
/// Private variant, uses tokens held by `address(this)`.
/// @param encodedPath Uniswap-encoded path.
@ -337,8 +355,16 @@ contract UniswapV3Feature is IFeature, IUniswapV3Feature, FixinCommon, FixinToke
}
}
// Convert null address values to alternative address.
function _normalizeRecipient(
address recipient,
address alternative
) private pure returns (address payable normalizedRecipient) {
return recipient == address(0) ? payable(alternative) : payable(recipient);
}
// Convert null address values to msg.sender.
function _normalizeRecipient(address recipient) private view returns (address payable normalizedRecipient) {
return recipient == address(0) ? msg.sender : payable(recipient);
return _normalizeRecipient(recipient, msg.sender);
}
}

View File

@ -0,0 +1,90 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../libs/LibSignature.sol";
/// @dev Meta-transactions feature.
interface IMetaTransactionsFeatureV2 {
/// @dev Describes an exchange proxy meta transaction.
struct MetaTransactionFeeData {
// ERC20 fee recipient
address recipient;
// ERC20 fee amount
uint256 amount;
}
struct MetaTransactionDataV2 {
// Signer of meta-transaction. On whose behalf to execute the MTX.
address payable signer;
// Required sender, or NULL for anyone.
address sender;
// MTX is invalid after this time.
uint256 expirationTimeSeconds;
// Nonce to make this MTX unique.
uint256 salt;
// Encoded call data to a function on the exchange proxy.
bytes callData;
// ERC20 fee `signer` pays `sender`.
IERC20Token feeToken;
// ERC20 fees.
MetaTransactionFeeData[] fees;
}
/// @dev Emitted whenever a meta-transaction is executed via
/// `executeMetaTransaction()` or `executeMetaTransactions()`.
/// @param hash The EIP712 hash of the MetaTransactionDataV2 struct.
/// @param selector The selector of the function being executed.
/// @param signer Who to execute the meta-transaction on behalf of.
/// @param sender Who executed the meta-transaction.
event MetaTransactionExecuted(bytes32 hash, bytes4 indexed selector, address signer, address sender);
/// @dev Execute a single meta-transaction.
/// @param mtx The meta-transaction.
/// @param signature The signature by `mtx.signer`.
/// @return returnResult The ABI-encoded result of the underlying call.
function executeMetaTransactionV2(
MetaTransactionDataV2 calldata mtx,
LibSignature.Signature calldata signature
) external returns (bytes memory returnResult);
/// @dev Execute multiple meta-transactions.
/// @param mtxs The meta-transactions.
/// @param signatures The signature by each respective `mtx.signer`.
/// @return returnResults The ABI-encoded results of the underlying calls.
function batchExecuteMetaTransactionsV2(
MetaTransactionDataV2[] calldata mtxs,
LibSignature.Signature[] calldata signatures
) external returns (bytes[] memory returnResults);
/// @dev Get the block at which a meta-transaction has been executed.
/// @param mtx The meta-transaction.
/// @return blockNumber The block height when the meta-transactioin was executed.
function getMetaTransactionV2ExecutedBlock(
MetaTransactionDataV2 calldata mtx
) external view returns (uint256 blockNumber);
/// @dev Get the block at which a meta-transaction hash has been executed.
/// @param mtxHash The EIP712 hash of the MetaTransactionDataV2 struct.
/// @return blockNumber The block height when the meta-transactioin was executed.
function getMetaTransactionV2HashExecutedBlock(bytes32 mtxHash) external view returns (uint256 blockNumber);
/// @dev Get the EIP712 hash of a meta-transaction.
/// @param mtx The meta-transaction.
/// @return mtxHash The EIP712 hash of `mtx`.
function getMetaTransactionV2Hash(MetaTransactionDataV2 calldata mtx) external view returns (bytes32 mtxHash);
}

View File

@ -46,6 +46,8 @@ interface IMultiplexFeature {
bool useSelfBalance;
// The recipient of the bought output tokens.
address recipient;
// The sender of the input tokens.
address payer;
}
// Represents a constituent call of a batch sell.
@ -75,6 +77,8 @@ interface IMultiplexFeature {
bool useSelfBalance;
// The recipient of the bought output tokens.
address recipient;
// The sender of the input tokens.
address payer;
}
// Represents a constituent call of a multi-hop sell.
@ -153,6 +157,17 @@ interface IMultiplexFeature {
uint256 minBuyAmount
) external returns (uint256 boughtAmount);
/// @dev Executes a multiplex BatchSell using the given
/// parameters. Internal only.
/// @param params The parameters for the BatchSell.
/// @param minBuyAmount The minimum amount of `params.outputToken`
/// that must be bought for this function to not revert.
/// @return boughtAmount The amount of `params.outputToken` bought.
function _multiplexBatchSell(
BatchSellParams memory params,
uint256 minBuyAmount
) external returns (uint256 boughtAmount);
/// @dev Sells attached ETH via the given sequence of tokens
/// and calls. `tokens[0]` must be WETH.
/// The last token in `tokens` is the output token that
@ -204,4 +219,15 @@ interface IMultiplexFeature {
uint256 sellAmount,
uint256 minBuyAmount
) external returns (uint256 boughtAmount);
/// @dev Executes a multiplex MultiHopSell using the given
/// parameters. Internal only.
/// @param params The parameters for the MultiHopSell.
/// @param minBuyAmount The minimum amount of the output token
/// that must be bought for this function to not revert.
/// @return boughtAmount The amount of the output token bought.
function _multiplexMultiHopSell(
MultiHopSellParams memory params,
uint256 minBuyAmount
) external returns (uint256 boughtAmount);
}

View File

@ -54,6 +54,21 @@ interface IUniswapV3Feature {
address recipient
) external returns (uint256 buyAmount);
/// @dev Sell a token for another token directly against uniswap v3. Internal variant.
/// @param encodedPath Uniswap-encoded path.
/// @param sellAmount amount of the first token in the path to sell.
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
/// @param recipient The recipient of the bought tokens. Can be zero for payer.
/// @param payer The address to pull the sold tokens from.
/// @return buyAmount Amount of the last token in the path bought.
function _sellTokenForTokenToUniswapV3(
bytes memory encodedPath,
uint256 sellAmount,
uint256 minBuyAmount,
address recipient,
address payer
) external returns (uint256 buyAmount);
/// @dev Sell a token for another token directly against uniswap v3.
/// Private variant, uses tokens held by `address(this)`.
/// @param encodedPath Uniswap-encoded path.

View File

@ -80,9 +80,11 @@ contract MultiplexFeature is
_registerFeatureFunction(this.multiplexBatchSellEthForToken.selector);
_registerFeatureFunction(this.multiplexBatchSellTokenForEth.selector);
_registerFeatureFunction(this.multiplexBatchSellTokenForToken.selector);
_registerFeatureFunction(this._multiplexBatchSell.selector);
_registerFeatureFunction(this.multiplexMultiHopSellEthForToken.selector);
_registerFeatureFunction(this.multiplexMultiHopSellTokenForEth.selector);
_registerFeatureFunction(this.multiplexMultiHopSellTokenForToken.selector);
_registerFeatureFunction(this._multiplexMultiHopSell.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
@ -103,14 +105,15 @@ contract MultiplexFeature is
// WETH is now held by this contract,
// so `useSelfBalance` is true.
return
_multiplexBatchSell(
_multiplexBatchSellPrivate(
BatchSellParams({
inputToken: WETH,
outputToken: outputToken,
sellAmount: msg.value,
calls: calls,
useSelfBalance: true,
recipient: msg.sender
recipient: msg.sender,
payer: msg.sender
}),
minBuyAmount
);
@ -133,14 +136,15 @@ contract MultiplexFeature is
// The outputToken is implicitly WETH. The `recipient`
// of the WETH is set to this contract, since we
// must unwrap the WETH and transfer the resulting ETH.
boughtAmount = _multiplexBatchSell(
boughtAmount = _multiplexBatchSellPrivate(
BatchSellParams({
inputToken: inputToken,
outputToken: WETH,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: address(this)
recipient: address(this),
payer: msg.sender
}),
minBuyAmount
);
@ -167,26 +171,40 @@ contract MultiplexFeature is
uint256 minBuyAmount
) public override returns (uint256 boughtAmount) {
return
_multiplexBatchSell(
_multiplexBatchSellPrivate(
BatchSellParams({
inputToken: inputToken,
outputToken: outputToken,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: msg.sender
recipient: msg.sender,
payer: msg.sender
}),
minBuyAmount
);
}
/// @dev Executes a batch sell and checks that at least
/// `minBuyAmount` of `outputToken` was bought. Internal variant.
/// @param params Batch sell parameters.
/// @param minBuyAmount The minimum amount of `outputToken` that
/// must be bought for this function to not revert.
/// @return boughtAmount The amount of `outputToken` bought.
function _multiplexBatchSell(
BatchSellParams memory params,
uint256 minBuyAmount
) public override onlySelf returns (uint256 boughtAmount) {
return _multiplexBatchSellPrivate(params, minBuyAmount);
}
/// @dev Executes a batch sell and checks that at least
/// `minBuyAmount` of `outputToken` was bought.
/// @param params Batch sell parameters.
/// @param minBuyAmount The minimum amount of `outputToken` that
/// must be bought for this function to not revert.
/// @return boughtAmount The amount of `outputToken` bought.
function _multiplexBatchSell(
function _multiplexBatchSellPrivate(
BatchSellParams memory params,
uint256 minBuyAmount
) private returns (uint256 boughtAmount) {
@ -226,13 +244,14 @@ contract MultiplexFeature is
// WETH is now held by this contract,
// so `useSelfBalance` is true.
return
_multiplexMultiHopSell(
_multiplexMultiHopSellPrivate(
MultiHopSellParams({
tokens: tokens,
sellAmount: msg.value,
calls: calls,
useSelfBalance: true,
recipient: msg.sender
recipient: msg.sender,
payer: msg.sender
}),
minBuyAmount
);
@ -262,13 +281,14 @@ contract MultiplexFeature is
);
// The `recipient of the WETH is set to this contract, since
// we must unwrap the WETH and transfer the resulting ETH.
boughtAmount = _multiplexMultiHopSell(
boughtAmount = _multiplexMultiHopSellPrivate(
MultiHopSellParams({
tokens: tokens,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: address(this)
recipient: address(this),
payer: msg.sender
}),
minBuyAmount
);
@ -297,25 +317,38 @@ contract MultiplexFeature is
uint256 minBuyAmount
) public override returns (uint256 boughtAmount) {
return
_multiplexMultiHopSell(
_multiplexMultiHopSellPrivate(
MultiHopSellParams({
tokens: tokens,
sellAmount: sellAmount,
calls: calls,
useSelfBalance: false,
recipient: msg.sender
recipient: msg.sender,
payer: msg.sender
}),
minBuyAmount
);
}
/// @dev Executes a multi-hop sell. Internal variant.
/// @param params Multi-hop sell parameters.
/// @param minBuyAmount The minimum amount of output tokens that
/// must be bought for this function to not revert.
/// @return boughtAmount The amount of output tokens bought.
function _multiplexMultiHopSell(
MultiHopSellParams memory params,
uint256 minBuyAmount
) public override onlySelf returns (uint256 boughtAmount) {
return _multiplexMultiHopSellPrivate(params, minBuyAmount);
}
/// @dev Executes a multi-hop sell and checks that at least
/// `minBuyAmount` of output tokens were bought.
/// @param params Multi-hop sell parameters.
/// @param minBuyAmount The minimum amount of output tokens that
/// must be bought for this function to not revert.
/// @return boughtAmount The amount of output tokens bought.
function _multiplexMultiHopSell(
function _multiplexMultiHopSellPrivate(
MultiHopSellParams memory params,
uint256 minBuyAmount
) private returns (uint256 boughtAmount) {
@ -387,14 +420,14 @@ contract MultiplexFeature is
// amount of the multi-hop fill.
state.outputTokenAmount = params.sellAmount;
// The first call may expect the input tokens to be held by
// `msg.sender`, `address(this)`, or some other address.
// `payer`, `address(this)`, or some other address.
// Compute the expected address and transfer the input tokens
// there if necessary.
state.from = _computeHopTarget(params, 0);
// If the input tokens are currently held by `msg.sender` but
// If the input tokens are currently held by `payer` but
// the first hop expects them elsewhere, perform a `transferFrom`.
if (!params.useSelfBalance && state.from != msg.sender) {
_transferERC20TokensFrom(IERC20Token(params.tokens[0]), msg.sender, state.from, params.sellAmount);
if (!params.useSelfBalance && state.from != params.payer) {
_transferERC20TokensFrom(IERC20Token(params.tokens[0]), params.payer, state.from, params.sellAmount);
}
// If the input tokens are currently held by `address(this)` but
// the first hop expects them elsewhere, perform a `transfer`.
@ -411,11 +444,13 @@ contract MultiplexFeature is
if (subcall.id == MultiplexSubcall.UniswapV2) {
_multiHopSellUniswapV2(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.UniswapV3) {
_multiHopSellUniswapV3(state, subcall.data);
_multiHopSellUniswapV3(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.LiquidityProvider) {
_multiHopSellLiquidityProvider(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.BatchSell) {
_nestedBatchSell(state, params, subcall.data);
} else if (subcall.id == MultiplexSubcall.OTC) {
_multiHopSellOtcOrder(state, params, subcall.data);
} else {
revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL");
}
@ -443,6 +478,8 @@ contract MultiplexFeature is
// Likewise, the recipient of the multi-hop sell is
// equal to the recipient of its containing batch sell.
multiHopParams.recipient = params.recipient;
// The payer is the same too.
multiHopParams.payer = params.payer;
// Execute the nested multi-hop sell.
uint256 outputTokenAmount = _executeMultiHopSell(multiHopParams).outputTokenAmount;
// Increment the sold and bought amounts.
@ -469,7 +506,7 @@ contract MultiplexFeature is
// If the nested batch sell is the first hop
// and `useSelfBalance` for the containing multi-
// hop sell is false, the nested batch sell should
// pull tokens from `msg.sender` (so `batchSellParams.useSelfBalance`
// pull tokens from `payer` (so `batchSellParams.useSelfBalance`
// should be false). Otherwise `batchSellParams.useSelfBalance`
// should be true.
batchSellParams.useSelfBalance = state.hopIndex > 0 || params.useSelfBalance;
@ -477,6 +514,8 @@ contract MultiplexFeature is
// that should receive the output tokens of the
// batch sell.
batchSellParams.recipient = state.to;
// payer shound be the same too.
batchSellParams.payer = params.payer;
// Execute the nested batch sell.
state.outputTokenAmount = _executeBatchSell(batchSellParams).boughtAmount;
}
@ -505,29 +544,33 @@ contract MultiplexFeature is
// is executed, so we the target is the address encoded
// in the subcall data.
(target, ) = abi.decode(subcall.data, (address, bytes));
} else if (subcall.id == MultiplexSubcall.UniswapV3 || subcall.id == MultiplexSubcall.BatchSell) {
} else if (
subcall.id == MultiplexSubcall.UniswapV3 ||
subcall.id == MultiplexSubcall.BatchSell ||
subcall.id == MultiplexSubcall.OTC
) {
// UniswapV3 uses a callback to pull in the tokens being
// sold to it. The callback implemented in `UniswapV3Feature`
// can either:
// - call `transferFrom` to move tokens from `msg.sender` to the
// - call `transferFrom` to move tokens from `payer` to the
// UniswapV3 pool, or
// - call `transfer` to move tokens from `address(this)` to the
// UniswapV3 pool.
// A nested batch sell is similar, in that it can either:
// - use tokens from `msg.sender`, or
// - use tokens from `payer`, or
// - use tokens held by `address(this)`.
// Suppose UniswapV3/BatchSell is the first call in the multi-hop
// path. The input tokens are either held by `msg.sender`,
// path. The input tokens are either held by `payer`,
// or in the case of `multiplexMultiHopSellEthForToken` WETH is
// held by `address(this)`. The target is set accordingly.
// If this is _not_ the first call in the multi-hop path, we
// are dealing with an "intermediate" token in the multi-hop path,
// which `msg.sender` may not have an allowance set for. Thus
// which `payer` may not have an allowance set for. Thus
// target must be set to `address(this)` for `i > 0`.
if (i == 0 && !params.useSelfBalance) {
target = msg.sender;
target = params.payer;
} else {
target = address(this);
}

View File

@ -67,7 +67,7 @@ abstract contract MultiplexLiquidityProvider is FixinCommon, FixinTokenSpender {
_transferERC20Tokens(params.inputToken, provider, sellAmount);
} else {
// Otherwise, transfer the input tokens from `msg.sender`.
_transferERC20TokensFrom(params.inputToken, msg.sender, provider, sellAmount);
_transferERC20TokensFrom(params.inputToken, params.payer, provider, sellAmount);
}
// Cache the recipient's balance of the output token.
uint256 balanceBefore = params.outputToken.balanceOf(params.recipient);

View File

@ -55,7 +55,7 @@ abstract contract MultiplexOtc is FixinEIP712 {
order,
signature,
sellAmount.safeDowncastToUint128(),
msg.sender,
params.payer,
params.useSelfBalance,
params.recipient
)
@ -65,4 +65,34 @@ abstract contract MultiplexOtc is FixinEIP712 {
state.boughtAmount = state.boughtAmount.safeAdd(makerTokenFilledAmount);
} catch {}
}
function _multiHopSellOtcOrder(
IMultiplexFeature.MultiHopSellState memory state,
IMultiplexFeature.MultiHopSellParams memory params,
bytes memory wrappedCallData
) internal {
// Decode the Otc order, and signature.
(LibNativeOrder.OtcOrder memory order, LibSignature.Signature memory signature) = abi.decode(
wrappedCallData,
(LibNativeOrder.OtcOrder, LibSignature.Signature)
);
//Make sure that the otc orders maker and taker tokens match the fill sequence in params.tokens[]
require(
address(order.takerToken) == params.tokens[state.hopIndex] &&
address(order.makerToken) == params.tokens[state.hopIndex + 1],
"MultiplexOtcOrder::_multiHopSellOtcOrder/INVALID_TOKENS"
);
// Try filling the Otc order. Bubble up reverts.
(uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount) = IOtcOrdersFeature(address(this))
._fillOtcOrder(
order,
signature,
state.outputTokenAmount.safeDowncastToUint128(),
state.from,
params.useSelfBalance,
state.to
);
//store the bought amount for the next hop
state.outputTokenAmount = makerTokenFilledAmount;
}
}

View File

@ -54,7 +54,7 @@ abstract contract MultiplexRfq is FixinEIP712 {
order,
signature,
sellAmount.safeDowncastToUint128(),
msg.sender,
params.payer,
params.useSelfBalance,
params.recipient
)

View File

@ -30,8 +30,8 @@ abstract contract MultiplexTransformERC20 {
) internal {
ITransformERC20Feature.TransformERC20Args memory args;
// We want the TransformedERC20 event to have
// `msg.sender` as the taker.
args.taker = msg.sender;
// `payer` as the taker.
args.taker = payable(params.payer);
args.inputToken = params.inputToken;
args.outputToken = params.outputToken;
args.inputTokenAmount = sellAmount;

View File

@ -77,7 +77,7 @@ abstract contract MultiplexUniswapV2 is FixinCommon, FixinTokenSpender {
if (params.useSelfBalance) {
_transferERC20Tokens(IERC20Token(tokens[0]), firstPairAddress, sellAmount);
} else {
_transferERC20TokensFrom(IERC20Token(tokens[0]), msg.sender, firstPairAddress, sellAmount);
_transferERC20TokensFrom(IERC20Token(tokens[0]), params.payer, firstPairAddress, sellAmount);
}
// Execute the Uniswap/Sushiswap trade.
return _sellToUniswapV2(tokens, sellAmount, isSushi, firstPairAddress, params.recipient);

View File

@ -45,16 +45,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
)
);
} else {
// Otherwise, we self-delegatecall the normal variant
// `sellTokenForTokenToUniswapV3`, which pulls the input token
// from `msg.sender`.
(success, resultData) = address(this).delegatecall(
// Otherwise, we self-call `_sellTokenForTokenToUniswapV3`,
// which pulls the input token from a specified `payer`.
(success, resultData) = address(this).call(
abi.encodeWithSelector(
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector,
wrappedCallData,
sellAmount,
0,
params.recipient
params.recipient,
params.payer
)
);
}
@ -69,6 +69,7 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
function _multiHopSellUniswapV3(
IMultiplexFeature.MultiHopSellState memory state,
IMultiplexFeature.MultiHopSellParams memory params,
bytes memory wrappedCallData
) internal {
bool success;
@ -87,16 +88,16 @@ abstract contract MultiplexUniswapV3 is FixinTokenSpender {
)
);
} else {
// Otherwise, we self-delegatecall the normal variant
// `sellTokenForTokenToUniswapV3`, which pulls the input token
// from `msg.sender`.
(success, resultData) = address(this).delegatecall(
// Otherwise, we self-call `_sellTokenForTokenToUniswapV3`,
// which pulls the input token from `payer`.
(success, resultData) = address(this).call(
abi.encodeWithSelector(
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
IUniswapV3Feature._sellTokenForTokenToUniswapV3.selector,
wrappedCallData,
state.outputTokenAmount,
0,
state.to
state.to,
params.payer
)
);
}

View File

@ -0,0 +1,38 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "./LibStorage.sol";
/// @dev Storage helpers for the `MetaTransactions` feature.
library LibMetaTransactionsV2Storage {
/// @dev Storage bucket for this feature.
struct Storage {
// The block number when a hash was executed.
mapping(bytes32 => uint256) mtxHashToExecutedBlockNumber;
}
/// @dev Get the storage bucket for this contract.
function getStorage() internal pure returns (Storage storage stor) {
uint256 storageSlot = LibStorage.getStorageSlot(LibStorage.StorageId.MetaTransactionsV2);
// Dip into assembly to change the slot pointed to by the local variable `stor`.
// solhint-disable-next-line max-line-length
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
assembly {
stor_slot := storageSlot
}
}
}

View File

@ -34,7 +34,8 @@ library LibStorage {
NativeOrders,
OtcOrders,
ERC721Orders,
ERC1155Orders
ERC1155Orders,
MetaTransactionsV2
}
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced slots to storage bucket variables

View File

@ -23,12 +23,14 @@ abstract contract AbstractBridgeAdapter is IBridgeAdapter {
assembly {
chainId := chainid()
}
// Skip chain id validation on Ganache (1337), Anvil (31337), Goerli (5), Mumbai (80001), Base Goerli (84531)
// Skip chain id validation on Ganache (1337), Anvil (31337), Goerli (5), Mumbai (80001), Base Goerli (84531),
// Sepolia (11155111)
bool skipValidation = (chainId == 1337 ||
chainId == 31337 ||
chainId == 5 ||
chainId == 80001 ||
chainId == 84531);
chainId == 84531 ||
chainId == 11155111);
if (chainId != expectedChainId && !skipValidation) {
revert(string(abi.encodePacked(expectedChainName, "BridgeAdapter.constructor: wrong chain ID")));

View File

@ -26,6 +26,7 @@ import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinGMX.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinTraderJoeV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinWOOFi.sol";
@ -42,6 +43,7 @@ contract ArbitrumBridgeAdapter is
MixinKyberElastic,
MixinGMX,
MixinNerve,
MixinTraderJoeV2,
MixinUniswapV3,
MixinUniswapV2,
MixinWOOFi,
@ -97,6 +99,11 @@ contract ArbitrumBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.TRADERJOEV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeTraderJoeV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.GMX) {
if (dryRun) {
return (0, true);

View File

@ -26,7 +26,9 @@ import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinPlatypus.sol";
import "./mixins/MixinTraderJoeV2.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
@ -41,7 +43,9 @@ contract AvalancheBridgeAdapter is
MixinAaveV2,
MixinNerve,
MixinPlatypus,
MixinTraderJoeV2,
MixinUniswapV2,
MixinUniswapV3,
MixinWOOFi,
MixinZeroExBridge
{
@ -70,6 +74,11 @@ contract AvalancheBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV3(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.NERVE) {
if (dryRun) {
return (0, true);
@ -100,6 +109,11 @@ contract AvalancheBridgeAdapter is
return (0, true);
}
boughtAmount = _tradePlatypus(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.TRADERJOEV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeTraderJoeV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.WOOFI) {
if (dryRun) {
return (0, true);

View File

@ -22,9 +22,11 @@ import "./mixins/MixinDodo.sol";
import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinMaverickV1.sol";
import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
@ -35,9 +37,11 @@ contract BSCBridgeAdapter is
MixinDodoV2,
MixinKyberDmm,
MixinKyberElastic,
MixinMaverickV1,
MixinMooniswap,
MixinNerve,
MixinUniswapV2,
MixinUniswapV3,
MixinWOOFi,
MixinZeroExBridge
{
@ -61,6 +65,11 @@ contract BSCBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV3(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MOONISWAP) {
if (dryRun) {
return (0, true);
@ -96,6 +105,11 @@ contract BSCBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeWOOFi(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MAVERICKV1) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeMaverickV1(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) {
return (0, true);

View File

@ -0,0 +1,93 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "./AbstractBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinBalancerV2Batch.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinMaverickV1.sol";
import "./mixins/MixinSolidly.sol";
import "./mixins/MixinVelodromeV2.sol";
contract BaseBridgeAdapter is
AbstractBridgeAdapter(8453, "Base"),
MixinUniswapV3,
MixinUniswapV2,
MixinBalancerV2Batch,
MixinCurve,
MixinCurveV2,
MixinMaverickV1,
MixinSolidly,
MixinVelodromeV2
{
constructor(IEtherToken weth) public MixinCurve(weth) {}
function _trade(
BridgeOrder memory order,
IERC20Token sellToken,
IERC20Token buyToken,
uint256 sellAmount,
bool dryRun
) internal override returns (uint256 boughtAmount, bool supportedSource) {
uint128 protocolId = uint128(uint256(order.source) >> 128);
if (protocolId == BridgeProtocols.CURVE) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeCurve(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.CURVEV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeCurveV2(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV3(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.SOLIDLY) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeSolidly(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeBalancerV2Batch(sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MAVERICKV1) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeMaverickV1(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.VELODROMEV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeVelodromeV2(sellToken, sellAmount, order.bridgeData);
}
emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount);
}
}

View File

@ -0,0 +1,45 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "./AbstractBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinUniswapV2.sol";
contract BaseGoerliBridgeAdapter is AbstractBridgeAdapter(84531, "Base Goerli"), MixinUniswapV3, MixinUniswapV2 {
function _trade(
BridgeOrder memory order,
IERC20Token sellToken,
IERC20Token buyToken,
uint256 sellAmount,
bool dryRun
) internal override returns (uint256 boughtAmount, bool supportedSource) {
uint128 protocolId = uint128(uint256(order.source) >> 128);
if (protocolId == BridgeProtocols.UNISWAPV3) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV3(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeUniswapV2(buyToken, sellAmount, order.bridgeData);
}
emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount);
}
}

View File

@ -55,4 +55,8 @@ library BridgeProtocols {
uint128 internal constant WOOFI = 31;
uint128 internal constant AAVEV3 = 32;
uint128 internal constant KYBERELASTIC = 33;
uint128 internal constant BARTER = 34;
uint128 internal constant TRADERJOEV2 = 35;
uint128 internal constant VELODROMEV2 = 36;
uint128 internal constant MAVERICKV1 = 37;
}

View File

@ -20,8 +20,8 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2Batch.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinBancorV3.sol";
import "./mixins/MixinBarter.sol";
import "./mixins/MixinCompound.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
@ -32,9 +32,8 @@ import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinLido.sol";
import "./mixins/MixinMakerPSM.sol";
import "./mixins/MixinMStable.sol";
import "./mixins/MixinMaverickV1.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinShell.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol";
@ -46,8 +45,8 @@ contract EthereumBridgeAdapter is
MixinAaveV2,
MixinBalancer,
MixinBalancerV2Batch,
MixinBancor,
MixinBancorV3,
MixinBarter,
MixinCompound,
MixinCurve,
MixinCurveV2,
@ -58,9 +57,8 @@ contract EthereumBridgeAdapter is
MixinKyberElastic,
MixinLido,
MixinMakerPSM,
MixinMStable,
MixinMaverickV1,
MixinNerve,
MixinShell,
MixinSynthetix,
MixinUniswap,
MixinUniswapV2,
@ -69,15 +67,7 @@ contract EthereumBridgeAdapter is
{
constructor(
IEtherToken weth
)
public
MixinBancor(weth)
MixinBancorV3(weth)
MixinCompound(weth)
MixinCurve(weth)
MixinLido(weth)
MixinUniswap(weth)
{}
) public MixinBancorV3(weth) MixinCompound(weth) MixinCurve(weth) MixinLido(weth) MixinUniswap(weth) {}
function _trade(
BridgeOrder memory order,
@ -127,16 +117,6 @@ contract EthereumBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeMakerPsm(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MSTABLE) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeMStable(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.SHELL) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeShell(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.DODO) {
if (dryRun) {
return (0, true);
@ -152,11 +132,6 @@ contract EthereumBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeCryptoCom(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.BANCOR) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeBancor(buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.NERVE) {
if (dryRun) {
return (0, true);
@ -197,6 +172,16 @@ contract EthereumBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeSynthetix(sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.BARTER) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeBarter(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MAVERICKV1) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeMaverickV1(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) {
return (0, true);

View File

@ -26,6 +26,7 @@ import "./mixins/MixinNerve.sol";
import "./mixins/MixinSolidly.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinVelodromeV2.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
@ -39,6 +40,7 @@ contract OptimismBridgeAdapter is
MixinNerve,
MixinSynthetix,
MixinUniswapV3,
MixinVelodromeV2,
MixinSolidly,
MixinWOOFi,
MixinZeroExBridge
@ -109,6 +111,11 @@ contract OptimismBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.VELODROMEV2) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeVelodromeV2(sellToken, sellAmount, order.bridgeData);
}
emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount);

View File

@ -26,7 +26,6 @@ import "./mixins/MixinDodo.sol";
import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinKyberElastic.sol";
import "./mixins/MixinMStable.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinSolidly.sol";
import "./mixins/MixinUniswapV2.sol";
@ -45,7 +44,6 @@ contract PolygonBridgeAdapter is
MixinDodoV2,
MixinKyberDmm,
MixinKyberElastic,
MixinMStable,
MixinNerve,
MixinUniswapV2,
MixinUniswapV3,
@ -88,11 +86,6 @@ contract PolygonBridgeAdapter is
return (0, true);
}
boughtAmount = _tradeBalancerV2Batch(sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.MSTABLE) {
if (dryRun) {
return (0, true);
}
boughtAmount = _tradeMStable(sellToken, buyToken, sellAmount, order.bridgeData);
} else if (protocolId == BridgeProtocols.DODO) {
if (dryRun) {
return (0, true);

View File

@ -1,93 +0,0 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-erc20/src/IEtherToken.sol";
import "../IBridgeAdapter.sol";
interface IBancorNetwork {
function convertByPath(
IERC20Token[] calldata _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
address _affiliateAccount,
uint256 _affiliateFee
) external payable returns (uint256);
}
contract MixinBancor {
/// @dev Bancor ETH pseudo-address.
IERC20Token public constant BANCOR_ETH_ADDRESS = IERC20Token(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
IEtherToken private immutable WETH;
constructor(IEtherToken weth) public {
WETH = weth;
}
function _tradeBancor(
IERC20Token buyToken,
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
// Decode the bridge data.
IBancorNetwork bancorNetworkAddress;
IERC20Token[] memory path;
{
address[] memory _path;
(bancorNetworkAddress, _path) = abi.decode(bridgeData, (IBancorNetwork, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly {
path := _path
}
}
require(path.length >= 2, "MixinBancor/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == buyToken || (path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == WETH),
"MixinBancor/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
uint256 payableAmount = 0;
// If it's ETH in the path then withdraw from WETH
// The Bancor path will have ETH as the 0xeee address
// Bancor expects to be paid in ETH not WETH
if (path[0] == BANCOR_ETH_ADDRESS) {
WETH.withdraw(sellAmount);
payableAmount = sellAmount;
} else {
// Grant an allowance to the Bancor Network.
LibERC20TokenV06.approveIfBelow(path[0], address(bancorNetworkAddress), sellAmount);
}
// Convert the tokens
boughtAmount = bancorNetworkAddress.convertByPath{value: payableAmount}(
path, // path originating with source token and terminating in destination token
sellAmount, // amount of source token to trade
1, // minimum amount of destination token expected to receive
address(this), // beneficiary
address(0), // affiliateAccount; no fee paid
0 // affiliateFee; no fee paid
);
if (path[path.length - 1] == BANCOR_ETH_ADDRESS) {
WETH.deposit{value: boughtAmount}();
}
return boughtAmount;
}
}

View File

@ -17,41 +17,25 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
interface IShell {
function originSwap(
IERC20Token from,
IERC20Token to,
uint256 fromAmount,
uint256 minTargetAmount,
uint256 deadline
) external returns (uint256 toAmount);
}
contract MixinShell {
contract MixinBarter {
using LibERC20TokenV06 for IERC20Token;
using LibRichErrorsV06 for bytes;
function _tradeShell(
function _tradeBarter(
IERC20Token sellToken,
IERC20Token buyToken,
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
IShell pool = abi.decode(bridgeData, (IShell));
(address barterRouter, bytes memory data) = abi.decode(bridgeData, (address, bytes));
sellToken.approveIfBelow(barterRouter, sellAmount);
// Grant the Shell contract an allowance to sell the first token.
IERC20Token(sellToken).approveIfBelow(address(pool), sellAmount);
(bool success, bytes memory resultData) = barterRouter.call(data);
if (!success) {
resultData.rrevert();
}
boughtAmount = pool.originSwap(
sellToken,
buyToken,
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.
1,
// deadline
block.timestamp + 1
);
return boughtAmount;
return abi.decode(resultData, (uint256));
}
}

View File

@ -0,0 +1,63 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
interface IMaverickV1Router {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
address pool;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint256 sqrtPriceLimitD18;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
}
contract MixinMaverickV1 {
using LibERC20TokenV06 for IERC20Token;
function _tradeMaverickV1(
IERC20Token sellToken,
IERC20Token buyToken,
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
(IMaverickV1Router router, address pool) = abi.decode(bridgeData, (IMaverickV1Router, address));
// Grant the MaverickV1 router an allowance to sell the sellToken
sellToken.approveIfBelow(address(router), sellAmount);
boughtAmount = router.exactInputSingle(
IMaverickV1Router.ExactInputSingleParams({
tokenIn: address(sellToken),
tokenOut: address(buyToken),
pool: pool,
recipient: address(this),
deadline: block.timestamp,
amountIn: sellAmount,
amountOutMinimum: 1,
sqrtPriceLimitD18: 0
})
);
}
}

View File

@ -0,0 +1,112 @@
// 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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
import "../IBridgeAdapter.sol";
interface ILBRouter {
/**
* @dev This enum represents the version of the pair requested
* - V1: Joe V1 pair
* - V2: LB pair V2. Also called legacyPair
* - V2_1: LB pair V2.1 (current version)
*/
enum Version {
V1,
V2,
V2_1
}
/**
* @dev The path parameters, such as:
* - pairBinSteps: The list of bin steps of the pairs to go through
* - versions: The list of versions of the pairs to go through
* - tokenPath: The list of tokens in the path to go through
*/
struct Path {
uint256[] pairBinSteps;
Version[] versions;
IERC20Token[] tokenPath;
}
/**
* @notice Swaps exact tokens for tokens while performing safety checks
* @param amountIn The amount of token to send
* @param amountOutMin The min amount of token to receive
* @param path The path of the swap
* @param to The address of the recipient
* @param deadline The deadline of the tx
* @return amountOut Output amount of the swap
*/
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) external returns (uint256 amountOut);
}
contract MixinTraderJoeV2 {
using LibERC20TokenV06 for IERC20Token;
function _tradeTraderJoeV2(
IERC20Token buyToken,
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
ILBRouter router;
IERC20Token[] memory tokenPath;
uint256[] memory pairBinSteps;
ILBRouter.Version[] memory versions;
{
address[] memory _tokenPath;
(router, _tokenPath, pairBinSteps, versions) = abi.decode(
bridgeData,
(ILBRouter, address[], uint256[], ILBRouter.Version[])
);
// To get around `abi.decode()` not supporting interface array types.
assembly {
tokenPath := _tokenPath
}
}
require(tokenPath.length >= 2, "MixinTraderJoeV2/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
tokenPath.length == pairBinSteps.length + 1,
"MixinTraderJoeV2/PAIR_BIN_STEPS_LENGTH_MUST_BE_ONE_LESS_THAN_TOKEN_PATH_LENGTH"
);
require(
versions.length == pairBinSteps.length,
"MixinTraderJoeV2/VERSIONS_LENGTH_MUST_BE_EQUAL_TO_PAIR_BIN_STEPS_LENGTH"
);
require(
tokenPath[tokenPath.length - 1] == buyToken,
"MixinTraderJoeV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
// Grant the Trader Joe V2 router an allowance to sell the first token.
tokenPath[0].approveIfBelow(address(router), sellAmount);
ILBRouter.Path memory path = ILBRouter.Path({
pairBinSteps: pairBinSteps,
versions: versions,
tokenPath: tokenPath
});
boughtAmount = router.swapExactTokensForTokens(sellAmount, 1, path, address(this), block.timestamp);
}
}

View File

@ -31,6 +31,18 @@ interface IUniswapV3Router {
function exactInput(ExactInputParams memory params) external payable returns (uint256 amountOut);
}
// https://github.com/Uniswap/swap-router-contracts/blob/main/contracts/interfaces/IV3SwapRouter.sol
interface IUniswapV3Router2 {
struct ExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
function exactInput(ExactInputParams memory params) external payable returns (uint256 amountOut);
}
contract MixinUniswapV3 {
using LibERC20TokenV06 for IERC20Token;
@ -39,19 +51,30 @@ contract MixinUniswapV3 {
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
(IUniswapV3Router router, bytes memory path) = abi.decode(bridgeData, (IUniswapV3Router, bytes));
(address router, bytes memory path, uint256 routerVersion) = abi.decode(bridgeData, (address, bytes, uint256));
// Grant the Uniswap router an allowance to sell the sell token.
sellToken.approveIfBelow(address(router), sellAmount);
sellToken.approveIfBelow(router, sellAmount);
boughtAmount = router.exactInput(
IUniswapV3Router.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: sellAmount,
amountOutMinimum: 1
})
);
if (routerVersion != 2) {
boughtAmount = IUniswapV3Router(router).exactInput(
IUniswapV3Router.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: sellAmount,
amountOutMinimum: 1
})
);
} else {
boughtAmount = IUniswapV3Router2(router).exactInput(
IUniswapV3Router2.ExactInputParams({
path: path,
recipient: address(this),
amountIn: sellAmount,
amountOutMinimum: 1
})
);
}
}
}

View File

@ -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.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/src/IERC20Token.sol";
interface IVelodromeV2Router {
struct Route {
address from;
address to;
bool stable;
address factory;
}
/// @notice Swap one token for another
/// @param amountIn Amount of token in
/// @param amountOutMin Minimum amount of desired token received
/// @param routes Array of trade routes used in the swap
/// @param to Recipient of the tokens received
/// @param deadline Deadline to receive tokens
/// @return amounts Array of amounts returned per route
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
Route[] calldata routes,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
contract MixinVelodromeV2 {
using LibERC20TokenV06 for IERC20Token;
function _tradeVelodromeV2(
IERC20Token sellToken,
uint256 sellAmount,
bytes memory bridgeData
) internal returns (uint256 boughtAmount) {
(IVelodromeV2Router router, IVelodromeV2Router.Route[] memory routes) = abi.decode(
bridgeData,
(IVelodromeV2Router, IVelodromeV2Router.Route[])
);
sellToken.approveIfBelow(address(router), sellAmount);
uint256[] memory amounts = router.swapExactTokensForTokens(
sellAmount,
1,
routes,
address(this),
block.timestamp + 1
);
return amounts[amounts.length - 1];
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.39.1",
"version": "0.49.0",
"engines": {
"node": ">=6.12"
},
@ -36,9 +36,9 @@
"typechain": "typechain --target=ethers-v5 --out-dir='typechain-wrappers' './foundry-artifacts/**/*.json'"
},
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BaseGoerliBridgeAdapter,BaseBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter,MetaTransactionsFeatureV2",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|ArbitrumBridgeAdapter|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSolidly|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AbstractBridgeAdapter|AffiliateFeeTransformer|ArbitrumBridgeAdapter|AvalancheBridgeAdapter|BSCBridgeAdapter|BaseBridgeAdapter|BaseGoerliBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMetaTransactionsFeatureV2|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MetaTransactionsFeatureV2|MixinAaveV2|MixinBalancer|MixinBalancerV2Batch|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinSolidly|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@ -50,12 +50,12 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.8.1",
"@0x/contract-addresses": "^8.2.0",
"@0x/abi-gen": "^5.8.5",
"@0x/contract-addresses": "^8.13.0",
"@0x/contracts-erc20": "^3.3.57",
"@0x/contracts-gen": "^2.0.48",
"@0x/contracts-test-utils": "^5.4.49",
"@0x/dev-utils": "^5.0.0",
"@0x/contracts-gen": "^2.0.50",
"@0x/contracts-test-utils": "^5.4.60",
"@0x/dev-utils": "^5.0.2",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.8.2",
"@0x/ts-doc-gen": "^0.0.28",
@ -80,12 +80,12 @@
},
"dependencies": {
"@0x/base-contract": "^7.0.0",
"@0x/protocol-utils": "^11.18.1",
"@0x/subproviders": "^7.0.0",
"@0x/types": "^3.3.6",
"@0x/protocol-utils": "^11.24.2",
"@0x/subproviders": "^8.0.1",
"@0x/types": "^3.3.7",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^7.0.0",
"@0x/web3-wrapper": "^8.0.0",
"@0x/web3-wrapper": "^8.0.1",
"ethereum-types": "^3.7.1",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4"

View File

@ -1,9 +1,9 @@
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { constants } from '@0x/contracts-test-utils';
import { RPCSubprovider, SupportedProvider, Web3ProviderEngine } from '@0x/subproviders';
import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { AbiEncoder, BigNumber, logUtils, providerUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { MethodAbi } from 'ethereum-types';
import { MethodAbi, SupportedProvider } from 'ethereum-types';
import * as fetch from 'isomorphic-fetch';
import * as _ from 'lodash';
import * as prompts from 'prompts';

View File

@ -7,6 +7,8 @@ import { ContractArtifact } from 'ethereum-types';
import * as AffiliateFeeTransformer from '../generated-artifacts/AffiliateFeeTransformer.json';
import * as AvalancheBridgeAdapter from '../generated-artifacts/AvalancheBridgeAdapter.json';
import * as BaseBridgeAdapter from '../generated-artifacts/BaseBridgeAdapter.json';
import * as BaseGoerliBridgeAdapter from '../generated-artifacts/BaseGoerliBridgeAdapter.json';
import * as BatchFillNativeOrdersFeature from '../generated-artifacts/BatchFillNativeOrdersFeature.json';
import * as BSCBridgeAdapter from '../generated-artifacts/BSCBridgeAdapter.json';
import * as CeloBridgeAdapter from '../generated-artifacts/CeloBridgeAdapter.json';
@ -32,6 +34,7 @@ import * as IZeroEx from '../generated-artifacts/IZeroEx.json';
import * as LiquidityProviderFeature from '../generated-artifacts/LiquidityProviderFeature.json';
import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTransformer.json';
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
import * as MetaTransactionsFeatureV2 from '../generated-artifacts/MetaTransactionsFeatureV2.json';
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.json';
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
import * as OptimismBridgeAdapter from '../generated-artifacts/OptimismBridgeAdapter.json';
@ -78,10 +81,13 @@ export const artifacts = {
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
IOtcOrdersFeature: IOtcOrdersFeature as ContractArtifact,
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
BaseGoerliBridgeAdapter: BaseGoerliBridgeAdapter as ContractArtifact,
BaseBridgeAdapter: BaseBridgeAdapter as ContractArtifact,
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
MetaTransactionsFeatureV2: MetaTransactionsFeatureV2 as ContractArtifact,
};

Some files were not shown because too many files have changed in this diff Show More