Compare commits
30 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
ad2d41fd90 | ||
|
721b984bbc | ||
|
72433ea5b7 | ||
|
f919e8ef02 | ||
|
dbcc7dd6f4 | ||
|
33b7515734 | ||
|
f96b944cce | ||
|
0fbdc1dccd | ||
|
484d9cafc4 | ||
|
e73885c9a0 | ||
|
948b17e2ed | ||
|
2b13dcfc14 | ||
|
d2bea4aa46 | ||
|
dc926a05b3 | ||
|
5cc53597de | ||
|
57ca2c07bb | ||
|
b0d2dee84d | ||
|
1422ad756d | ||
|
9d216f9c1c | ||
|
ff74738178 | ||
|
fe4fe48021 | ||
|
356e74a645 | ||
|
70590e3e56 | ||
|
4e0b671bd8 | ||
|
ea2ca700c3 | ||
|
3574ea5b27 | ||
|
b7bf5b5dfe | ||
|
cfbb9c6f6c | ||
|
7e40dd1826 | ||
|
bcbfbfa16c |
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@@ -31,6 +31,11 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Add foundry
|
||||
uses: foundry-rs/foundry-toolchain@v1
|
||||
with:
|
||||
version: nightly
|
||||
|
||||
- name: Build solution
|
||||
run: yarn build
|
||||
|
||||
@@ -78,11 +83,6 @@ jobs:
|
||||
-p @0x/order-utils \
|
||||
-m --serial -c test:ci
|
||||
|
||||
- name: Add foundry
|
||||
uses: foundry-rs/foundry-toolchain@v1
|
||||
with:
|
||||
version: nightly
|
||||
|
||||
- name: Run Forge build for erc20
|
||||
working-directory: contracts/erc20
|
||||
run: |
|
||||
@@ -135,3 +135,26 @@ jobs:
|
||||
path: ./contracts/zero-ex/lcov.info
|
||||
min_coverage: 6.98
|
||||
exclude: '**/tests'
|
||||
|
||||
- name: Run Forge build on governance contracts
|
||||
working-directory: ./contracts/governance
|
||||
run: |
|
||||
forge --version
|
||||
forge build --sizes
|
||||
|
||||
- name: Run Forge tests on governance contracts
|
||||
working-directory: ./contracts/governance
|
||||
run: |
|
||||
forge test -vvv --gas-report
|
||||
|
||||
- name: Run Forge coverage on governance contracts
|
||||
working-directory: ./contracts/governance
|
||||
run: |
|
||||
forge coverage --report lcov
|
||||
|
||||
- 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
|
||||
|
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -22,6 +22,10 @@ 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 }}
|
||||
|
19
.gitignore
vendored
19
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -90,6 +99,7 @@ foundry-artifacts/
|
||||
|
||||
# foundry cache
|
||||
cache/
|
||||
foundry-cache/
|
||||
|
||||
#foundry output artifacts
|
||||
out/
|
||||
@@ -97,10 +107,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
11
.gitmodules
vendored
@@ -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
|
||||
|
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "4.0.2",
|
||||
"version": "4.0.5",
|
||||
"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.43",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"typedoc": "~0.16.11"
|
||||
},
|
||||
|
38
contracts/governance/CHANGELOG.json
Normal file
38
contracts/governance/CHANGELOG.json
Normal file
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
22
contracts/governance/CHANGELOG.md
Normal file
22
contracts/governance/CHANGELOG.md
Normal file
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
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.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
|
74
contracts/governance/README.md
Normal file
74
contracts/governance/README.md
Normal 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.
|
505
contracts/governance/ZRXToken.json
Normal file
505
contracts/governance/ZRXToken.json
Normal 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
|
||||
}
|
BIN
contracts/governance/audits/0xProtocol-06-04-2023-Final.pdf
Normal file
BIN
contracts/governance/audits/0xProtocol-06-04-2023-Final.pdf
Normal file
Binary file not shown.
BIN
contracts/governance/audits/OxProtocol-31-03-2023-Pre.pdf
Normal file
BIN
contracts/governance/audits/OxProtocol-31-03-2023-Pre.pdf
Normal file
Binary file not shown.
37
contracts/governance/foundry.toml
Normal file
37
contracts/governance/foundry.toml
Normal 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' ] }
|
1
contracts/governance/lib/forge-std
Submodule
1
contracts/governance/lib/forge-std
Submodule
Submodule contracts/governance/lib/forge-std added at eb980e1d4f
1
contracts/governance/lib/openzeppelin-contracts
Submodule
1
contracts/governance/lib/openzeppelin-contracts
Submodule
Submodule contracts/governance/lib/openzeppelin-contracts added at d00acef405
Submodule contracts/governance/lib/openzeppelin-contracts-upgradeable added at f6c4c9c4ec
27
contracts/governance/package.json
Normal file
27
contracts/governance/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@0x/governance",
|
||||
"version": "1.0.4",
|
||||
"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"
|
||||
}
|
80
contracts/governance/script/DeployGovernance.s.sol
Normal file
80
contracts/governance/script/DeployGovernance.s.sol
Normal 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)))))));
|
||||
}
|
||||
}
|
22
contracts/governance/script/DeployToken.s.sol
Normal file
22
contracts/governance/script/DeployToken.s.sol
Normal 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();
|
||||
}
|
||||
}
|
169
contracts/governance/src/CallWithGas.sol
Normal file
169
contracts/governance/src/CallWithGas.sol
Normal 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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
contracts/governance/src/IZeroExGovernor.sol
Normal file
39
contracts/governance/src/IZeroExGovernor.sol
Normal 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);
|
||||
}
|
132
contracts/governance/src/IZeroExVotes.sol
Normal file
132
contracts/governance/src/IZeroExVotes.sol
Normal 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);
|
||||
}
|
85
contracts/governance/src/SecurityCouncil.sol
Normal file
85
contracts/governance/src/SecurityCouncil.sol
Normal 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");
|
||||
}
|
||||
}
|
164
contracts/governance/src/ZRXWrappedToken.sol
Normal file
164
contracts/governance/src/ZRXWrappedToken.sol
Normal 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
|
||||
);
|
||||
}
|
||||
}
|
145
contracts/governance/src/ZeroExProtocolGovernor.sol
Normal file
145
contracts/governance/src/ZeroExProtocolGovernor.sol
Normal 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);
|
||||
}
|
||||
}
|
70
contracts/governance/src/ZeroExTimelock.sol
Normal file
70
contracts/governance/src/ZeroExTimelock.sol
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
154
contracts/governance/src/ZeroExTreasuryGovernor.sol
Normal file
154
contracts/governance/src/ZeroExTreasuryGovernor.sol
Normal 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);
|
||||
}
|
||||
}
|
336
contracts/governance/src/ZeroExVotes.sol
Normal file
336
contracts/governance/src/ZeroExVotes.sol
Normal 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");
|
||||
}
|
||||
}
|
167
contracts/governance/test/BaseTest.t.sol
Normal file
167
contracts/governance/test/BaseTest.t.sol
Normal 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)))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
454
contracts/governance/test/integration/GovernanceE2E.t.sol
Normal file
454
contracts/governance/test/integration/GovernanceE2E.t.sol
Normal 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);
|
||||
}
|
||||
}
|
21
contracts/governance/test/mocks/CubeRoot.sol
Normal file
21
contracts/governance/test/mocks/CubeRoot.sol
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
36
contracts/governance/test/mocks/IOwnableFeature.sol
Normal file
36
contracts/governance/test/mocks/IOwnableFeature.sol
Normal 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;
|
||||
}
|
@@ -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);
|
||||
}
|
196
contracts/governance/test/mocks/IStakingMock.sol
Normal file
196
contracts/governance/test/mocks/IStakingMock.sol
Normal 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);
|
||||
}
|
49
contracts/governance/test/mocks/IStorageMock.sol
Normal file
49
contracts/governance/test/mocks/IStorageMock.sol
Normal 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);
|
||||
}
|
92
contracts/governance/test/mocks/IStructs.sol
Normal file
92
contracts/governance/test/mocks/IStructs.sol
Normal 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;
|
||||
}
|
||||
}
|
24
contracts/governance/test/mocks/IZeroExMock.sol
Normal file
24
contracts/governance/test/mocks/IZeroExMock.sol
Normal 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;
|
||||
}
|
159
contracts/governance/test/mocks/IZrxTreasuryMock.sol
Normal file
159
contracts/governance/test/mocks/IZrxTreasuryMock.sol
Normal 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);
|
||||
}
|
83
contracts/governance/test/mocks/IZrxVaultMock.sol
Normal file
83
contracts/governance/test/mocks/IZrxVaultMock.sol
Normal 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);
|
||||
}
|
31
contracts/governance/test/mocks/ZRXMock.sol
Normal file
31
contracts/governance/test/mocks/ZRXMock.sol
Normal 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);
|
||||
}
|
||||
}
|
11
contracts/governance/test/mocks/ZeroExMock.sol
Normal file
11
contracts/governance/test/mocks/ZeroExMock.sol
Normal 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;
|
||||
}
|
||||
}
|
32
contracts/governance/test/mocks/ZeroExVotesMalicious.sol
Normal file
32
contracts/governance/test/mocks/ZeroExVotesMalicious.sol
Normal 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");
|
||||
}
|
||||
}
|
215
contracts/governance/test/mocks/ZeroExVotesMigration.sol
Normal file
215
contracts/governance/test/mocks/ZeroExVotesMigration.sol
Normal 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);
|
||||
}
|
||||
}
|
315
contracts/governance/test/unit/ZRXWrappedTokenTest.t.sol
Normal file
315
contracts/governance/test/unit/ZRXWrappedTokenTest.t.sol
Normal 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);
|
||||
}
|
||||
}
|
460
contracts/governance/test/unit/ZeroExGovernorBaseTest.t.sol
Normal file
460
contracts/governance/test/unit/ZeroExGovernorBaseTest.t.sol
Normal file
@@ -0,0 +1,460 @@
|
||||
// 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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
|
||||
// Execute proposal
|
||||
governor.execute(targets, values, calldatas, keccak256("Assign new security council"));
|
||||
|
||||
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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Assign new security council")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
|
||||
// Execute proposal
|
||||
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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Set security council to address(0)
|
||||
setSecurityCouncil(address(0));
|
||||
|
||||
vm.expectRevert("SecurityCouncil: security council not assigned and this is not an assignment call");
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description")));
|
||||
|
||||
IGovernor.ProposalState state = governor.state(proposalId);
|
||||
assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded));
|
||||
}
|
||||
|
||||
function testWhenNoSecurityCouncilCanPassProposalToAssignSecurityCouncil() public {
|
||||
setSecurityCouncil(address(0));
|
||||
|
||||
setSecurityCouncil(account1);
|
||||
}
|
||||
|
||||
function 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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting delay to 3 days")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
// Execute proposal
|
||||
governor.execute(targets, values, calldatas, keccak256("Increase voting delay to 3 days"));
|
||||
|
||||
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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Increase voting period to 14 days")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
// Execute proposal
|
||||
governor.execute(targets, values, calldatas, keccak256("Increase voting period to 14 days"));
|
||||
|
||||
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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Increase proposal threshold to 2000000e18")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
// Execute proposal
|
||||
governor.execute(targets, values, calldatas, keccak256("Increase proposal threshold to 2000000e18"));
|
||||
|
||||
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"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Increase timelock delay to 7 days")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
// Execute proposal
|
||||
governor.execute(targets, values, calldatas, keccak256("Increase timelock delay to 7 days"));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
169
contracts/governance/test/unit/ZeroExProtocolGovernor.t.sol
Normal file
169
contracts/governance/test/unit/ZeroExProtocolGovernor.t.sol
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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.stopPrank();
|
||||
vm.prank(account3);
|
||||
governor.castVote(proposalId, 0); // Vote "against"
|
||||
vm.stopPrank();
|
||||
vm.prank(account4);
|
||||
governor.castVote(proposalId, 2); // Vote "abstain"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Get vote results
|
||||
(uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId);
|
||||
assertEq(votesFor, 10000000e18);
|
||||
assertEq(votesAgainst, 2000000e18);
|
||||
assertEq(votesAbstain, 3000000e18);
|
||||
|
||||
IGovernor.ProposalState state = governor.state(proposalId);
|
||||
assertEq(uint256(state), uint256(IGovernor.ProposalState.Succeeded));
|
||||
|
||||
// Queue proposal
|
||||
governor.queue(targets, values, calldatas, keccak256(bytes("Proposal description")));
|
||||
vm.warp(governor.proposalEta(proposalId) + 1);
|
||||
|
||||
governor.execute(targets, values, calldatas, keccak256("Proposal description"));
|
||||
state = governor.state(proposalId);
|
||||
assertEq(uint256(state), uint256(IGovernor.ProposalState.Executed));
|
||||
}
|
||||
|
||||
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")));
|
||||
}
|
||||
}
|
97
contracts/governance/test/unit/ZeroExTreasuryGovernor.t.sol
Normal file
97
contracts/governance/test/unit/ZeroExTreasuryGovernor.t.sol
Normal file
@@ -0,0 +1,97 @@
|
||||
// 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.stopPrank();
|
||||
vm.prank(account3);
|
||||
governor.castVote(proposalId, 0); // Vote "against"
|
||||
vm.stopPrank();
|
||||
vm.prank(account4);
|
||||
governor.castVote(proposalId, 2); // Vote "abstain"
|
||||
vm.stopPrank();
|
||||
|
||||
// Fast forward to vote end
|
||||
vm.roll(governor.proposalDeadline(proposalId) + 1);
|
||||
|
||||
// Get vote results
|
||||
(uint256 votesAgainst, uint256 votesFor, uint256 votesAbstain) = governor.proposalVotes(proposalId);
|
||||
assertEq(votesFor, (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));
|
||||
}
|
||||
}
|
435
contracts/governance/test/unit/ZeroExVotesTest.t.sol
Normal file
435
contracts/governance/test/unit/ZeroExVotesTest.t.sol
Normal 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);
|
||||
}
|
||||
}
|
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.51 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.50 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.49 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.49",
|
||||
"version": "5.4.51",
|
||||
"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.4.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",
|
||||
|
@@ -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);
|
||||
|
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
|
@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.44 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.43 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.42 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.42",
|
||||
"version": "1.4.44",
|
||||
"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.4.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.51",
|
||||
"@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.20.0",
|
||||
"@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"
|
||||
},
|
||||
|
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.8.40",
|
||||
"version": "4.8.43",
|
||||
"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.51",
|
||||
"@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",
|
||||
|
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"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": [
|
||||
|
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## 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
|
||||
|
@@ -55,4 +55,5 @@ library BridgeProtocols {
|
||||
uint128 internal constant WOOFI = 31;
|
||||
uint128 internal constant AAVEV3 = 32;
|
||||
uint128 internal constant KYBERELASTIC = 33;
|
||||
uint128 internal constant BARTER = 34;
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ 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";
|
||||
@@ -48,6 +49,7 @@ contract EthereumBridgeAdapter is
|
||||
MixinBalancerV2Batch,
|
||||
MixinBancor,
|
||||
MixinBancorV3,
|
||||
MixinBarter,
|
||||
MixinCompound,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
@@ -197,6 +199,11 @@ 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.UNKNOWN) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@@ -0,0 +1,41 @@
|
||||
// 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-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
|
||||
contract MixinBarter {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
function _tradeBarter(
|
||||
IERC20Token sellToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
) internal returns (uint256 boughtAmount) {
|
||||
(address barterRouter, bytes memory data) = abi.decode(bridgeData, (address, bytes));
|
||||
sellToken.approveIfBelow(barterRouter, sellAmount);
|
||||
|
||||
(bool success, bytes memory resultData) = barterRouter.call(data);
|
||||
if (!success) {
|
||||
resultData.rrevert();
|
||||
}
|
||||
|
||||
return abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.39.1",
|
||||
"version": "0.41.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -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.4.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.51",
|
||||
"@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.20.0",
|
||||
"@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"
|
||||
|
@@ -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';
|
||||
|
@@ -26,6 +26,7 @@ export {
|
||||
RevertErrorAbi,
|
||||
StandardContractOutput,
|
||||
StateMutability,
|
||||
SupportedProvider,
|
||||
TupleDataItem,
|
||||
} from 'ethereum-types';
|
||||
export { artifacts } from './artifacts';
|
||||
@@ -58,4 +59,3 @@ export {
|
||||
ZeroExContract,
|
||||
} from './wrappers';
|
||||
export { EIP712TypedData } from '@0x/types';
|
||||
export { SupportedProvider } from '@0x/subproviders';
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SimpleContractArtifact } from '@0x/types';
|
||||
import { NULL_ADDRESS } from '@0x/utils';
|
||||
import { TxData } from 'ethereum-types';
|
||||
import { TxData, SupportedProvider } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
|
@@ -33,7 +33,7 @@ contract SwapEthForERC20Test is Test, ForkUtils, TestUtils {
|
||||
_setup();
|
||||
}
|
||||
|
||||
function test_swapEthForERC20OnUniswap() public {
|
||||
function swapEthForERC20OnUniswap() public {
|
||||
log_string("SwapEthForERC20OnUniswap");
|
||||
for (uint256 i = 0; i < chains.length; i++) {
|
||||
//skip fantom/avax failing test
|
||||
|
@@ -40,7 +40,7 @@
|
||||
"test:all": "wsrun --fast-exit --serial --exclude-missing -p $PKG -c test",
|
||||
"test:contracts": "wsrun --serial -p $(echo ${npm_package_config_contractsPackages} ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') --fast-exit --exclude-missing -c test",
|
||||
"test:contracts:all": "wsrun --serial -p ${npm_package_config_contractsPackages} --fast-exit --exclude-missing -c test",
|
||||
"test:links": "yarn check-md --ignore **/forge-std/README.md,**/lib/openzeppelin-contracts,**/node_modules",
|
||||
"test:links": "yarn check-md --ignore **/forge-std/README.md,**/lib/openzeppelin-contracts,**/node_modules,**/lib",
|
||||
"generate_doc": "node ./node_modules/@0x/monorepo-scripts/lib/doc_generate.js --config ./doc-gen-config.json",
|
||||
"upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun --exclude-missing -c s3:sync_md_docs",
|
||||
"diff_md_docs:ci": "wsrun --exclude-missing -c diff_docs",
|
||||
@@ -93,5 +93,7 @@
|
||||
"resolutions": {
|
||||
"**/bignumber.js": "^9.0.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "^4.8.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"version": "8.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support in Ethereum FillQuoteTransformer"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"version": "8.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support in Ethereum FillQuoteTransformer"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681697326
|
||||
},
|
||||
{
|
||||
"version": "8.2.0",
|
||||
"changes": [
|
||||
|
@@ -6,6 +6,12 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v8.4.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support in Ethereum FillQuoteTransformer
|
||||
## v8.3.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support in Ethereum FillQuoteTransformer
|
||||
## v8.2.0 - _March 10, 2023_
|
||||
|
||||
* Upgrade Polygon, Avalanche, Arbitrum, and Optimism FillQuoteTransformers to remove Aave V3 L2 Encoding and support KyberElastic (#678)
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
|
||||
"payTakerTransformer": "0xea500d073652336a58846ada15c25f2c6d2d241f",
|
||||
"affiliateFeeTransformer": "0x8146cbbe327364b13d0699f2ced39c637f92501a",
|
||||
"fillQuoteTransformer": "0xd7a6ced9e0eaa8e5ad6e7335d25d46b6f456d4fd",
|
||||
"fillQuoteTransformer": "0x21c3bee93fad436dedd29f971dc4fdf82f3e3a3a",
|
||||
"positiveSlippageFeeTransformer": "0x818a4a855bfeb16c305cb65e8d4fb239a308bc48"
|
||||
}
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-addresses",
|
||||
"version": "8.2.0",
|
||||
"version": "8.4.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
|
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1681697326,
|
||||
"version": "13.22.20",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "13.22.19",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "13.22.18",
|
||||
|
@@ -6,6 +6,12 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v13.22.20 - _April 17, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.19 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v13.22.18 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-wrappers",
|
||||
"version": "13.22.18",
|
||||
"version": "13.22.20",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -57,13 +57,13 @@
|
||||
"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/contract-addresses": "^8.4.0",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
|
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"version": "11.20.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681756154
|
||||
},
|
||||
{
|
||||
"version": "11.19.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Barter support"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681697326
|
||||
},
|
||||
{
|
||||
"version": "11.18.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
],
|
||||
"timestamp": 1681157139
|
||||
},
|
||||
{
|
||||
"timestamp": 1678410794,
|
||||
"version": "11.18.1",
|
||||
|
@@ -6,6 +6,15 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v11.20.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support
|
||||
## v11.19.0 - _April 17, 2023_
|
||||
|
||||
* Add Barter support
|
||||
## v11.18.2 - _April 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
## v11.18.1 - _March 10, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/protocol-utils",
|
||||
"version": "11.18.1",
|
||||
"version": "11.20.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -38,9 +38,9 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/packages/protocol-utils",
|
||||
"devDependencies": {
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@0x/dev-utils": "^5.0.2",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/types": "^3.3.7",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -61,13 +61,13 @@
|
||||
"web3-provider-engine": "14.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/contract-addresses": "^8.2.0",
|
||||
"@0x/contract-wrappers": "^13.22.18",
|
||||
"@0x/assert": "^3.0.36",
|
||||
"@0x/contract-addresses": "^8.4.0",
|
||||
"@0x/contract-wrappers": "^13.22.20",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/subproviders": "^8.0.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.1",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"chai": "^4.0.1",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { SupportedProvider } from '@0x/subproviders';
|
||||
import { SupportedProvider } from 'ethereum-types';
|
||||
import { EIP712TypedData } from '@0x/types';
|
||||
import { hexUtils, providerUtils, signTypedDataUtils } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
|
@@ -164,6 +164,7 @@ export enum BridgeProtocol {
|
||||
WOOFi,
|
||||
AaveV3,
|
||||
KyberElastic,
|
||||
Barter,
|
||||
}
|
||||
|
||||
/**
|
||||
|
394
yarn.lock
394
yarn.lock
@@ -703,10 +703,10 @@
|
||||
npmlog "^4.1.2"
|
||||
write-file-atomic "^2.3.0"
|
||||
|
||||
"@0x/abi-gen@^5.8.1":
|
||||
version "5.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@0x/abi-gen/-/abi-gen-5.8.1.tgz#1818dd32c16456a4e245feecf128679ffeea0458"
|
||||
integrity sha512-DG1PNUEhUIxw552c4xwIjG8ythnyCNhLkUgbM9p9ugkNoTehL/Vev6BDFSjwlBWcIUd69On2qSnwiY8imTlrXw==
|
||||
"@0x/abi-gen@^5.8.5":
|
||||
version "5.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@0x/abi-gen/-/abi-gen-5.8.5.tgz#9df908a57844d8c3b8bc14b7e9f0533467fd7a3b"
|
||||
integrity sha512-1DDijrWql0Y5ym6W4QSgLkhLLK7miU97QR3obHCphXleXQSDjZGTd6KyT3nHFuATVMX54rlR3axXGS8bf56aOQ==
|
||||
dependencies:
|
||||
"@0x/types" "^3.3.7"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
@@ -714,7 +714,7 @@
|
||||
"@types/node" "12.12.54"
|
||||
"@types/toposort" "^2.0.1"
|
||||
chalk "^2.3.0"
|
||||
change-case "^3.0.2"
|
||||
change-case "^4.1.2"
|
||||
cli-format "^3.0.9"
|
||||
ethereum-types "^3.7.1"
|
||||
glob "^7.1.2"
|
||||
@@ -738,6 +738,18 @@
|
||||
lodash "^4.17.21"
|
||||
valid-url "^1.0.9"
|
||||
|
||||
"@0x/assert@^3.0.36":
|
||||
version "3.0.36"
|
||||
resolved "https://registry.yarnpkg.com/@0x/assert/-/assert-3.0.36.tgz#91f82973e11826c08011a5830508246262b43284"
|
||||
integrity sha512-sUtrV5MhixXvWZpATjFqIDtgvvv64duSTuOyPdPJjB+/Lcl5jQhlSNuoN0X3XP0P79Sp+6tuez5MupgFGPA2QQ==
|
||||
dependencies:
|
||||
"@0x/json-schemas" "^6.4.6"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@0x/utils" "^7.0.0"
|
||||
"@types/node" "12.12.54"
|
||||
lodash "^4.17.21"
|
||||
valid-url "^1.0.9"
|
||||
|
||||
"@0x/base-contract@^6.4.0":
|
||||
version "6.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@0x/base-contract/-/base-contract-6.5.0.tgz#95b0c3000e571cf4c2a4ee648d029d0ed744b88f"
|
||||
@@ -836,12 +848,12 @@
|
||||
"@0x/utils" "^6.4.3"
|
||||
ethereum-types "^3.5.0"
|
||||
|
||||
"@0x/contracts-gen@^2.0.48":
|
||||
version "2.0.48"
|
||||
resolved "https://registry.yarnpkg.com/@0x/contracts-gen/-/contracts-gen-2.0.48.tgz#fa308099246e0b694f4ee5a2d26a83445d2cc945"
|
||||
integrity sha512-tTtrkD0Z28k8Y9feJ45LUXRhQKgwtQqgYMgPSbqBY+IOvXXKaNNmXHe/sy7n1RKJLo171ez6fM6NehA3ls57iw==
|
||||
"@0x/contracts-gen@^2.0.50":
|
||||
version "2.0.51"
|
||||
resolved "https://registry.yarnpkg.com/@0x/contracts-gen/-/contracts-gen-2.0.51.tgz#0a1434e3b81e0bac9387cf17e040900e9ee9e405"
|
||||
integrity sha512-yqKxzxi8wArTkgOLHAVhGioKdorqpLQlDyqbGirxOUBXjUsjNU1go5OBrPicjF/mA6Yy2cReBeT8edwERj3elg==
|
||||
dependencies:
|
||||
"@0x/sol-compiler" "^4.8.2"
|
||||
"@0x/sol-compiler" "^4.8.5"
|
||||
"@0x/sol-resolver" "^3.1.13"
|
||||
"@0x/types" "^3.3.7"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
@@ -885,6 +897,26 @@
|
||||
lodash "^4.17.21"
|
||||
web3-provider-engine "16.0.4"
|
||||
|
||||
"@0x/dev-utils@^5.0.2":
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@0x/dev-utils/-/dev-utils-5.0.3.tgz#8de9b2f8241caa649ceffc6b891122aa923150a0"
|
||||
integrity sha512-V58tT2aiiHNSQtEt2XQWZDsPMsQ4wPDnjZRUaW38W46QasIFfCbcpKmfCsAGJyFxM7t0m0JJJwmYTJwZhCGcoQ==
|
||||
dependencies:
|
||||
"@0x/subproviders" "^8.0.1"
|
||||
"@0x/types" "^3.3.7"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@0x/utils" "^7.0.0"
|
||||
"@0x/web3-wrapper" "^8.0.1"
|
||||
"@types/node" "12.12.54"
|
||||
"@types/web3-provider-engine" "^14.0.0"
|
||||
chai "^4.0.1"
|
||||
chai-as-promised "^7.1.0"
|
||||
chai-bignumber "^3.0.0"
|
||||
dirty-chai "^2.0.1"
|
||||
ethereum-types "^3.7.1"
|
||||
lodash "^4.17.21"
|
||||
web3-provider-engine "16.0.4"
|
||||
|
||||
"@0x/json-schemas@^6.1.3", "@0x/json-schemas@^6.4.4", "@0x/json-schemas@^6.4.5":
|
||||
version "6.4.5"
|
||||
resolved "https://registry.yarnpkg.com/@0x/json-schemas/-/json-schemas-6.4.5.tgz#c509411360fcf0a6aa5774ba99e337ac4427fdf0"
|
||||
@@ -895,6 +927,16 @@
|
||||
ajv "^6.12.5"
|
||||
lodash.values "^4.3.0"
|
||||
|
||||
"@0x/json-schemas@^6.4.6":
|
||||
version "6.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@0x/json-schemas/-/json-schemas-6.4.6.tgz#3904b8219ad2fefdf47ddb94fb65c1b9fb82d1d7"
|
||||
integrity sha512-TaqvhOkmLN/vkcpMUNVFZBTnWP05ZVo9iGAnP1CG/B8l4rvnUbLZvWx8KeDKs62I/5d7jdYISvXyOwP4EwrG4w==
|
||||
dependencies:
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@types/node" "12.12.54"
|
||||
ajv "^6.12.5"
|
||||
lodash.values "^4.3.0"
|
||||
|
||||
"@0x/monorepo-scripts@^3.2.4":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@0x/monorepo-scripts/-/monorepo-scripts-3.2.5.tgz#12b8c81848d02de33682a5f7aecf50a2a7a9c89f"
|
||||
@@ -967,6 +1009,35 @@
|
||||
web3-eth-abi "^1.0.0-beta.24"
|
||||
yargs "^17.5.1"
|
||||
|
||||
"@0x/sol-compiler@^4.8.5":
|
||||
version "4.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@0x/sol-compiler/-/sol-compiler-4.8.5.tgz#49579cee7de838d8ebd1bfd094a0d30a9e96506c"
|
||||
integrity sha512-hAc3ZjpD+/fgSt/UQaAim8d2fQL3kWpnP5+tSEVf3/xetDDp3BhTOMi+wKnVuYo9FzuTgHx5MFueWM+mojE41A==
|
||||
dependencies:
|
||||
"@0x/assert" "^3.0.36"
|
||||
"@0x/json-schemas" "^6.4.6"
|
||||
"@0x/sol-resolver" "^3.1.13"
|
||||
"@0x/types" "^3.3.7"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@0x/utils" "^7.0.0"
|
||||
"@0x/web3-wrapper" "^8.0.1"
|
||||
"@types/node" "12.12.54"
|
||||
"@types/yargs" "^11.0.0"
|
||||
chalk "^2.3.0"
|
||||
chokidar "^3.0.2"
|
||||
ethereum-types "^3.7.1"
|
||||
ethereumjs-util "^7.1.5"
|
||||
lodash "^4.17.21"
|
||||
mkdirp "^0.5.1"
|
||||
pluralize "^7.0.0"
|
||||
require-from-string "^2.0.1"
|
||||
semver "5.5.0"
|
||||
solc "^0.8"
|
||||
source-map-support "^0.5.0"
|
||||
strip-comments "^2.0.1"
|
||||
web3-eth-abi "^1.0.0-beta.24"
|
||||
yargs "^17.5.1"
|
||||
|
||||
"@0x/sol-profiler@^4.1.36":
|
||||
version "4.1.36"
|
||||
resolved "https://registry.yarnpkg.com/@0x/sol-profiler/-/sol-profiler-4.1.36.tgz#c478f7e4ecf73472697a8777bb0d1ebe5f602e10"
|
||||
@@ -1066,6 +1137,30 @@
|
||||
optionalDependencies:
|
||||
"@ledgerhq/hw-transport-node-hid" "^5.51.1"
|
||||
|
||||
"@0x/subproviders@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-8.0.1.tgz#b934547bfa1c8049030b243a134de9755792d53a"
|
||||
integrity sha512-Lax7Msb1Ef9D6Dd7PQ19oPgjl5GIrKje7XsrO7YCfx5A0RM3Hr4nSQIxgg78jwvuulSFxQ5Sr8WiZ2hTHATtQg==
|
||||
dependencies:
|
||||
"@0x/assert" "^3.0.36"
|
||||
"@0x/types" "^3.3.7"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@0x/utils" "^7.0.0"
|
||||
"@0x/web3-wrapper" "^8.0.1"
|
||||
"@ethereumjs/common" "^2.6.3"
|
||||
"@ethereumjs/tx" "^3.5.1"
|
||||
"@types/hdkey" "^0.7.0"
|
||||
"@types/node" "12.12.54"
|
||||
"@types/web3-provider-engine" "^14.0.0"
|
||||
bip39 "2.5.0"
|
||||
ethereum-types "^3.7.1"
|
||||
ethereumjs-util "^7.1.5"
|
||||
ganache "^7.4.0"
|
||||
hdkey "2.1.0"
|
||||
json-rpc-error "2.0.0"
|
||||
lodash "^4.17.21"
|
||||
web3-provider-engine "16.0.4"
|
||||
|
||||
"@0x/ts-doc-gen@^0.0.28":
|
||||
version "0.0.28"
|
||||
resolved "https://registry.yarnpkg.com/@0x/ts-doc-gen/-/ts-doc-gen-0.0.28.tgz#075f53268e99ece6448515a3347b1d51fe92365f"
|
||||
@@ -1190,6 +1285,21 @@
|
||||
ethers "~4.0.4"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@0x/web3-wrapper@^8.0.1":
|
||||
version "8.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@0x/web3-wrapper/-/web3-wrapper-8.0.1.tgz#3625e63737d005fe6a92b71d0e676b4d03e88c60"
|
||||
integrity sha512-2rqugeCld5r/3yg+Un9sPCUNVeZW5J64Fm6i/W6qRE87X+spIJG48oJymTjSMDXw/w3FaP4nAvhSj2C5fvhN6w==
|
||||
dependencies:
|
||||
"@0x/assert" "^3.0.36"
|
||||
"@0x/json-schemas" "^6.4.6"
|
||||
"@0x/typescript-typings" "^5.3.2"
|
||||
"@0x/utils" "^7.0.0"
|
||||
"@types/node" "12.12.54"
|
||||
ethereum-types "^3.7.1"
|
||||
ethereumjs-util "^7.1.5"
|
||||
ethers "~4.0.4"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@0xproject/npm-cli-login@^0.0.11":
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e"
|
||||
@@ -2775,6 +2885,11 @@
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^12.11.0"
|
||||
|
||||
"@openzeppelin/contracts@^4.8.1":
|
||||
version "4.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.1.tgz#709cfc4bbb3ca9f4460d60101f15dac6b7a2d5e4"
|
||||
integrity sha512-xQ6eUZl+RDyb/FiZe1h+U7qr/f4p/SrTSQcTPH2bjur3C5DbuW/zFgCU/b1P/xcIaEqJep+9ju4xDRi3rmChdQ==
|
||||
|
||||
"@sindresorhus/slugify@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/slugify/-/slugify-0.8.0.tgz#5550b7fa064f3a8a82651463ad635378054c72d0"
|
||||
@@ -4244,6 +4359,17 @@ bindings@^1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bip39@2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235"
|
||||
integrity sha512-xwIx/8JKoT2+IPJpFEfXoWdYwP7UVAoUxxLNfGCfVowaJE7yg1Y5B1BVPqlUNsBq5/nGwmFkwRJ8xDW4sX8OdA==
|
||||
dependencies:
|
||||
create-hash "^1.1.0"
|
||||
pbkdf2 "^3.0.9"
|
||||
randombytes "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
unorm "^1.3.3"
|
||||
|
||||
bip39@^2.5.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.6.0.tgz#9e3a720b42ec8b3fbe4038f1e445317b6a99321c"
|
||||
@@ -4559,13 +4685,13 @@ callsites@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
|
||||
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
|
||||
|
||||
camel-case@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
|
||||
integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==
|
||||
camel-case@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
|
||||
integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.1"
|
||||
pascal-case "^3.1.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
camelcase-keys@^2.0.0:
|
||||
version "2.1.0"
|
||||
@@ -4613,6 +4739,15 @@ caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001370:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz#3d2159bf5a8f9ca093275b0d3ecc717b00f27b67"
|
||||
integrity sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==
|
||||
|
||||
capital-case@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
|
||||
integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==
|
||||
dependencies:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
caseless@~0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
@@ -4676,29 +4811,23 @@ chalk@^4.0.0, chalk@^4.1.0:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
change-case@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e"
|
||||
integrity sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==
|
||||
change-case@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
|
||||
integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==
|
||||
dependencies:
|
||||
camel-case "^3.0.0"
|
||||
constant-case "^2.0.0"
|
||||
dot-case "^2.1.0"
|
||||
header-case "^1.0.0"
|
||||
is-lower-case "^1.1.0"
|
||||
is-upper-case "^1.1.0"
|
||||
lower-case "^1.1.1"
|
||||
lower-case-first "^1.0.0"
|
||||
no-case "^2.3.2"
|
||||
param-case "^2.1.0"
|
||||
pascal-case "^2.0.0"
|
||||
path-case "^2.1.0"
|
||||
sentence-case "^2.1.0"
|
||||
snake-case "^2.1.0"
|
||||
swap-case "^1.1.0"
|
||||
title-case "^2.1.0"
|
||||
upper-case "^1.1.1"
|
||||
upper-case-first "^1.1.0"
|
||||
camel-case "^4.1.2"
|
||||
capital-case "^1.0.4"
|
||||
constant-case "^3.0.4"
|
||||
dot-case "^3.0.4"
|
||||
header-case "^2.0.4"
|
||||
no-case "^3.0.4"
|
||||
param-case "^3.0.4"
|
||||
pascal-case "^3.1.2"
|
||||
path-case "^3.0.4"
|
||||
sentence-case "^3.0.4"
|
||||
snake-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
chardet@^0.7.0:
|
||||
version "0.7.0"
|
||||
@@ -5053,13 +5182,14 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
|
||||
|
||||
constant-case@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46"
|
||||
integrity sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==
|
||||
constant-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
|
||||
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
|
||||
dependencies:
|
||||
snake-case "^2.1.0"
|
||||
upper-case "^1.1.1"
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case "^2.0.2"
|
||||
|
||||
conventional-changelog-angular@^5.0.3:
|
||||
version "5.0.13"
|
||||
@@ -5589,12 +5719,13 @@ dom-walk@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
|
||||
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
|
||||
|
||||
dot-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee"
|
||||
integrity sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==
|
||||
dot-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
|
||||
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
dot-prop@^4.2.0:
|
||||
version "4.2.1"
|
||||
@@ -7488,6 +7619,16 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hdkey@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-2.1.0.tgz#755b30b73f54e93c31919c1b2f19205a8e57cb92"
|
||||
integrity sha512-i9Wzi0Dy49bNS4tXXeGeu0vIcn86xXdPQUpEYg+SO1YiO8HtomjmmRMaRyqL0r59QfcD4PfVbSF3qmsWFwAemA==
|
||||
dependencies:
|
||||
bs58check "^2.1.2"
|
||||
ripemd160 "^2.0.2"
|
||||
safe-buffer "^5.1.1"
|
||||
secp256k1 "^4.0.0"
|
||||
|
||||
hdkey@^0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/hdkey/-/hdkey-0.7.1.tgz#caee4be81aa77921e909b8d228dd0f29acaee632"
|
||||
@@ -7501,13 +7642,13 @@ he@1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
header-case@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d"
|
||||
integrity sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==
|
||||
header-case@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
|
||||
integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.1.3"
|
||||
capital-case "^1.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
highlight.js@^9.17.1:
|
||||
version "9.18.5"
|
||||
@@ -7987,13 +8128,6 @@ is-hex-prefixed@1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554"
|
||||
integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==
|
||||
|
||||
is-lower-case@^1.1.0:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393"
|
||||
integrity sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==
|
||||
dependencies:
|
||||
lower-case "^1.1.0"
|
||||
|
||||
is-negative-zero@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||
@@ -8122,13 +8256,6 @@ is-typedarray@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
|
||||
|
||||
is-upper-case@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f"
|
||||
integrity sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==
|
||||
dependencies:
|
||||
upper-case "^1.1.0"
|
||||
|
||||
is-url@^1.1.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
|
||||
@@ -8973,17 +9100,12 @@ loupe@^2.3.1:
|
||||
dependencies:
|
||||
get-func-name "^2.0.0"
|
||||
|
||||
lower-case-first@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1"
|
||||
integrity sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==
|
||||
lower-case@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
|
||||
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
|
||||
dependencies:
|
||||
lower-case "^1.1.2"
|
||||
|
||||
lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==
|
||||
tslib "^2.0.3"
|
||||
|
||||
lru-cache@^4.0.1:
|
||||
version "4.1.5"
|
||||
@@ -9611,12 +9733,13 @@ nise@^1.2.0:
|
||||
lolex "^5.0.1"
|
||||
path-to-regexp "^1.7.0"
|
||||
|
||||
no-case@^2.2.0, no-case@^2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
|
||||
integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
|
||||
no-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
|
||||
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
lower-case "^2.0.2"
|
||||
tslib "^2.0.3"
|
||||
|
||||
node-abi@^2.21.0:
|
||||
version "2.30.1"
|
||||
@@ -10234,12 +10357,13 @@ parallel-transform@^1.1.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.1.5"
|
||||
|
||||
param-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
|
||||
integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==
|
||||
param-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
|
||||
integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
dot-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
@@ -10303,25 +10427,26 @@ parse-url@^6.0.0:
|
||||
parse-path "^4.0.0"
|
||||
protocols "^1.4.0"
|
||||
|
||||
pascal-case@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e"
|
||||
integrity sha512-qjS4s8rBOJa2Xm0jmxXiyh1+OFf6ekCWOvUaRgAQSktzlTbMotS0nmG9gyYAybCWBcuP4fsBeRCKNwGBnMe2OQ==
|
||||
pascal-case@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
|
||||
integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
|
||||
dependencies:
|
||||
camel-case "^3.0.0"
|
||||
upper-case-first "^1.1.0"
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
pascalcase@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
|
||||
integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==
|
||||
|
||||
path-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5"
|
||||
integrity sha512-Ou0N05MioItesaLr9q8TtHVWmJ6fxWdqKB2RohFmNWVyJ+2zeKIeDNWAN6B/Pe7wpzWChhZX6nONYmOnMeJQ/Q==
|
||||
path-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
|
||||
integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
dot-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
path-dirname@^1.0.0:
|
||||
version "1.0.2"
|
||||
@@ -11263,7 +11388,7 @@ rimraf@^3.0.2:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
|
||||
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
|
||||
@@ -11383,7 +11508,7 @@ secp256k1@^3.0.1:
|
||||
nan "^2.14.0"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
secp256k1@^4.0.1:
|
||||
secp256k1@^4.0.0, secp256k1@^4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303"
|
||||
integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==
|
||||
@@ -11461,13 +11586,14 @@ semver@~5.4.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
||||
integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==
|
||||
|
||||
sentence-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4"
|
||||
integrity sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==
|
||||
sentence-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
|
||||
integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case-first "^1.1.2"
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -11681,12 +11807,13 @@ smart-buffer@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
||||
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
||||
|
||||
snake-case@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
|
||||
integrity sha512-FMR5YoPFwOLuh4rRz92dywJjyKYZNLpMn1R5ujVpIYkbA9p01fq8RMg0FkO4M+Yobt4MjHeLTJVm5xFFBHSV2Q==
|
||||
snake-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
|
||||
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
dot-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
@@ -12240,14 +12367,6 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
swap-case@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3"
|
||||
integrity sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==
|
||||
dependencies:
|
||||
lower-case "^1.1.1"
|
||||
upper-case "^1.1.1"
|
||||
|
||||
table-layout@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-1.0.2.tgz#c4038a1853b0136d63365a734b6931cf4fad4a04"
|
||||
@@ -12436,14 +12555,6 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8,
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
|
||||
|
||||
title-case@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa"
|
||||
integrity sha512-EkJoZ2O3zdCz3zJsYCsxyq2OC5hrxR9mfdd5I+w8h/tmFfeOxJ+vvkxsKxdmN0WtS9zLdHEgfgVOiMVgv+Po4Q==
|
||||
dependencies:
|
||||
no-case "^2.2.0"
|
||||
upper-case "^1.0.3"
|
||||
|
||||
tmp@0.0.33, tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
@@ -12587,6 +12698,11 @@ tslib@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
tslib@^2.0.3:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
|
||||
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
|
||||
|
||||
tslib@^2.1.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
|
||||
@@ -12833,17 +12949,19 @@ update-browserslist-db@^1.0.5:
|
||||
escalade "^3.1.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
upper-case-first@^1.1.0, upper-case-first@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
|
||||
integrity sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==
|
||||
upper-case-first@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
|
||||
integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==
|
||||
dependencies:
|
||||
upper-case "^1.1.1"
|
||||
tslib "^2.0.3"
|
||||
|
||||
upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
|
||||
integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==
|
||||
upper-case@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
|
||||
integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
|
Reference in New Issue
Block a user