Compare commits
68 Commits
feat/Multi
...
@0xproject
Author | SHA1 | Date | |
---|---|---|---|
|
599554b785 | ||
|
d57d282cab | ||
|
562bef98e2 | ||
|
900f819be8 | ||
|
e7337c2451 | ||
|
dc81193132 | ||
|
fa868b4963 | ||
|
62945ad363 | ||
|
61c38d35be | ||
|
4e0ec72896 | ||
|
c28289e9a8 | ||
|
e7e9ddf232 | ||
|
f03784ba37 | ||
|
2445e23c0b | ||
|
cd29483187 | ||
|
9bacbdab48 | ||
|
551d484fb7 | ||
|
ff66432b83 | ||
|
a7ae82a2d1 | ||
|
5f34b5a80e | ||
|
8fca2a8116 | ||
|
5472500119 | ||
|
8fb5c29b4b | ||
|
40a061a5ca | ||
|
2ef82592a3 | ||
|
14b5448d70 | ||
|
e7d45e47bf | ||
|
28268d4355 | ||
|
50fa02c1d1 | ||
|
b36ff9103d | ||
|
f032c2466c | ||
|
b63ddc9be4 | ||
|
171430b617 | ||
|
d561043774 | ||
|
b1871e9ddd | ||
|
b4a5e7258c | ||
|
3c75debdf9 | ||
|
84d1053f73 | ||
|
f9df42f5d9 | ||
|
4414ef0a0f | ||
|
b7729ada38 | ||
|
3da67feeb2 | ||
|
8a0d563a32 | ||
|
612fc4a949 | ||
|
bf915ce403 | ||
|
0f9ea9773e | ||
|
447a3a6c26 | ||
|
f1cc16c44d | ||
|
aae16b6343 | ||
|
834e1538d1 | ||
|
afc489bc2c | ||
|
3e061e7364 | ||
|
6395c2a8b2 | ||
|
c8225288cd | ||
|
4a108aa67d | ||
|
fd9b3e0dcf | ||
|
f94b647e61 | ||
|
2eccc3efaf | ||
|
972341725e | ||
|
40b10fd29d | ||
|
b31fcffc76 | ||
|
ec222ea0cd | ||
|
5533220da0 | ||
|
6ee7024457 | ||
|
f839ac9c58 | ||
|
1a8b1460a6 | ||
|
feac0779a4 | ||
|
d9eeb0421c |
219
.circleci/config.yml
Normal file
219
.circleci/config.yml
Normal file
@@ -0,0 +1,219 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
environment:
|
||||
CONTRACTS_COMMIT_HASH: '9ed05f5'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
- restore_cache:
|
||||
key: dependency-cache-v1-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile
|
||||
- save_cache:
|
||||
key: dependency-cache-v1-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- run: wget https://s3.amazonaws.com/testrpc-shapshots/${CONTRACTS_COMMIT_HASH}.zip
|
||||
- run: unzip ${CONTRACTS_COMMIT_HASH}.zip -d testrpc_snapshot
|
||||
- run: node ./node_modules/lerna/bin/lerna.js bootstrap
|
||||
- run: yarn build
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo
|
||||
test-installation:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn test:installation
|
||||
test-0xjs:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci 0x.js
|
||||
- save_cache:
|
||||
key: coverage-0xjs-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/0x.js/coverage/lcov.info
|
||||
test-contracts:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci contracts
|
||||
- save_cache:
|
||||
key: coverage-contracts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/contracts/coverage/lcov.info
|
||||
test-sol-compiler:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci @0xproject/sol-compiler
|
||||
- save_cache:
|
||||
key: coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-compiler/coverage/lcov.info
|
||||
test-rest:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run:
|
||||
name: testrpc
|
||||
command: npm run testrpc -- --db testrpc_snapshot
|
||||
background: true
|
||||
- run: yarn wsrun test:circleci --exclude contracts --exclude 0x.js --exclude @0xproject/sol-compiler --stages --exclude-missing
|
||||
- save_cache:
|
||||
key: coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/assert/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/connect/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/dev-utils/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/json-schemas/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/subproviders/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/sol-cov/coverage/lcov.info
|
||||
- save_cache:
|
||||
key: coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- ~/repo/packages/metacoin/coverage/lcov.info
|
||||
lint:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn lerna:run lint
|
||||
prettier:
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn prettier:ci
|
||||
submit-coverage:
|
||||
docker:
|
||||
- image: circleci/node:9
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
- repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-contracts-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-assert-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-connect-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-dev-utils-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-json-schemas-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-subproviders-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-cov-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-sol-compiler-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-0xjs-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- restore_cache:
|
||||
keys:
|
||||
- coverage-metacoin-{{ .Environment.CIRCLE_SHA1 }}
|
||||
- run: yarn report_coverage
|
||||
workflows:
|
||||
version: 2
|
||||
main:
|
||||
jobs:
|
||||
- build
|
||||
- test-installation:
|
||||
requires:
|
||||
- build
|
||||
- test-0xjs:
|
||||
requires:
|
||||
- build
|
||||
- test-contracts:
|
||||
requires:
|
||||
- build
|
||||
- test-sol-compiler:
|
||||
requires:
|
||||
- build
|
||||
- test-rest:
|
||||
requires:
|
||||
- build
|
||||
- prettier:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
- submit-coverage:
|
||||
requires:
|
||||
- test-0xjs
|
||||
- test-sol-compiler
|
||||
- test-rest
|
||||
- test-contracts
|
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,4 +1 @@
|
||||
*.sol linguist-language=Solidity
|
||||
|
||||
# Automatically collapse generated files in GitHub.
|
||||
*.svg linguist-generated=true
|
||||
|
18
.github/CODEOWNERS
vendored
18
.github/CODEOWNERS
vendored
@@ -1,18 +0,0 @@
|
||||
# See https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# for more info about CODEOWNERS file
|
||||
|
||||
# It uses the same pattern rule for gitignore file
|
||||
|
||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||
|
||||
# Dev tools & setup
|
||||
|
||||
.github/ @dekz
|
||||
packages/contract-addresses/ @dekz @dextracker @kyu-c
|
||||
packages/contract-artifacts/ @dekz
|
||||
packages/protocol-utils/ @dekz
|
||||
|
||||
# Protocol/smart contracts
|
||||
|
||||
contracts/ @dekz @dextracker
|
91
.github/CONTRIBUTING.md
vendored
91
.github/CONTRIBUTING.md
vendored
@@ -1,91 +0,0 @@
|
||||
## 0x Contribution Guide
|
||||
|
||||
We welcome contributions from anyone on the internet and are grateful for even the smallest contributions. This document will help get you setup to start contributing back to 0x.
|
||||
|
||||
### Getting started
|
||||
|
||||
1. Fork `0xproject/0x-tools`
|
||||
2. Clone your fork
|
||||
3. Follow the [installation & build steps](https://github.com/0xProject/0x-tools#install-dependencies) in the repo's top-level README.
|
||||
4. Setup the recommended [Development Tooling](#development-tooling).
|
||||
5. Open a [draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/) against the `development` branch and describe the change you are intending to undertake in the PR description. (see [our branch naming conventions](#branch-structure))
|
||||
|
||||
Before making the PR "Ready for review", make sure:
|
||||
|
||||
- It passes our linter checks (`yarn lint`)
|
||||
- It is properly formatted with Prettier (`yarn prettier`)
|
||||
- It passes our continuous integration tests (See: [Enabling code coverage checks on your fork](#fix-submit-coverage-ci-failure) for instructions on getting the `submit-coverage` test to pass on forks)
|
||||
- You've created/updated the corresponding [CHANGELOG](#CHANGELOGs) entries.
|
||||
- Your changes have sufficient test coverage (e.g regression tests have been added for bug fixes)
|
||||
|
||||
### Branch structure
|
||||
|
||||
We have two main branches:
|
||||
|
||||
- `master` represents the most recently released (published on npm) version of the codebase.
|
||||
- `development` represents the current development state of the codebase.
|
||||
|
||||
ALL PRs should be opened against `development`.
|
||||
|
||||
Branch names should be prefixed with `fix`, `feature` or `refactor`.
|
||||
|
||||
- e.g `fix/missing-import`
|
||||
- If the PR only edits a single package, add it's name too
|
||||
- e.g `fix/subproviders/missing-import`
|
||||
|
||||
### CHANGELOGs
|
||||
|
||||
At 0x we use [Semantic Versioning](http://semver.org/) for all our published packages. If a change you make corresponds to a semver bump, you must modify the package's `CHANGELOG.json` file accordingly.
|
||||
|
||||
Each CHANGELOG entry that corresponds to a published package will have a `timestamp`. If no entry exists without a `timestamp`, you must first create a new one:
|
||||
|
||||
```
|
||||
{
|
||||
"version": "1.0.1", <- The updated package version
|
||||
"changes": [
|
||||
{
|
||||
"note": "", <- Describe your change
|
||||
"PR": 100 <- Your PR number
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
If an entry without a `timestamp` already exists, this means other changes have been introduced by other collaborators since the last publish. Add your changes to the list of notes and adjust the version if your PR introduces a greater semver change (i.e current changes required a patch bump, but your changes require a major version bump).
|
||||
|
||||
### Development Tooling
|
||||
|
||||
We strongly recommend you use the [VSCode](https://code.visualstudio.com/) text editor since most of our code is written in TypeScript and it offers amazing support for the language.
|
||||
|
||||
#### Linter
|
||||
|
||||
We use [ESLint](https://eslint.org/docs/latest/) to keep our code-style consistent.
|
||||
|
||||
Use `yarn lint` to lint the entire monorepo, and `PKG={PACKAGE_NAME} yarn lint` to lint a specific package.
|
||||
|
||||
Integrate it into your text editor:
|
||||
|
||||
- VSCode: [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
|
||||
- Atom: [ESLint](https://atom.io/packages/eslint)
|
||||
|
||||
#### Auto-formatter
|
||||
|
||||
We use [Prettier](https://prettier.io/) to auto-format our code. Be sure to either add a [text editor integration](https://prettier.io/docs/en/editors.html) or a [pre-commit hook](https://prettier.io/docs/en/precommit.html) to properly format your code changes.
|
||||
|
||||
If using the Atom text editor, we recommend you install the following packages:
|
||||
|
||||
- VSCode: [prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
||||
- Atom: [prettier-atom](https://atom.io/packages/prettier-atom)
|
||||
|
||||
## Unenforced coding conventions
|
||||
|
||||
A few of our coding conventions are not yet enforced by the linter/auto-formatter. Be careful to follow these conventions in your PR's.
|
||||
|
||||
1. Unused anonymous function parameters should be named with an underscore + number (e.g \_1, \_2, etc...)
|
||||
1. There should be a new-line between methods in a class and between test cases.
|
||||
1. If a string literal has the same value in two or more places, it should be a single constant referenced in both places.
|
||||
1. Do not import from a project's `index.ts` (e.g import { Token } from '../src';). Always import from the source file itself.
|
||||
1. Generic error variables should be named `err` instead of `e` or `error`.
|
||||
1. If you _must_ cast a variable to any - try to type it back as fast as possible. (e.g., `const cw = ((zeroEx as any)._contractWrappers as ContractWrappers);`). This ensures subsequent code is type-safe.
|
||||
1. Our enum conventions coincide with the recommended TypeScript conventions, using capitalized keys, and all-caps snake-case values. Eg `GetStats = 'GET_STATS'`
|
||||
1. All public, exported methods/functions/classes must have associated Javadoc-style comments.
|
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
25
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +0,0 @@
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Testing instructions
|
||||
|
||||
<!--- Please describe how reviewers can test your changes -->
|
||||
|
||||
## Types of changes
|
||||
|
||||
<!--- What types of changes does your code introduce? Uncomment all the bullets that apply: -->
|
||||
|
||||
<!-- * Bug fix (non-breaking change which fixes an issue) -->
|
||||
|
||||
<!-- * New feature (non-breaking change which adds functionality) -->
|
||||
|
||||
<!-- * Breaking change (fix or feature that would cause existing functionality to change) -->
|
||||
|
||||
## Checklist:
|
||||
|
||||
<!--- The following points should be used to indicate the progress of your PR. Put an `x` in all the boxes that apply right now, and come back over time and check them off as you make progress. If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
|
||||
- [ ] Add tests to cover changes as needed.
|
||||
- [ ] Update documentation as needed.
|
||||
- [ ] Add new entries to the relevant CHANGELOG.jsons.
|
2
.github/autolabeler.yml
vendored
2
.github/autolabeler.yml
vendored
@@ -1,2 +0,0 @@
|
||||
documentation: ['docs']
|
||||
liquidity integrations: ['contracts/zero-ex/contracts/src/transformers']
|
7
.github/dependabot.yml
vendored
7
.github/dependabot.yml
vendored
@@ -1,7 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
target-branch: "development"
|
137
.github/workflows/ci.yml
vendored
137
.github/workflows/ci.yml
vendored
@@ -1,137 +0,0 @@
|
||||
name: Continuous Integration
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- development
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ARBITRUM_RPC_URL: ${{ secrets.ARBITRUM_RPC_URL }}
|
||||
AVALANCHE_RPC_URL: ${{ secrets.AVALANCHE_RPC_URL }}
|
||||
BSC_RPC_URL: ${{ secrets.BSC_RPC_URL }}
|
||||
FANTOM_RPC_URL: ${{ secrets.FANTOM_RPC_URL }}
|
||||
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
|
||||
OPTIMISM_RPC_URL: ${{ secrets.OPTIMISM_RPC_URL }}
|
||||
POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build solution
|
||||
run: yarn build
|
||||
|
||||
- name: Lint typescript
|
||||
run: yarn lint:ts
|
||||
|
||||
- name: Lint solidity
|
||||
run: yarn lint:contracts
|
||||
|
||||
- name: Run prettier
|
||||
run: yarn prettier:ci
|
||||
|
||||
- name: Check dependent packages have consistent versions
|
||||
run: yarn deps_versions:ci
|
||||
|
||||
- name: Check diff in docs
|
||||
run: yarn diff_md_docs:ci
|
||||
|
||||
- name: Check for broken links in markdown files
|
||||
run: yarn test:links
|
||||
|
||||
- name: Test doc generation
|
||||
run: yarn test:generate_docs:ci
|
||||
|
||||
- name: Test @0x/contracts-*
|
||||
run: |
|
||||
yarn wsrun \
|
||||
-p @0x/contracts-multisig \
|
||||
-p @0x/contracts-utils \
|
||||
-p @0x/contracts-exchange-libs \
|
||||
-p @0x/contracts-erc721 \
|
||||
-p @0x/contracts-erc1155 \
|
||||
-p @0x/contracts-asset-proxy \
|
||||
-p @0x/contracts-broker \
|
||||
-p @0x/contracts-zero-ex \
|
||||
-m --serial -c test:ci
|
||||
|
||||
- name: Test local @0x/contracts-*
|
||||
run: |
|
||||
yarn wsrun \
|
||||
-p @0x/contracts-test-utils \
|
||||
-p @0x/contract-addresses \
|
||||
-p @0x/contract-artifacts \
|
||||
-p @0x/contract-wrappers-test \
|
||||
-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: |
|
||||
forge --version
|
||||
forge build --sizes
|
||||
|
||||
- name: Run Forge tests for erc20
|
||||
working-directory: contracts/erc20
|
||||
run: |
|
||||
forge test -vvv --gas-report
|
||||
|
||||
- name: Run Forge coverage for erc20
|
||||
working-directory: contracts/erc20
|
||||
run: |
|
||||
forge coverage --report summary --report lcov
|
||||
|
||||
- name: Upload the coverage report to Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
base-path: ./contracts/erc20/
|
||||
path-to-lcov: ./contracts/erc20/lcov.info
|
||||
|
||||
- name: Run Forge build for zero-ex
|
||||
working-directory: contracts/zero-ex
|
||||
run: |
|
||||
forge --version
|
||||
forge build --sizes
|
||||
|
||||
- name: Run Forge tests for zero-ex
|
||||
working-directory: contracts/zero-ex
|
||||
run: |
|
||||
forge test -vvv --gas-report
|
||||
|
||||
- name: Run Forge coverage for zero-ex
|
||||
working-directory: contracts/zero-ex
|
||||
run: |
|
||||
forge coverage --report summary --report lcov
|
||||
|
||||
- name: Upload the coverage report to Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
base-path: ./contracts/zero-ex/
|
||||
path-to-lcov: ./contracts/zero-ex/lcov.info
|
||||
|
||||
- name: Check coverage threshold
|
||||
uses: VeryGoodOpenSource/very_good_coverage@v2
|
||||
with:
|
||||
path: ./contracts/zero-ex/lcov.info
|
||||
min_coverage: 6.98
|
||||
exclude: '**/tests'
|
53
.github/workflows/publish.yml
vendored
53
.github/workflows/publish.yml
vendored
@@ -1,53 +0,0 @@
|
||||
name: publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ci_status:
|
||||
description: 'required CI status'
|
||||
default: 'success'
|
||||
required: true
|
||||
prerelease:
|
||||
description: 'prerelease name'
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'check successful status'
|
||||
run: |
|
||||
REF_STATUS=$(curl -s \
|
||||
'https://api.github.com/repos/${{ github.repository }}/commits/${{ github.ref }}/status' \
|
||||
| jq .state)
|
||||
[[ "${REF_STATUS}" == '"${{ github.event.inputs.ci_status }}"' ]] || \
|
||||
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16
|
||||
- uses: actions/setup-python@v2
|
||||
- name: 'configure git'
|
||||
run: |
|
||||
git config --global user.email "github-actions@github.com"
|
||||
git config --global user.name "Github Actions"
|
||||
- name: 'install dependencies'
|
||||
run: |
|
||||
yarn -D
|
||||
- name: 'build and publish'
|
||||
run: |
|
||||
echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc
|
||||
npm run run:publish:gha
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }}
|
||||
- name: 'merge into main branch'
|
||||
if: github.event.inputs.prerelease == '' # unless it's a prerelease
|
||||
run: |
|
||||
git checkout main && \
|
||||
git merge ${{ github.ref }} && \
|
||||
git push
|
40
.github/workflows/stale.yml
vendored
40
.github/workflows/stale.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: "Close stale issues and PRs"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * 1-5" # This is in UTC.
|
||||
# Do a dry-run (debug-only: true) whenever this workflow itself is changed.
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/stale.yml
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v7
|
||||
with:
|
||||
ascending: true # Spend API operations budget on older, more-likely-to-get-closed issues first
|
||||
close-issue-message: "" # Leave no comment when closing
|
||||
close-pr-message: "" # Leave no comment when closing
|
||||
days-before-issue-stale: 30
|
||||
days-before-pr-stale: 30
|
||||
days-before-close: 14
|
||||
debug-only: ${{ github.event_name == 'pull_request' }} # Dry-run when true.
|
||||
exempt-issue-labels: bug,enhancement,feature,question
|
||||
# No actual changes get made in debug-only mode, so we can raise the operations ceiling.
|
||||
operations-per-run: ${{ github.event_name == 'pull_request' && 100 || 30}}
|
||||
stale-issue-label: stale
|
||||
stale-issue-message: "This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions."
|
||||
stale-pr-label: stale
|
||||
stale-pr-message: "This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions."
|
||||
# Time immemorial when in debug-only mode (ie. on pull requests).
|
||||
# `STALEBOT_START_DATE` otherwise.
|
||||
# You can use this as a killswitch by setting `STALEBOT_START_DATE` in the far future.
|
||||
start-date: ${{ github.event_name == 'pull_request' && '1970-01-01T00:00:00Z' || secrets.STALEBOT_START_DATE }} # ISO 8601 or RFC 2822
|
57
.gitignore
vendored
57
.gitignore
vendored
@@ -11,9 +11,8 @@ pids
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# SQLite database files
|
||||
*.db
|
||||
*.sqlite
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
@@ -37,15 +36,11 @@ build/Release
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# NVM config
|
||||
.nvmrc
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.npmrc
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
@@ -72,35 +67,31 @@ generated_docs/
|
||||
|
||||
TODO.md
|
||||
|
||||
# IDE file
|
||||
.vscode
|
||||
.idea
|
||||
packages/website/public/bundle*
|
||||
packages/react-docs/example/public/bundle*
|
||||
|
||||
# server cli
|
||||
packages/testnet-faucets/server/
|
||||
|
||||
# generated contract artifacts/
|
||||
generated-artifacts/
|
||||
packages/sol-cov/test/fixtures/artifacts/
|
||||
packages/metacoin/artifacts/
|
||||
packages/order-watcher/test/artifacts/
|
||||
packages/contract-wrappers/test/artifacts/
|
||||
|
||||
# generated contract wrappers
|
||||
generated-wrappers/
|
||||
packages/0x.js/src/contract_wrappers/generated/
|
||||
packages/contracts/src/contract_wrappers/generated/
|
||||
packages/contract-wrappers/src/contract_wrappers/generated/
|
||||
packages/metacoin/src/contract_wrappers
|
||||
packages/fill-scenarios/src/generated_contract_wrappers/
|
||||
packages/order-watcher/src/generated_contract_wrappers/
|
||||
packages/migrations/src/contract_wrappers
|
||||
|
||||
# forge std-lib
|
||||
contracts/zero-ex/contracts/deps/forge-std
|
||||
# solc-bin in sol-compiler
|
||||
packages/sol-compiler/solc_bin/
|
||||
|
||||
# foundry artifacts
|
||||
foundry-artifacts/
|
||||
# Monorepo scripts
|
||||
packages/*/scripts/
|
||||
|
||||
# foundry cache
|
||||
cache/
|
||||
|
||||
#foundry output artifacts
|
||||
out/
|
||||
|
||||
# typechain wrappers
|
||||
contracts/zero-ex/typechain-wrappers/
|
||||
|
||||
# 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*
|
||||
ganache.log
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,6 +0,0 @@
|
||||
[submodule "contracts/zero-ex/contracts/deps/forge-std"]
|
||||
path = contracts/zero-ex/contracts/deps/forge-std
|
||||
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
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged --no-stash
|
@@ -1,10 +1,10 @@
|
||||
lib
|
||||
deps
|
||||
.nyc_output
|
||||
generated-artifacts/
|
||||
generated-wrappers/
|
||||
foundry-artifacts/
|
||||
out/
|
||||
cache/
|
||||
/packages/0x.js/test/artifacts
|
||||
/packages/contracts/src/artifacts
|
||||
/packages/metacoin/artifacts
|
||||
/packages/contract-wrappers/test/artifacts
|
||||
/packages/order-watcher/test/artifacts
|
||||
/packages/migrations/artifacts/1.0.0
|
||||
package.json
|
||||
packages
|
||||
scripts/postpublish_utils.js
|
||||
|
20
.prettierrc
20
.prettierrc
@@ -1,20 +1,6 @@
|
||||
{
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "**/*.sol",
|
||||
"options": {
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"singleQuote": false,
|
||||
"bracketSpacing": false
|
||||
}
|
||||
}
|
||||
]
|
||||
"printWidth": 120,
|
||||
"trailingComma": all,
|
||||
"singleQuote": true
|
||||
}
|
||||
|
@@ -1,23 +0,0 @@
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Build documentation with MkDocs
|
||||
#mkdocs:
|
||||
# configuration: mkdocs.yml
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF
|
||||
#formats:
|
||||
# - pdf
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"extends": "solhint:recommended",
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"avoid-low-level-calls": "off",
|
||||
"avoid-tx-origin": "warn",
|
||||
"code-complexity": "off",
|
||||
"const-name-snakecase": "error",
|
||||
"function-max-lines": ["error", 350],
|
||||
"max-line-length": ["error", 120],
|
||||
"no-inline-assembly": "off",
|
||||
"quotes": ["error", "double"],
|
||||
"no-empty-blocks": "off",
|
||||
"compiler-version": "off"
|
||||
}
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
contracts/erc20/src/ZRXToken.sol
|
||||
node_modules/
|
||||
lib
|
||||
deps
|
||||
generated-artifacts/
|
||||
generated-wrappers/
|
||||
foundry-artifacts/
|
||||
out/
|
||||
cache/
|
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
49
CONTRIBUTING.md
Normal file
49
CONTRIBUTING.md
Normal file
@@ -0,0 +1,49 @@
|
||||
## 0x Contribution Guide
|
||||
|
||||
Thank you for your interest in contributing to 0x protocol! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes!
|
||||
|
||||
### How to contribute
|
||||
|
||||
If you'd like to contribute to 0x protocol, please fork the repo, fix, commit and send a pull request against the `development` branch for the maintainers to review and merge into the main code base. If you wish to submit more complex changes though, please check with a core dev first on [our RocketChat #dev channel](http://chat.0xproject.com) to ensure those changes are in-line with the general philosophy of the project and/or to get some early feedback which can make both your efforts easier as well as our review and merge procedures quick and simple.
|
||||
|
||||
We encourage a “PR early” approach so create the PR as early as possible even without the fix/feature ready, so that devs and other contributors know you have picked up the issue. These early PRs should indicate an 'in progress' status by adding the '[WIP]' prefix to the PR title. Please make sure your contributions adhere to our coding guidelines:
|
||||
|
||||
* Pull requests adding features or refactoring should be opened against the `development` branch
|
||||
* Pull requests fixing bugs in the latest release version should be opened again the `master` branch
|
||||
* Write [good commit messages](https://chris.beams.io/posts/git-commit/)
|
||||
|
||||
### Code quality
|
||||
|
||||
Because 0x.js is used by multiple relayers in production and their businesses depend on it, we strive for exceptional code quality. Please follow the existing code standards and conventions. `tslint` and `prettier` (described below) will help you.
|
||||
|
||||
If you're adding functionality, please also add tests and make sure they pass. We have an automatic coverage reporting tool, so we'll see it if they are missing ;)
|
||||
If you're adding a new public function/member, make sure you document it with Java doc-style comments. We use typedoc to generate [awesome documentation](https://0xproject.com/docs/0xjs) from the comments within our source code.
|
||||
|
||||
If the sub-package you are modifying has a `CHANGELOG.md` file, make sure to add an entry in it for the change made to the package. For published packages, only changes that modify the public interface or behavior of the package need a CHANGELOG entry.
|
||||
|
||||
### Styleguide
|
||||
|
||||
We use [TSLint](https://palantir.github.io/tslint/) with [custom configs](https://github.com/0xProject/0x-monorepo/tree/development/packages/tslint-config) to keep our code style consistent.
|
||||
|
||||
To lint your code just run: `yarn lint`
|
||||
|
||||
We also use [Prettier](https://prettier.io/) to auto-format our code. Be sure to either add a [text editor integration](https://prettier.io/docs/en/editors.html) or a [pre-commit hook](https://prettier.io/docs/en/precommit.html) to properly format your code changes.
|
||||
|
||||
If using the Atom text editor, we recommend you install the following packages:
|
||||
|
||||
* [atom-typescript](https://atom.io/packages/atom-typescript)
|
||||
* [linter-tslint](https://atom.io/packages/linter-tslint)
|
||||
* [prettier-atom](https://atom.io/packages/prettier-atom)
|
||||
* [language-ethereum](https://atom.io/packages/language-ethereum)
|
||||
|
||||
Our CI will also run TSLint and Prettier as a part of the test run when you submit your PR. Make sure that the CI tests pass for your contribution.
|
||||
|
||||
### Branch structure & versioning
|
||||
|
||||
We use [semantic versioning](http://semver.org/), but before a package reaches v1.0.0 all breaking changes as well as new features will be minor version bumps.
|
||||
|
||||
We have two main branches: `master` and `development`.
|
||||
|
||||
`master` represents the most recent released (published on npm) version.
|
||||
|
||||
`development` represents the development state and is a default branch to which you will submit a PR. We use this structure so that we can push hotfixes to the currently released version without needing to publish all the changes made towards the next release. If a hotfix is implemented on `master`, it is back-ported to `development`.
|
@@ -2,13 +2,15 @@
|
||||
|
||||
<!--- Before submitting please check to see if this issue was already reported -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!--- If you're describing a bug, tell us what should happen -->
|
||||
|
||||
<!--- If you're suggesting a package change/improvement, tell us how it should work -->
|
||||
|
||||
<!--- If you're suggesting a contract or protocol change/improvement, visit our ZEIPs repo https://github.com/0xProject/ZEIPs -->
|
||||
<!--- If you're suggesting a contract or protocol change/improvement, visit our ZEIPs repo -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
@@ -44,13 +46,10 @@
|
||||
|
||||
<!--- Include as many relevant details about the environment you experienced the bug in -->
|
||||
|
||||
| Package | Version |
|
||||
| ------: | :------ |
|
||||
|
||||
<!-- For example:
|
||||
| `protocol-utils` | 2.0.4 |
|
||||
| `Exchange Contract` | v3 |
|
||||
-->
|
||||
| Package | Version |
|
||||
| ------------------: | :------ |
|
||||
| `0x.js` | 0.25.0 |
|
||||
| `Exchange Contract` | v1 |
|
||||
|
||||
| Network |
|
||||
| ------- |
|
||||
@@ -58,6 +57,6 @@
|
||||
|
||||
<!-- For example:
|
||||
| mainnet |
|
||||
| goerli |
|
||||
| development |
|
||||
| kovan |
|
||||
| testrpc |
|
||||
-->
|
4
LICENSE
4
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2020 ZeroEx Labs
|
||||
Copyright 2017 ZeroEx Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -10,4 +10,4 @@ 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.
|
||||
limitations under the License.
|
41
PULL_REQUEST_TEMPLATE.md
Normal file
41
PULL_REQUEST_TEMPLATE.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!--- Thank you for taking the time to submit a Pull Request -->
|
||||
|
||||
<!--- Provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Motivation and Context
|
||||
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
## How Has This Been Tested?
|
||||
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
|
||||
## Types of changes
|
||||
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
|
||||
* [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
* [ ] New feature (non-breaking change which adds functionality)
|
||||
* [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
|
||||
## Checklist:
|
||||
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
|
||||
* [ ] Change requires a change to the documentation.
|
||||
* [ ] Added tests to cover my changes.
|
||||
* [ ] Added new entries to the relevant CHANGELOG.jsons.
|
||||
* [ ] Labeled this PR with the 'WIP' label if it is a work in progress.
|
||||
* [ ] Labeled this PR with the labels corresponding to the changed package.
|
112
README.md
112
README.md
@@ -1,66 +1,88 @@
|
||||
<img src="https://github.com/0xProject/branding/blob/master/0x%20Logo/PNG/0x-Logo-Black.png" width="150px" >
|
||||
<img src="https://github.com/0xProject/branding/blob/master/0x_Black_CMYK.png" width="200px" >
|
||||
|
||||
---
|
||||
|
||||
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. For more information on how it works, check out the [0x protocol specification](https://protocol.0x.org/).
|
||||
[0x][website-url] is an open protocol that facilitates trustless, low friction exchange of Ethereum-based assets. A full description of the protocol may be found in our [whitepaper][whitepaper-url].
|
||||
|
||||
This repository is a monorepo including the 0x protocol smart contracts and numerous developer tools. Each public sub-package is independently published to NPM.
|
||||
|
||||
[website-url]: https://0x.org
|
||||
[website-url]: https://0xproject.com/
|
||||
[whitepaper-url]: https://0xproject.com/pdfs/0x_white_paper.pdf
|
||||
|
||||
[](https://coveralls.io/github/0xProject/protocol?branch=development)
|
||||
[](https://discordapp.com/invite/d3FTX3M)
|
||||
[](https://circleci.com/gh/0xProject/0x-monorepo)
|
||||
[](https://coveralls.io/github/0xProject/0x-monorepo?branch=development)
|
||||
[](https://chat.0xproject.com)
|
||||
[](https://gitter.im/0xProject/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://opensource.org/licenses/Apache-2.0)
|
||||
|
||||
## Packages
|
||||
### Published Packages
|
||||
|
||||
Visit our [developer portal](https://0x.org/docs/) for a comprehensive list of core & community maintained packages. All packages maintained with this monorepo are listed below.
|
||||
| Package | Version | Description |
|
||||
| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [`0x.js`](/packages/0x.js) | [](https://www.npmjs.com/package/0x.js) | A Javascript library for interacting with the 0x protocol |
|
||||
| [`@0xproject/abi-gen`](/packages/abi-gen) | [](https://www.npmjs.com/package/@0xproject/abi-gen) | Tool to generate TS wrappers from smart contract ABIs |
|
||||
| [`@0xproject/assert`](/packages/assert) | [](https://www.npmjs.com/package/@0xproject/assert) | Type and schema assertions used by our packages |
|
||||
| [`@0xproject/base-contract`](/packages/base-contract) | [](https://www.npmjs.com/package/@0xproject/base-contract) | BaseContract used by auto-generated `abi-gen` wrapper contracts |
|
||||
| [`@0xproject/connect`](/packages/connect) | [](https://www.npmjs.com/package/@0xproject/connect) | A Javascript library for interacting with the Standard Relayer API |
|
||||
| [`@0xproject/sol-compiler`](/packages/sol-compiler) | [](https://www.npmjs.com/package/@0xproject/sol-compiler) | A thin wrapper around Solc.js that outputs artifacts, resolves imports, only re-compiles when needed, and other niceties. |
|
||||
| [`@0xproject/dev-utils`](/packages/dev-utils) | [](https://www.npmjs.com/package/@0xproject/dev-utils) | Dev utils to be shared across 0x projects and packages |
|
||||
| [`@0xproject/json-schemas`](/packages/json-schemas) | [](https://www.npmjs.com/package/@0xproject/json-schemas) | 0x-related json schemas |
|
||||
| [`@0xproject/monorepo-scripts`](/packages/monorepo-scripts) | [](https://www.npmjs.com/package/@0xproject/monorepo-scripts) | Monorepo scripts |
|
||||
| [`@0xproject/react-docs`](/packages/react-docs) | [](https://www.npmjs.com/package/@0xproject/react-docs) | React documentation component for rendering TypeDoc & Doxity generated JSON |
|
||||
| [`@0xproject/react-shared`](/packages/react-shared) | [](https://www.npmjs.com/package/@0xproject/react-shared) | 0x shared react components |
|
||||
| [`@0xproject/sra-report`](/packages/sra-report) | [](https://www.npmjs.com/package/@0xproject/sra-report) | Generate reports for standard relayer API compliance |
|
||||
| [`@0xproject/sol-cov`](/packages/sol-cov) | [](https://www.npmjs.com/package/@0xproject/sol-cov) | Solidity test coverage tool |
|
||||
| [`@0xproject/subproviders`](/packages/subproviders) | [](https://www.npmjs.com/package/@0xproject/subproviders) | Useful web3 subproviders (e.g LedgerSubprovider) |
|
||||
| [`@0xproject/tslint-config`](/packages/tslint-config) | [](https://www.npmjs.com/package/@0xproject/tslint-config) | Custom 0x development TSLint rules |
|
||||
| [`@0xproject/types`](/packages/types) | [](https://www.npmjs.com/package/@0xproject/types) | Shared type declarations |
|
||||
| [`@0xproject/typescript-typings`](/packages/typescript-typings) | [](https://www.npmjs.com/package/@0xproject/typescript-typings) | Repository of types for external packages |
|
||||
| [`@0xproject/utils`](/packages/utils) | [](https://www.npmjs.com/package/@0xproject/utils) | Shared utilities |
|
||||
| [`@0xproject/web3-wrapper`](/packages/web3-wrapper) | [](https://www.npmjs.com/package/@0xproject/web3-wrapper) | Web3 wrapper |
|
||||
|
||||
### Solidity Packages
|
||||
### Private Packages
|
||||
|
||||
These packages are all under development. See [/contracts/README.md](/contracts/README.md) for a list of deployed packages.
|
||||
|
||||
| Package | Version | Description |
|
||||
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| [`@0x/contracts-zero-ex`](/contracts/zero-ex) | [](https://www.npmjs.com/package/@0x/contracts-zero-ex) | The contracts used for settling trades within the protocol |
|
||||
| [`@0x/contracts-erc20`](/contracts/erc20) | [](https://www.npmjs.com/package/@0x/contracts-erc20) | Implementations of various ERC20 tokens |
|
||||
| [`@0x/contracts-test-utils`](/contracts/test-utils) | [](https://www.npmjs.com/package/@0x/contracts-test-utils) | TypeScript/Javascript shared utilities used for testing contracts |
|
||||
| [`@0x/contracts-utils`](/contracts/utils) | [](https://www.npmjs.com/package/@0x/contracts-utils) | Generic libraries and utilities used throughout all of the contracts |
|
||||
|
||||
### TypeScript/Javascript Packages
|
||||
|
||||
#### 0x-specific packages
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/protocol-utils`](/packages/protocol-utils) | [](https://www.npmjs.com/package/@0x/protocol-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
| Package | Description |
|
||||
| --------------------------------------------------------------- | ---------------------------------------------------------------- |
|
||||
| [`@0xproject/contracts`](/packages/contracts) | 0x solidity smart contracts & tests |
|
||||
| [`@0xproject/react-docs-example`](/packages/react-docs-example) | Example documentation site created with `@0xproject/react-docs` |
|
||||
| [`@0xproject/testnet-faucets`](/packages/testnet-faucets) | A faucet micro-service that dispenses test ERC20 tokens or Ether |
|
||||
| [`@0xproject/website`](/packages/website) | 0x website & Portal DApp |
|
||||
|
||||
## Usage
|
||||
|
||||
Node version 6.x or 8.x is required.
|
||||
Dedicated documentation pages:
|
||||
|
||||
* [0x.js Library](https://0xproject.com/docs/0xjs)
|
||||
* [0x Connect](https://0xproject.com/docs/connect)
|
||||
* [Smart contracts](https://0xproject.com/docs/contracts)
|
||||
* [Subproviders](https://0xproject.com/docs/subproviders)
|
||||
* [Sol Compiler](https://0xproject.com/docs/sol-compiler)
|
||||
* [Web3-wrapper](https://0xproject.com/docs/web3-wrapper)
|
||||
* [JSON-schemas](https://0xproject.com/docs/json-schemas)
|
||||
* [Sol-cov](https://0xproject.com/docs/sol-cov)
|
||||
* [Standard Relayer API](https://github.com/0xProject/standard-relayer-api/blob/master/README.md)
|
||||
|
||||
Node version >= 6.12 is required.
|
||||
|
||||
Most of the packages require additional typings for external dependencies.
|
||||
You can include those by prepending the `@0x/typescript-typings` package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
||||
You can include those by prepending @0xproject/typescript-typings package to your [`typeRoots`](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html) config.
|
||||
|
||||
```json
|
||||
"typeRoots": ["node_modules/@0x/typescript-typings/types", "node_modules/@types"],
|
||||
"typeRoots": ["node_modules/@0xproject/typescript-typings/types", "node_modules/@types"],
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
#### Read our [contribution guidelines](.github/CONTRIBUTING.md).
|
||||
#### Read our [contribution guidelines](./CONTRIBUTING.md).
|
||||
|
||||
### Install dependencies
|
||||
|
||||
Make sure you are using Yarn v1.9.4. To install using brew:
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
brew install yarn@1.9.4
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
@@ -80,13 +102,7 @@ yarn build
|
||||
To build a specific package:
|
||||
|
||||
```bash
|
||||
PKG=@0x/protocol-utils yarn build
|
||||
```
|
||||
|
||||
To build all contracts packages:
|
||||
|
||||
```bash
|
||||
yarn build:contracts
|
||||
PKG=@0xproject/web3-wrapper yarn build
|
||||
```
|
||||
|
||||
### Watch
|
||||
@@ -103,7 +119,7 @@ To watch a specific package and all it's dependent packages:
|
||||
PKG=[NPM_PACKAGE_NAME] yarn watch
|
||||
|
||||
e.g
|
||||
PKG=@0x/protocol-utils yarn watch
|
||||
PKG=@0xproject/web3-wrapper yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
@@ -117,7 +133,7 @@ yarn clean
|
||||
Clean a specific package
|
||||
|
||||
```bash
|
||||
PKG=@0x/protocol-utils yarn clean
|
||||
PKG=0x.js yarn clean
|
||||
```
|
||||
|
||||
### Rebuild
|
||||
@@ -131,7 +147,7 @@ yarn rebuild
|
||||
To re-build (clean & build) a specific package & it's deps:
|
||||
|
||||
```bash
|
||||
PKG=@0x/protocol-utils yarn rebuild
|
||||
PKG=0x.js yarn rebuild
|
||||
```
|
||||
|
||||
### Lint
|
||||
@@ -145,7 +161,7 @@ yarn lint
|
||||
Lint a specific package:
|
||||
|
||||
```bash
|
||||
PKG=@0x/protocol-utils yarn lint
|
||||
PKG=0x.js yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
@@ -159,11 +175,5 @@ yarn test
|
||||
Run a specific package's test:
|
||||
|
||||
```bash
|
||||
PKG=@0x/protocol-utils yarn test
|
||||
```
|
||||
|
||||
Run all contracts packages tests:
|
||||
|
||||
```bash
|
||||
yarn test:contracts
|
||||
PKG=@0xproject/web3-wrapper yarn test
|
||||
```
|
||||
|
@@ -1,3 +0,0 @@
|
||||
#### Development
|
||||
|
||||
Building solidity files will update the contract artifact in `{package-name}/generated-artifacts/{contract}.json`, but does not automatically update the `contract-artifacts` or `contract-wrappers` packages, which are generated from the artifact JSON. See `contract-artifacts/README.md` for instructions on updating these packages.
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
File diff suppressed because it is too large
Load Diff
@@ -1,487 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.0.0 - _February 16, 2023_
|
||||
|
||||
* Migrated package to foundry
|
||||
|
||||
## v3.3.57 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.56 - _January 30, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.55 - _January 23, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.54 - _December 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.53 - _November 23, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.52 - _November 15, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.51 - _November 5, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.50 - _November 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.49 - _October 24, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.48 - _October 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.47 - _October 13, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.46 - _October 11, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.45 - _October 5, 2022_
|
||||
|
||||
* Migrate from TSLint to ESLint and fix linting errors (#589)
|
||||
|
||||
## v3.3.44 - _September 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.43 - _September 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.42 - _September 7, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.41 - _September 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.40 - _September 1, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.39 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.38 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.37 - _August 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.36 - _August 10, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.35 - _August 9, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.34 - _August 6, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.33 - _July 27, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.32 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.31 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.30 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.29 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.28 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.27 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.26 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.25 - _December 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.24 - _December 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.23 - _November 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.22 - _November 3, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.21 - _October 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.20 - _September 15, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.19 - _September 8, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.18 - _September 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.17 - _August 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.16 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.15 - _August 11, 2021_
|
||||
|
||||
* Add ethers as a dependency (#305)
|
||||
|
||||
## v3.3.14 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.13 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.12 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.11 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.10 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.9 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.8 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.7 - _April 28, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.6 - _April 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.5 - _March 17, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.4 - _February 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.3 - _February 10, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.2 - _January 26, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.1 - _January 13, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.0 - _January 4, 2021_
|
||||
|
||||
* Add SPDX license identifiers to solidity files (#105)
|
||||
* Allow for excess return data in `LibERC20TokenV06` compat* functions (#97)
|
||||
|
||||
## v3.2.14 - _December 23, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.13 - _December 17, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.12 - _December 9, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.11 - _December 7, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.10 - _December 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.9 - _November 19, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.8 - _November 13, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.7 - _November 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.6 - _November 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.5 - _November 2, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.4 - _October 28, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.3 - _October 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.2 - _October 21, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.1 - _July 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.0 - _June 24, 2020_
|
||||
|
||||
* Add `LibERC20Token.approveIfBelow()` (#2512)
|
||||
* Add solidity 0.6 contracts (#2545)
|
||||
* Update `LibERC20TokenV06` comments. (#2597)
|
||||
|
||||
## v3.1.5 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.4 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.3 - _February 26, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.2 - _February 25, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.1 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0 - _February 8, 2020_
|
||||
|
||||
* Add `allowance()` and `balanceOf()` to `LibERC20Token` (#2464)
|
||||
* Fix broken tests (#2456)
|
||||
|
||||
## v3.0.6 - _February 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.5 - _February 4, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.4 - _January 22, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.3 - _January 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.1 - _December 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.0 - _December 2, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Add `decimals()` to `LibERC20Token`. (#2344)
|
||||
* Create `LibERC20Token` (#2309)
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.3.0-beta.4 - _December 2, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.3.0-beta.3 - _November 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.3.0-beta.2 - _November 17, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Add `decimals()` to `LibERC20Token`. (#2344)
|
||||
|
||||
## v2.3.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* Create `LibERC20Token` (#2309)
|
||||
|
||||
## v2.3.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
* Replaced `SafeMath` with `LibSafeMath` (#2254)
|
||||
|
||||
## v2.2.14 - _September 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.13 - _September 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.12 - _August 22, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.11 - _August 8, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.10 - _July 31, 2019_
|
||||
|
||||
* Updated calls to <contract wrapper>.deployFrom0xArtifactAsync to include artifact dependencies. (#1995)
|
||||
|
||||
## v2.2.9 - _July 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.8 - _July 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.7 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.6 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.5 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.4 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.3 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.1 - _May 10, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.2.0 - _April 11, 2019_
|
||||
|
||||
* Added UntransferrableDummyERC20Token (#1714)
|
||||
|
||||
## v2.1.0 - _March 21, 2019_
|
||||
|
||||
* Run Web3ProviderEngine without excess block polling (#1695)
|
||||
|
||||
## v2.0.0 - _March 20, 2019_
|
||||
|
||||
* Upgrade contracts to Solidity 0.5.5 (#1682)
|
||||
|
||||
## v1.0.9 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.8 - _February 27, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.7 - _February 26, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.6 - _February 25, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.5 - _February 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.4 - _February 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.3 - _February 7, 2019_
|
||||
|
||||
* Fake publish to enable pinning
|
||||
|
||||
## v1.0.2 - _February 6, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1 - _February 5, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.0 - _Invalid date_
|
||||
|
||||
* Move all ERC20 contracts out of contracts-tokens to new package (#1539)
|
@@ -1,17 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "ZRXToken",
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "protocol v1 deploy",
|
||||
"networks": {
|
||||
"1": "0xe41d2489571d322189246dafa5ebde1f4699f498",
|
||||
"3": "0xff67881f8d12f372d91baae9752eb3631ff0ed00",
|
||||
"4": "0x2727e688b8fd40b198cd5fe6e408e00494a06f07",
|
||||
"42": "0x2002d3812f58e35f0ea1ffbf80a75a38c32175fa"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -1,69 +0,0 @@
|
||||
## ERC20 Tokens
|
||||
|
||||
This package contains implementations of various [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) tokens, including WETH (Wrapped Ether) and ZRX. Addresses of the deployed contracts can be found in this 0x [guide](https://0x.org/docs/guides/0x-cheat-sheet) or the [DEPLOYS](./DEPLOYS.json) file within this package.
|
||||
|
||||
## Installation
|
||||
|
||||
**Install**
|
||||
|
||||
```bash
|
||||
npm install @0x/contracts-erc20 --save
|
||||
```
|
||||
|
||||
## Bug bounty
|
||||
|
||||
A bug bounty for the 2.0.0 contracts is ongoing! Instructions can be found [here](https://0x.org/docs/guides/bug-bounty-program).
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
|
||||
|
||||
Please read our [contribution guidelines](../../.github/CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc20 yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-erc20 yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
@@ -1,18 +0,0 @@
|
||||
[profile.default]
|
||||
src = 'src'
|
||||
out = 'out'
|
||||
libs = [
|
||||
'lib',
|
||||
'node_modules',
|
||||
]
|
||||
remappings = [
|
||||
'@0x/contracts-utils/=../utils/'
|
||||
]
|
||||
allow_paths = [
|
||||
'../utils/'
|
||||
]
|
||||
|
||||
fs_permissions = [{ access = "read", path = "./out/ZRXToken.sol" }]
|
||||
|
||||
optimizer_runs = 1_000_000
|
||||
# See more config options https://github.com/foundry-rs/foundry/tree/master/config
|
Submodule contracts/erc20/lib/forge-std deleted from a2edd39db9
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "4.0.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Token contracts used by 0x protocol",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test:ci": "forge test",
|
||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/protocol.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/protocol/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol",
|
||||
"devDependencies": {
|
||||
"@0x/contracts-utils": "^4.8.38",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"typedoc": "~0.16.11"
|
||||
},
|
||||
"dependencies": {},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "4f91bfd907996b2f4dd383778b50c479c2602b56"
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2023 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity >=0.6.5 <0.9;
|
||||
|
||||
interface IERC20Token {
|
||||
event Transfer(address indexed from, address indexed to, uint256 value);
|
||||
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
|
||||
/// @dev send `value` token to `to` from `msg.sender`
|
||||
/// @param to The address of the recipient
|
||||
/// @param value The amount of token to be transferred
|
||||
/// @return True if transfer was successful
|
||||
function transfer(address to, uint256 value) external returns (bool);
|
||||
|
||||
/// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
|
||||
/// @param from The address of the sender
|
||||
/// @param to The address of the recipient
|
||||
/// @param value The amount of token to be transferred
|
||||
/// @return True if transfer was successful
|
||||
function transferFrom(address from, address to, uint256 value) external returns (bool);
|
||||
|
||||
/// @dev `msg.sender` approves `spender` to spend `value` tokens
|
||||
/// @param spender The address of the account able to transfer the tokens
|
||||
/// @param value The amount of wei to be approved for transfer
|
||||
/// @return Always true if the call has enough gas to complete execution
|
||||
function approve(address spender, uint256 value) external returns (bool);
|
||||
|
||||
/// @dev Query total supply of token
|
||||
/// @return Total supply of token
|
||||
function totalSupply() external view returns (uint256);
|
||||
|
||||
/// @dev Get the balance of `owner`.
|
||||
/// @param owner The address from which the balance will be retrieved
|
||||
/// @return Balance of owner
|
||||
function balanceOf(address owner) external view returns (uint256);
|
||||
|
||||
/// @dev Get the allowance for `spender` to spend from `owner`.
|
||||
/// @param owner The address of the account owning tokens
|
||||
/// @param spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
|
||||
/// @dev Get the number of decimals this token has.
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 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;
|
||||
|
||||
import "./IERC20Token.sol";
|
||||
|
||||
interface IEtherToken is IERC20Token {
|
||||
/// @dev Wrap ether.
|
||||
function deposit() external payable;
|
||||
|
||||
/// @dev Unwrap ether.
|
||||
function withdraw(uint256 amount) external;
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
|
||||
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.4.11;
|
||||
|
||||
contract Token {
|
||||
/// @return total amount of tokens
|
||||
function totalSupply() constant returns (uint256 supply) {}
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) constant returns (uint256 balance) {}
|
||||
|
||||
/// @notice send `_value` token to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transfer(address _to, uint256 _value) returns (bool success) {}
|
||||
|
||||
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
/// @param _from The address of the sender
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {}
|
||||
|
||||
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of wei to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint256 _value) returns (bool success) {}
|
||||
|
||||
/// @param _owner The address of the account owning tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address _owner, address _spender) constant returns (uint256 remaining) {}
|
||||
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
}
|
||||
|
||||
contract ERC20Token is Token {
|
||||
function transfer(address _to, uint256 _value) returns (bool) {
|
||||
//Default assumes totalSupply can't be over max (2^256 - 1).
|
||||
if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
|
||||
balances[msg.sender] -= _value;
|
||||
balances[_to] += _value;
|
||||
Transfer(msg.sender, _to, _value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
|
||||
if (
|
||||
balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]
|
||||
) {
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
Transfer(_from, _to, _value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function balanceOf(address _owner) constant returns (uint256) {
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value) returns (bool) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address _owner, address _spender) constant returns (uint256) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
|
||||
mapping(address => uint256) balances;
|
||||
mapping(address => mapping(address => uint256)) allowed;
|
||||
uint256 public totalSupply;
|
||||
}
|
||||
|
||||
contract UnlimitedAllowanceToken is ERC20Token {
|
||||
uint256 constant MAX_UINT = 2 ** 256 - 1;
|
||||
|
||||
/// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance.
|
||||
/// @param _from Address to transfer from.
|
||||
/// @param _to Address to transfer to.
|
||||
/// @param _value Amount to transfer.
|
||||
/// @return Success of transfer.
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
|
||||
uint256 allowance = allowed[_from][msg.sender];
|
||||
if (balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]) {
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
if (allowance < MAX_UINT) {
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
}
|
||||
Transfer(_from, _to, _value);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract ZRXToken is UnlimitedAllowanceToken {
|
||||
uint8 public constant decimals = 18;
|
||||
uint256 public totalSupply = 10 ** 27; // 1 billion tokens, 18 decimal places
|
||||
string public constant name = "0x Protocol Token";
|
||||
string public constant symbol = "ZRX";
|
||||
|
||||
function ZRXToken() public {
|
||||
balances[msg.sender] = totalSupply;
|
||||
}
|
||||
}
|
@@ -1,153 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 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;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "../IERC20Token.sol";
|
||||
|
||||
library LibERC20TokenV06 {
|
||||
bytes private constant DECIMALS_CALL_DATA = hex"313ce567";
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param allowance The allowance to set.
|
||||
function compatApprove(IERC20Token token, address spender, uint256 allowance) internal {
|
||||
bytes memory callData = abi.encodeWithSelector(token.approve.selector, spender, allowance);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()` and sets the allowance to the
|
||||
/// maximum if the current approval is not already >= an amount.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param amount The minimum allowance needed.
|
||||
function approveIfBelow(IERC20Token token, address spender, uint256 amount) internal {
|
||||
if (token.allowance(address(this), spender) < amount) {
|
||||
compatApprove(token, spender, uint256(-1));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transfer()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransfer(IERC20Token token, address to, uint256 amount) internal {
|
||||
bytes memory callData = abi.encodeWithSelector(token.transfer.selector, to, amount);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transferFrom()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param from The owner of the tokens.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransferFrom(IERC20Token token, address from, address to, uint256 amount) internal {
|
||||
bytes memory callData = abi.encodeWithSelector(token.transferFrom.selector, from, to, amount);
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Retrieves the number of decimals for a token.
|
||||
/// Returns `18` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @return tokenDecimals The number of decimals places for the token.
|
||||
function compatDecimals(IERC20Token token) internal view returns (uint8 tokenDecimals) {
|
||||
tokenDecimals = 18;
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
tokenDecimals = uint8(LibBytesV06.readUint256(resultData, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the allowance for a token, owner, and spender.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @param spender The address the spender.
|
||||
/// @return allowance_ The allowance for a token, owner, and spender.
|
||||
function compatAllowance(
|
||||
IERC20Token token,
|
||||
address owner,
|
||||
address spender
|
||||
) internal view returns (uint256 allowance_) {
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeWithSelector(token.allowance.selector, owner, spender)
|
||||
);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
allowance_ = LibBytesV06.readUint256(resultData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the balance for a token owner.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @return balance The token balance of an owner.
|
||||
function compatBalanceOf(IERC20Token token, address owner) internal view returns (uint256 balance) {
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeWithSelector(token.balanceOf.selector, owner)
|
||||
);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
balance = LibBytesV06.readUint256(resultData, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes a call on address `target` with calldata `callData`
|
||||
/// and asserts that either nothing was returned or a single boolean
|
||||
/// was returned equal to `true`.
|
||||
/// @param target The call target.
|
||||
/// @param callData The abi-encoded call data.
|
||||
function _callWithOptionalBooleanResult(address target, bytes memory callData) private {
|
||||
(bool didSucceed, bytes memory resultData) = target.call(callData);
|
||||
// Revert if the call reverted.
|
||||
if (!didSucceed) {
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
// If we get back 0 returndata, this may be a non-standard ERC-20 that
|
||||
// does not return a boolean. Check that it at least contains code.
|
||||
if (resultData.length == 0) {
|
||||
uint256 size;
|
||||
assembly {
|
||||
size := extcodesize(target)
|
||||
}
|
||||
require(size > 0, "invalid token address, contains no code");
|
||||
return;
|
||||
}
|
||||
// If we get back at least 32 bytes, we know the target address
|
||||
// contains code, and we assume it is a token that returned a boolean
|
||||
// success value, which must be true.
|
||||
if (resultData.length >= 32) {
|
||||
uint256 result = LibBytesV06.readUint256(resultData, 0);
|
||||
if (result == 1) {
|
||||
return;
|
||||
} else {
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
||||
// If 0 < returndatasize < 32, the target is a contract, but not a
|
||||
// valid token.
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
@@ -1,755 +0,0 @@
|
||||
// Copyright (C) 2015, 2016, 2017 Dapphub
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.6.0;
|
||||
|
||||
contract WETH9V06 {
|
||||
string public name = "Wrapped Ether";
|
||||
string public symbol = "WETH";
|
||||
uint8 public decimals = 18;
|
||||
|
||||
event Approval(address indexed _owner, address indexed _spender, uint _value);
|
||||
event Transfer(address indexed _from, address indexed _to, uint _value);
|
||||
event Deposit(address indexed _owner, uint _value);
|
||||
event Withdrawal(address indexed _owner, uint _value);
|
||||
|
||||
mapping(address => uint) public balanceOf;
|
||||
mapping(address => mapping(address => uint)) public allowance;
|
||||
|
||||
receive() external payable {
|
||||
deposit();
|
||||
}
|
||||
|
||||
function deposit() public payable {
|
||||
balanceOf[msg.sender] += msg.value;
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
function withdraw(uint wad) public {
|
||||
require(balanceOf[msg.sender] >= wad);
|
||||
balanceOf[msg.sender] -= wad;
|
||||
msg.sender.transfer(wad);
|
||||
emit Withdrawal(msg.sender, wad);
|
||||
}
|
||||
|
||||
function totalSupply() public view returns (uint) {
|
||||
return address(this).balance;
|
||||
}
|
||||
|
||||
function approve(address guy, uint wad) public returns (bool) {
|
||||
allowance[msg.sender][guy] = wad;
|
||||
emit Approval(msg.sender, guy, wad);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(address dst, uint wad) public returns (bool) {
|
||||
return transferFrom(msg.sender, dst, wad);
|
||||
}
|
||||
|
||||
function transferFrom(address src, address dst, uint wad) public returns (bool) {
|
||||
require(balanceOf[src] >= wad);
|
||||
|
||||
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
|
||||
require(allowance[src][msg.sender] >= wad);
|
||||
allowance[src][msg.sender] -= wad;
|
||||
}
|
||||
|
||||
balanceOf[src] -= wad;
|
||||
balanceOf[dst] += wad;
|
||||
|
||||
emit Transfer(src, dst, wad);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
*/
|
@@ -1,146 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 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.0;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v08/errors/LibRichErrorsV08.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v08/LibBytesV08.sol";
|
||||
import "../IERC20Token.sol";
|
||||
|
||||
library LibERC20TokenV08 {
|
||||
bytes private constant DECIMALS_CALL_DATA = hex"313ce567";
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param allowance The allowance to set.
|
||||
function compatApprove(IERC20Token token, address spender, uint256 allowance) internal {
|
||||
bytes memory callData = abi.encodeCall(token.approve, (spender, allowance));
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).approve()` and sets the allowance to the
|
||||
/// maximum if the current approval is not already >= an amount.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param amount The minimum allowance needed.
|
||||
function approveIfBelow(IERC20Token token, address spender, uint256 amount) internal {
|
||||
if (token.allowance(address(this), spender) < amount) {
|
||||
compatApprove(token, spender, type(uint256).max);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transfer()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransfer(IERC20Token token, address to, uint256 amount) internal {
|
||||
bytes memory callData = abi.encodeCall(token.transfer, (to, amount));
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20Token(token).transferFrom()`.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param from The owner of the tokens.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
function compatTransferFrom(IERC20Token token, address from, address to, uint256 amount) internal {
|
||||
bytes memory callData = abi.encodeCall(token.transferFrom, (from, to, amount));
|
||||
_callWithOptionalBooleanResult(address(token), callData);
|
||||
}
|
||||
|
||||
/// @dev Retrieves the number of decimals for a token.
|
||||
/// Returns `18` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @return tokenDecimals The number of decimals places for the token.
|
||||
function compatDecimals(IERC20Token token) internal view returns (uint8 tokenDecimals) {
|
||||
tokenDecimals = 18;
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(DECIMALS_CALL_DATA);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
tokenDecimals = abi.decode(resultData, (uint8));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the allowance for a token, owner, and spender.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @param spender The address the spender.
|
||||
/// @return allowance_ The allowance for a token, owner, and spender.
|
||||
function compatAllowance(
|
||||
IERC20Token token,
|
||||
address owner,
|
||||
address spender
|
||||
) internal view returns (uint256 allowance_) {
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeCall(token.allowance, (owner, spender))
|
||||
);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
allowance_ = abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrieves the balance for a token owner.
|
||||
/// Returns `0` if the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @return balance The token balance of an owner.
|
||||
function compatBalanceOf(IERC20Token token, address owner) internal view returns (uint256 balance) {
|
||||
(bool didSucceed, bytes memory resultData) = address(token).staticcall(
|
||||
abi.encodeCall(token.balanceOf, (owner))
|
||||
);
|
||||
if (didSucceed && resultData.length >= 32) {
|
||||
balance = abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes a call on address `target` with calldata `callData`
|
||||
/// and asserts that either nothing was returned or a single boolean
|
||||
/// was returned equal to `true`.
|
||||
/// @param target The call target.
|
||||
/// @param callData The abi-encoded call data.
|
||||
function _callWithOptionalBooleanResult(address target, bytes memory callData) private {
|
||||
(bool didSucceed, bytes memory resultData) = target.call(callData);
|
||||
// Revert if the call reverted.
|
||||
if (!didSucceed) {
|
||||
LibRichErrorsV08.rrevert(resultData);
|
||||
}
|
||||
// If we get back 0 returndata, this may be a non-standard ERC-20 that
|
||||
// does not return a boolean. Check that it at least contains code.
|
||||
if (resultData.length == 0) {
|
||||
require(target.code.length > 0, "invalid token address, contains no code");
|
||||
return;
|
||||
}
|
||||
// If we get back at least 32 bytes, we know the target address
|
||||
// contains code, and we assume it is a token that returned a boolean
|
||||
// success value, which must be true.
|
||||
if (resultData.length >= 32) {
|
||||
if (!abi.decode(resultData, (bool))) {
|
||||
LibRichErrorsV08.rrevert(resultData);
|
||||
}
|
||||
}
|
||||
// If 0 < returndatasize < 32, the target is a contract, but not a
|
||||
// valid token.
|
||||
LibRichErrorsV08.rrevert(resultData);
|
||||
}
|
||||
}
|
@@ -1,76 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 ZeroEx Intl.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../src/v06/WETH9V06.sol";
|
||||
|
||||
contract WETH9V06Test is Test {
|
||||
address payable internal owner = payable(vm.addr(1));
|
||||
address payable internal user = payable(vm.addr(2));
|
||||
WETH9V06 internal etherToken;
|
||||
|
||||
function setUp() public {
|
||||
vm.deal(owner, 1e20);
|
||||
vm.deal(user, 1e20);
|
||||
|
||||
etherToken = new WETH9V06();
|
||||
}
|
||||
|
||||
function testShouldRevertIfCallerAttemptsToDepositMoreThanTheirBalance() public {
|
||||
vm.prank(user);
|
||||
vm.expectRevert();
|
||||
etherToken.deposit{value: 1e20 + 1}();
|
||||
}
|
||||
|
||||
function testShouldConvertDepositedETHToWrappedETH() public {
|
||||
vm.prank(user);
|
||||
etherToken.deposit{value: 1e20}();
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(etherToken.balanceOf(user), 1e20);
|
||||
assertEq(address(etherToken).balance, 1e20);
|
||||
}
|
||||
|
||||
function testShouldRevertIfCallerAttemptsToWithdrawMoreThanTheirBalance() public {
|
||||
vm.prank(user);
|
||||
etherToken.deposit{value: 1e20}();
|
||||
|
||||
vm.expectRevert();
|
||||
etherToken.withdraw(1e20 + 1);
|
||||
}
|
||||
|
||||
function testShouldConvertWithdrawWrappedETHToETH() public {
|
||||
vm.prank(user);
|
||||
etherToken.deposit{value: 1e20}();
|
||||
vm.prank(user);
|
||||
etherToken.withdraw(100);
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(etherToken.balanceOf(user), 1e20 - 100);
|
||||
assertEq(address(etherToken).balance, 1e20 - 100);
|
||||
assertEq(user.balance, 100);
|
||||
}
|
||||
|
||||
function testShouldConvertSentETHToWrappedETH() public {
|
||||
vm.prank(user);
|
||||
address(etherToken).call{value: 1e20}(new bytes(0));
|
||||
vm.stopPrank();
|
||||
|
||||
assertEq(etherToken.balanceOf(user), 1e20);
|
||||
assertEq(address(etherToken).balance, 1e20);
|
||||
}
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 ZeroEx Intl.
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
pragma solidity ^0.8.17;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "../src/IERC20Token.sol";
|
||||
|
||||
contract ZRXTokenTest is Test {
|
||||
address payable internal owner = payable(vm.addr(1));
|
||||
address payable internal user = payable(vm.addr(2));
|
||||
address payable internal anotherUser = payable(vm.addr(3));
|
||||
uint256 internal totalSupply = 1_000_000_000 * 1e18;
|
||||
IERC20Token zrxToken;
|
||||
|
||||
function setUp() public {
|
||||
vm.deal(owner, 1e20);
|
||||
vm.deal(user, 1e20);
|
||||
|
||||
vm.prank(owner);
|
||||
bytes memory _bytecode = vm.getCode("./out/ZRXToken.sol/ZRXToken.json");
|
||||
address _address;
|
||||
assembly {
|
||||
_address := create(0, add(_bytecode, 0x20), mload(_bytecode))
|
||||
}
|
||||
vm.stopPrank();
|
||||
zrxToken = IERC20Token(address(_address));
|
||||
}
|
||||
|
||||
function testShouldHave18Decimals() public {
|
||||
assertEq(zrxToken.decimals(), 18);
|
||||
}
|
||||
|
||||
function testShouldHaveTotalSupplyOf1Billion() public {
|
||||
assertEq(zrxToken.totalSupply(), totalSupply);
|
||||
}
|
||||
|
||||
function testShouldInitializeOwnerBalanceToTotalSupply() public {
|
||||
assertEq(zrxToken.balanceOf(owner), totalSupply);
|
||||
}
|
||||
|
||||
function testShouldTransferBalanceCorrectly() public {
|
||||
vm.prank(owner);
|
||||
zrxToken.transfer(user, 100);
|
||||
|
||||
assertEq(zrxToken.balanceOf(user), 100);
|
||||
assertEq(zrxToken.balanceOf(owner), totalSupply - 100);
|
||||
}
|
||||
|
||||
function testShouldReturnTrueOnAZeroValueTransfer() public {
|
||||
vm.prank(owner);
|
||||
bool success = zrxToken.transfer(user, 0);
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
function testShouldReturnTrueOnAZeroValueTransferByUserWithZeroBalance() public {
|
||||
vm.prank(anotherUser);
|
||||
bool success = zrxToken.transfer(user, 0);
|
||||
assertTrue(success);
|
||||
}
|
||||
|
||||
function testShouldReturnFalseIfSenderHasInsufficientBalance() public {
|
||||
vm.prank(owner);
|
||||
zrxToken.approve(user, totalSupply + 1);
|
||||
vm.stopPrank();
|
||||
|
||||
bool success = zrxToken.transferFrom(owner, user, totalSupply + 1);
|
||||
assertEq(success, false);
|
||||
}
|
||||
|
||||
function testShouldReturnFalseIfRecipientHasInsufficientAllowance() public {
|
||||
vm.prank(owner);
|
||||
zrxToken.approve(user, totalSupply - 1);
|
||||
vm.stopPrank();
|
||||
|
||||
bool success = zrxToken.transferFrom(owner, user, totalSupply);
|
||||
assertEq(success, false);
|
||||
}
|
||||
|
||||
function testShouldReturnTrueOnAZeroValueApprovedTransfer() public {
|
||||
vm.prank(user);
|
||||
bool success = zrxToken.transferFrom(owner, user, 0);
|
||||
assertEq(success, true);
|
||||
}
|
||||
|
||||
function testShouldNotModifySenderAllowanceIfSetToUINT256Max() public {
|
||||
vm.prank(owner);
|
||||
zrxToken.approve(user, type(uint256).max);
|
||||
vm.stopPrank();
|
||||
|
||||
zrxToken.transferFrom(owner, user, 100);
|
||||
assertEq(zrxToken.allowance(owner, user), type(uint256).max);
|
||||
}
|
||||
|
||||
function testShouldTransferCorrectlyWhenSufficientAllowance() public {
|
||||
vm.prank(owner);
|
||||
zrxToken.approve(user, 1000 * 1e18);
|
||||
vm.stopPrank();
|
||||
|
||||
vm.prank(user);
|
||||
zrxToken.transferFrom(owner, user, 100 * 1e18);
|
||||
assertEq(zrxToken.allowance(owner, user), 900 * 1e18);
|
||||
assertEq(zrxToken.balanceOf(user), 100 * 1e18);
|
||||
assertEq(zrxToken.balanceOf(owner), totalSupply - 100 * 1e18);
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
"overrides": [],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"ignorePatterns": [
|
||||
"lib/**/*",
|
||||
"contracts/**/*",
|
||||
"generated-wrappers/**/*",
|
||||
"generated-artifacts/**/*",
|
||||
"test/generated-wrappers/**/*",
|
||||
"test/generated-artifacts/**/*"
|
||||
|
||||
],
|
||||
"rules": {}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
File diff suppressed because it is too large
Load Diff
@@ -1,540 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.47 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.46 - _January 30, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.45 - _January 23, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.44 - _December 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.43 - _November 23, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.42 - _November 15, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.41 - _November 5, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.40 - _November 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.39 - _October 24, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.38 - _October 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.37 - _October 11, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.36 - _October 5, 2022_
|
||||
|
||||
* Migrate from TSLint to ESLint and fix linting errors (#589)
|
||||
|
||||
## v5.4.35 - _September 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.34 - _September 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.33 - _September 7, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.32 - _September 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.31 - _September 1, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.30 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.29 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.28 - _August 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.27 - _August 10, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.26 - _August 9, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.25 - _August 6, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.24 - _July 27, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.23 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.22 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.21 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.20 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.19 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.18 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.17 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.16 - _December 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.15 - _December 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.14 - _November 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.13 - _November 3, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.12 - _October 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.11 - _September 15, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.10 - _September 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.9 - _August 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.8 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.7 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.6 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.5 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.4 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.3 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.2 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.1 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.0 - _May 5, 2021_
|
||||
|
||||
* Set default ganache gas limit to 100e6 (#197)
|
||||
|
||||
## v5.3.25 - _April 28, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.24 - _April 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.23 - _March 17, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.22 - _February 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.21 - _February 10, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.20 - _January 26, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.19 - _January 13, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.18 - _January 4, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.17 - _December 23, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.16 - _December 17, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.15 - _December 9, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.14 - _December 7, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.13 - _December 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.12 - _November 19, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.11 - _November 13, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.10 - _November 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.9 - _November 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.8 - _November 2, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.7 - _October 28, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.6 - _October 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.5 - _October 21, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.4 - _July 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.3 - _June 24, 2020_
|
||||
|
||||
* Add `msg` param to `assertIntegerRoughlyEquals` (#2576)
|
||||
|
||||
## v5.3.2 - _March 3, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.1 - _February 27, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.3.0 - _February 25, 2020_
|
||||
|
||||
* Add `blockchainTests.config` (#2466)
|
||||
|
||||
## v5.1.5 - _February 15, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.4 - _February 8, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.3 - _February 6, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.2 - _February 4, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.1 - _January 22, 2020_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.1.0 - _January 6, 2020_
|
||||
|
||||
* Added ERC20BridgeProxy to list of unlocked accounts on mainnet fork (#2401)
|
||||
* Add `blockchainTests.live()` for live network tests. (#2407)
|
||||
* Add modifiers to `blockchainTests.fork()`. (#2407)
|
||||
|
||||
## v5.0.1 - _December 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.0.0 - _December 9, 2019_
|
||||
|
||||
* Removed `hex_utils.ts`. Moved to @0x/utils (#2373)
|
||||
|
||||
## v4.0.0 - _December 2, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Add new exports orderHashUtils and transactionHashUtils (#2321)
|
||||
* Remove TransactionHelper and MutatorContractFunction (#2325)
|
||||
* OrderFactory default order expiration time increased from ten minutes to fifteen minutes (#2304)
|
||||
* Add `chainId` to `TransactionFactory` constructor (#1742)
|
||||
* Use new `Order` structure with `domain` field (#1742)
|
||||
* Inherit `chaiSetup` from `@0x/dev-utils` (#1761)
|
||||
* Add `generatePseudoRandomOrderHash()` to `orderUtils` (#1761)
|
||||
* Inherit `OrderStatus` from `@0x/types` (#1761)
|
||||
* Update types for arbitrary fee tokens (#1819)
|
||||
* Remove formatters (#1834)
|
||||
* Add `hexConcat()` in `hex_utils.ts` (#1885)
|
||||
* Introduce Mocha blockchain extensions (#2007)
|
||||
* Move `*FillResults`, `OrderInfo` types to `@0x/types` (#2031)
|
||||
* Add `log_utils.ts` (#2031)
|
||||
* Add `hexRandom()` to `hex_utils.ts` (#2031)
|
||||
* Add the constants: `MAX_UINT256`, `ADDRESS_LENGTH`, `MAX_UINT256_ROOT`, `ONE_ETHER` (#2031)
|
||||
* Make `testCombinatoriallyWithReferenceFuncAsync` non-async (#2031)
|
||||
* Update `testWithReferenceFuncAsync` to work with `RevertErrors` (#2031)
|
||||
* `web3Wrapper` is created with `shouldAllowUnlimitedContractSize` if `UNLIMITED_CONTRACT_SIZE` environment variable is set. (#2075)
|
||||
* Add `toHex()`, `hexLeftPad()`, `hexRightPad()`, and 'hexInvert()' hex utils (#2109)
|
||||
* Add `PPM_DENOMINATOR` and `PPM_100_PERCENT` constants. (#2109)
|
||||
* Increase the number of ganache accounts to 20 (#2109)
|
||||
* Add `Numberish` type. (#2131)
|
||||
* Tweaks/Upgrades to `hex_utils`, most notably `hexSlice()` (#2155)
|
||||
* Add `hexHash()` to `hex_utils` (#2155)
|
||||
* Add `shortZip()` to `lang_utils.ts` (#2155)
|
||||
* Add `number_utils.ts` and `hexSize()` (#2220)
|
||||
* Add `verifyEventsFromLogs()` (#2287)
|
||||
|
||||
## v3.2.0-beta.4 - _December 2, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.0-beta.3 - _November 20, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.0-beta.2 - _November 17, 2019_
|
||||
|
||||
* Drastically reduced bundle size by adding .npmignore, only exporting specific artifacts/wrappers/utils (#2330)
|
||||
* Add new exports orderHashUtils and transactionHashUtils (#2321)
|
||||
* Remove TransactionHelper and MutatorContractFunction (#2325)
|
||||
|
||||
## v3.2.0-beta.1 - _November 7, 2019_
|
||||
|
||||
* OrderFactory default order expiration time increased from ten minutes to fifteen minutes (#2304)
|
||||
|
||||
## v3.2.0-beta.0 - _October 3, 2019_
|
||||
|
||||
* Add `chainId` to `TransactionFactory` constructor (#1742)
|
||||
* Use new `Order` structure with `domain` field (#1742)
|
||||
* Inherit `chaiSetup` from `@0x/dev-utils` (#1761)
|
||||
* Add `generatePseudoRandomOrderHash()` to `orderUtils` (#1761)
|
||||
* Inherit `OrderStatus` from `@0x/types` (#1761)
|
||||
* Update types for arbitrary fee tokens (#1819)
|
||||
* Remove formatters (#1834)
|
||||
* Add `hexConcat()` in `hex_utils.ts` (#1885)
|
||||
* Introduce Mocha blockchain extensions (#2007)
|
||||
* Move `*FillResults`, `OrderInfo` types to `@0x/types` (#2031)
|
||||
* Add `log_utils.ts` (#2031)
|
||||
* Add `haxRandom()` to `hex_utils.ts` (#2031)
|
||||
* Add the constants: `MAX_UINT256`, `ADDRESS_LENGTH`, `MAX_UINT256_ROOT`, `ONE_ETHER` (#2031)
|
||||
* Make `testCombinatoriallyWithReferenceFuncAsync` non-async (#2031)
|
||||
* Update `testWithReferenceFuncAsync` to work with `RevertErrors` (#2031)
|
||||
* `web3Wrapper` is created with `shouldAllowUnlimitedContractSize` if `UNLIMITED_CONTRACT_SIZE` environment variable is set. (#2075)
|
||||
* Add `toHex()`, `hexLeftPad()`, `hexRightPad()`, and 'hexInvert()' hex utils (#2109)
|
||||
* Add `PPM_DENOMINATOR` and `PPM_100_PERCENT` constants. (#2109)
|
||||
* Increase the number of ganache accounts to 20 (#2109)
|
||||
* Add `Numberish` type. (#2131)
|
||||
* Tweaks/Upgrades to `hex_utils`, most notably `hexSlice()` (#2155)
|
||||
* Add `hexHash()` to `hex_utils` (#2155)
|
||||
* Add `shortZip()` to `lang_utils.ts` (#2155)
|
||||
* Add `number_utils.ts` and `hexSize()` (#2220)
|
||||
* Add `verifyEventsFromLogs()` (#2287)
|
||||
|
||||
## v3.1.16 - _September 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.15 - _September 3, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.14 - _August 22, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.13 - _August 8, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.12 - _July 31, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.11 - _July 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.10 - _July 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.9 - _July 13, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.8 - _July 13, 2019_
|
||||
|
||||
* Fixed false positives in `expectTransactionFailedAsync` and `expectContractCallFailedAsync` (#1852)
|
||||
|
||||
## v3.1.7 - _May 24, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.6 - _May 15, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.5 - _May 14, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.3 - _May 10, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.2 - _April 11, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.1 - _March 21, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.0 - _March 20, 2019_
|
||||
|
||||
* Added ERC1155Proxy test constants and interfaces (#1661)
|
||||
|
||||
## v3.0.9 - _Invalid date_
|
||||
|
||||
* Set evmVersion to byzantium (#1678)
|
||||
* Remove Coordinator EIP712 constants. They're now in the `order-utils` package. (#1705)
|
||||
|
||||
## v3.0.8 - _March 1, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.7 - _February 27, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.6 - _February 26, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.5 - _February 25, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.4 - _February 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.3 - _February 7, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.2 - _February 7, 2019_
|
||||
|
||||
* Fake publish to enable pinning
|
||||
|
||||
## v3.0.1 - _February 6, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.0.0 - _February 5, 2019_
|
||||
|
||||
* Upgrade the bignumber.js to v8.0.2 (#1517)
|
||||
* Import `ZeroExTransaction` type instead of using type defined in this package (#1576)
|
||||
|
||||
## v2.0.1 - _January 17, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.0 - _January 15, 2019_
|
||||
|
||||
* Renamed OrderStatus enum members to PascalCase to conform with tslint enum-naming rule (#1474)
|
||||
|
||||
## v1.0.4 - _January 11, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.3 - _January 9, 2019_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.2 - _December 13, 2018_
|
||||
|
||||
* Dependencies updated
|
@@ -1,73 +0,0 @@
|
||||
## Contracts test utils
|
||||
|
||||
This package contains test utilities used by other smart contracts packages.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import {
|
||||
chaiSetup,
|
||||
constants,
|
||||
expectContractCallFailedAsync,
|
||||
expectContractCreationFailedAsync,
|
||||
expectTransactionFailedAsync,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
increaseTimeAndMineBlockAsync,
|
||||
provider,
|
||||
sendTransactionResult,
|
||||
txDefaults,
|
||||
web3Wrapper,
|
||||
} from '@0x/contracts-test-utils';
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
|
||||
|
||||
Please read our [contribution guidelines](../../.github/CONTRIBUTING.md) before getting started.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
|
||||
|
||||
```bash
|
||||
yarn config set workspaces-experimental true
|
||||
```
|
||||
|
||||
Then install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-test-utils yarn build
|
||||
```
|
||||
|
||||
Or continuously rebuild on change:
|
||||
|
||||
```bash
|
||||
PKG=@0x/contracts-test-utils yarn watch
|
||||
```
|
||||
|
||||
### Clean
|
||||
|
||||
```bash
|
||||
yarn clean
|
||||
```
|
||||
|
||||
### Lint
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
### Run Tests
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
@@ -1,78 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.47",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Test utils for 0x contracts",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"build:ci": "yarn build",
|
||||
"test": "yarn run_mocha",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"clean": "shx rm -rf lib",
|
||||
"lint": "eslint src test",
|
||||
"fix": "eslint --fix src test",
|
||||
"test:ci": "yarn test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/protocol.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/protocol/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
|
||||
"devDependencies": {
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.35",
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/dev-utils": "^5.0.0",
|
||||
"@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/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "12.12.54",
|
||||
"bn.js": "^4.11.8",
|
||||
"chai": "^4.0.1",
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"decimal.js": "^10.2.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
"ethers": "~4.0.4",
|
||||
"js-combinatorics": "^0.5.3",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"gitHead": "4f91bfd907996b2f4dd383778b50c479c2602b56"
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
export abstract class AbstractAssetWrapper {
|
||||
public abstract getProxyId(): string;
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
import { hexUtils } from '@0x/utils';
|
||||
|
||||
import { constants } from './constants';
|
||||
|
||||
/**
|
||||
* Generates a random address.
|
||||
*/
|
||||
export function randomAddress(): string {
|
||||
return hexUtils.random(constants.ADDRESS_LENGTH);
|
||||
}
|
@@ -1,201 +0,0 @@
|
||||
import { RevertReason } from '@0x/types';
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { NodeType } from '@0x/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import { TransactionReceipt, TransactionReceiptStatus, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { web3Wrapper } from './web3_wrapper';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
let nodeType: NodeType | undefined;
|
||||
|
||||
// Represents the return value of a `sendTransaction` call. The Promise should
|
||||
// resolve with either a transaction receipt or a transaction hash.
|
||||
export type sendTransactionResult = Promise<TransactionReceipt | TransactionReceiptWithDecodedLogs | string>;
|
||||
|
||||
/**
|
||||
* Returns ganacheError if the backing Ethereum node is Ganache and gethError
|
||||
* if it is Geth.
|
||||
* @param ganacheError the error to be returned if the backing node is Ganache.
|
||||
* @param gethError the error to be returned if the backing node is Geth.
|
||||
* @returns either the given ganacheError or gethError depending on the backing
|
||||
* node.
|
||||
*/
|
||||
async function _getGanacheOrGethErrorAsync(ganacheError: string, gethError: string): Promise<string> {
|
||||
if (nodeType === undefined) {
|
||||
nodeType = await web3Wrapper.getNodeTypeAsync();
|
||||
}
|
||||
switch (nodeType) {
|
||||
case NodeType.Ganache:
|
||||
return ganacheError;
|
||||
case NodeType.Geth:
|
||||
return gethError;
|
||||
default:
|
||||
throw new Error(`Unknown node type: ${nodeType}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function _getInsufficientFundsErrorMessageAsync(): Promise<string> {
|
||||
return _getGanacheOrGethErrorAsync('insufficient funds for gas * price + value', 'insufficient funds');
|
||||
}
|
||||
|
||||
async function _getTransactionFailedErrorMessageAsync(): Promise<string> {
|
||||
return _getGanacheOrGethErrorAsync('revert', 'always failing transaction');
|
||||
}
|
||||
|
||||
async function _getContractCallFailedErrorMessageAsync(): Promise<string> {
|
||||
return _getGanacheOrGethErrorAsync('revert', 'Contract call failed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected error message for an 'invalid opcode' resulting from a
|
||||
* contract call. The exact error message depends on the backing Ethereum node.
|
||||
*/
|
||||
export async function getInvalidOpcodeErrorMessageForCallAsync(): Promise<string> {
|
||||
return _getGanacheOrGethErrorAsync('invalid opcode', 'Contract call failed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected error message for the given revert reason resulting from
|
||||
* a sendTransaction call. The exact error message depends on the backing
|
||||
* Ethereum node and whether it supports revert reasons.
|
||||
* @param reason a specific revert reason.
|
||||
* @returns the expected error message.
|
||||
*/
|
||||
export async function getRevertReasonOrErrorMessageForSendTransactionAsync(reason: RevertReason): Promise<string> {
|
||||
return _getGanacheOrGethErrorAsync(reason, 'always failing transaction');
|
||||
}
|
||||
|
||||
/**
|
||||
* Rejects if the given Promise does not reject with an error indicating
|
||||
* insufficient funds.
|
||||
* @param p a promise resulting from a contract call or sendTransaction call.
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectInsufficientFundsAsync<T>(p: Promise<T>): Promise<void> {
|
||||
const errMessage = await _getInsufficientFundsErrorMessageAsync();
|
||||
return expect(p).to.be.rejectedWith(errMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the the sendTransaction call fails with the given revert reason.
|
||||
* However, since Geth does not support revert reasons for sendTransaction, this
|
||||
* falls back to expectTransactionFailedWithoutReasonAsync if the backing
|
||||
* Ethereum node is Geth.
|
||||
* @param p a Promise resulting from a sendTransaction call
|
||||
* @param reason a specific revert reason
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectTransactionFailedAsync(p: sendTransactionResult, reason: RevertReason): Promise<void> {
|
||||
// HACK(albrow): This dummy `catch` should not be necessary, but if you
|
||||
// remove it, there is an uncaught exception and the Node process will
|
||||
// forcibly exit. It's possible this is a false positive in
|
||||
// make-promises-safe.
|
||||
p.catch(e => {
|
||||
_.noop(e);
|
||||
});
|
||||
|
||||
if (nodeType === undefined) {
|
||||
nodeType = await web3Wrapper.getNodeTypeAsync();
|
||||
}
|
||||
const rejectionMessageRegex = new RegExp(`^VM Exception while processing transaction: revert ${reason}$`);
|
||||
switch (nodeType) {
|
||||
case NodeType.Ganache:
|
||||
return expect(p).to.be.rejectedWith(rejectionMessageRegex);
|
||||
case NodeType.Geth:
|
||||
logUtils.warn(
|
||||
'WARNING: Geth does not support revert reasons for sendTransaction. This test will pass if the transaction fails for any reason.',
|
||||
);
|
||||
return expectTransactionFailedWithoutReasonAsync(p);
|
||||
default:
|
||||
throw new Error(`Unknown node type: ${nodeType}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the transaction fails without a revert reason, or if the
|
||||
* corresponding transactionReceipt has a status of 0 or '0', indicating
|
||||
* failure.
|
||||
* @param p a Promise resulting from a sendTransaction call
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectTransactionFailedWithoutReasonAsync(p: sendTransactionResult): Promise<void> {
|
||||
return p
|
||||
.then(async result => {
|
||||
let txReceiptStatus: TransactionReceiptStatus;
|
||||
if (_.isString(result)) {
|
||||
// Result is a txHash. We need to make a web3 call to get the
|
||||
// receipt, then get the status from the receipt.
|
||||
const txReceipt = await web3Wrapper.awaitTransactionMinedAsync(result);
|
||||
txReceiptStatus = txReceipt.status;
|
||||
} else if ('status' in result) {
|
||||
// Result is a transaction receipt, so we can get the status
|
||||
// directly.
|
||||
txReceiptStatus = result.status;
|
||||
} else {
|
||||
throw new Error(`Unexpected result type: ${typeof result}`);
|
||||
}
|
||||
expect(_.toString(txReceiptStatus)).to.equal(
|
||||
'0',
|
||||
'Expected transaction to fail but receipt had a non-zero status, indicating success',
|
||||
);
|
||||
})
|
||||
.catch(async err => {
|
||||
// If the promise rejects, we expect a specific error message,
|
||||
// depending on the backing Ethereum node type.
|
||||
const errMessage = await _getTransactionFailedErrorMessageAsync();
|
||||
expect(err.message).to.include(errMessage);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the the contract call fails with the given revert reason.
|
||||
* @param p a Promise resulting from a contract call
|
||||
* @param reason a specific revert reason
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectContractCallFailedAsync<T>(p: Promise<T>, reason: RevertReason): Promise<void> {
|
||||
const rejectionMessageRegex = new RegExp(`^VM Exception while processing transaction: revert ${reason}$`);
|
||||
return expect(p).to.be.rejectedWith(rejectionMessageRegex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the contract call fails without a revert reason.
|
||||
* @param p a Promise resulting from a contract call
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectContractCallFailedWithoutReasonAsync<T>(p: Promise<T>): Promise<void> {
|
||||
const errMessage = await _getContractCallFailedErrorMessageAsync();
|
||||
return expect(p).to.be.rejectedWith(errMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the contract creation/deployment fails without a revert reason.
|
||||
* @param p a Promise resulting from a contract creation/deployment
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectContractCreationFailedAsync<T>(
|
||||
p: sendTransactionResult,
|
||||
reason: RevertReason,
|
||||
): Promise<void> {
|
||||
return expectTransactionFailedAsync(p, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves if the contract creation/deployment fails without a revert reason.
|
||||
* @param p a Promise resulting from a contract creation/deployment
|
||||
* @returns a new Promise which will reject if the conditions are not met and
|
||||
* otherwise resolve with no value.
|
||||
*/
|
||||
export async function expectContractCreationFailedWithoutReasonAsync<T>(p: Promise<T>): Promise<void> {
|
||||
const errMessage = await _getTransactionFailedErrorMessageAsync();
|
||||
return expect(p).to.be.rejectedWith(errMessage);
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
import { constants } from './constants';
|
||||
import { web3Wrapper } from './web3_wrapper';
|
||||
|
||||
let firstAccount: string | undefined;
|
||||
|
||||
/**
|
||||
* Increases time by the given number of seconds and then mines a block so that
|
||||
* the current block timestamp has the offset applied.
|
||||
* @param seconds the number of seconds by which to incrase the time offset.
|
||||
* @returns a new Promise which will resolve with the new total time offset or
|
||||
* reject if the time could not be increased.
|
||||
*/
|
||||
export async function increaseTimeAndMineBlockAsync(seconds: number): Promise<number> {
|
||||
if (firstAccount === undefined) {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
firstAccount = accounts[0];
|
||||
}
|
||||
|
||||
const offset = await web3Wrapper.increaseTimeAsync(seconds);
|
||||
// Note: we need to send a transaction after increasing time so
|
||||
// that a block is actually mined. The contract looks at the
|
||||
// last mined block for the timestamp.
|
||||
await web3Wrapper.awaitTransactionSuccessAsync(
|
||||
await web3Wrapper.sendTransactionAsync({ from: firstAccount, to: firstAccount, value: 0 }),
|
||||
constants.AWAIT_TRANSACTION_MINED_MS,
|
||||
);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the latest block in seconds since the Unix epoch.
|
||||
* @returns a new Promise which will resolve with the timestamp in seconds.
|
||||
*/
|
||||
export async function getLatestBlockTimestampAsync(): Promise<number> {
|
||||
const currentBlockIfExists = await web3Wrapper.getBlockIfExistsAsync('latest');
|
||||
if (currentBlockIfExists === undefined) {
|
||||
throw new Error(`Unable to fetch latest block.`);
|
||||
}
|
||||
return currentBlockIfExists.timestamp;
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
import { chaiSetup } from '@0x/dev-utils';
|
||||
export { chaiSetup } from '@0x/dev-utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
// Set up chai.
|
||||
chaiSetup.configure();
|
||||
export const expect = chai.expect;
|
@@ -1,8 +0,0 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
/**
|
||||
* Get the codesize of a provided artifact.
|
||||
*/
|
||||
export function getCodesizeFromArtifact(artifact: ContractArtifact): number {
|
||||
return (artifact.compilerOutput.evm.bytecode.object.length - 2) / 2;
|
||||
}
|
@@ -1,113 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as combinatorics from 'js-combinatorics';
|
||||
|
||||
import { testWithReferenceFuncAsync } from './test_with_reference';
|
||||
|
||||
// A set of values corresponding to the uint256 type in Solidity. This set
|
||||
// contains some notable edge cases, including some values which will overflow
|
||||
// the uint256 type when used in different mathematical operations.
|
||||
export const uint256Values = [
|
||||
new BigNumber(0),
|
||||
new BigNumber(1),
|
||||
new BigNumber(2),
|
||||
// Non-trivial big number.
|
||||
new BigNumber(2).pow(64),
|
||||
// Max that does not overflow when squared.
|
||||
new BigNumber(2).pow(128).minus(1),
|
||||
// Min that does overflow when squared.
|
||||
new BigNumber(2).pow(128),
|
||||
// Max that does not overflow when doubled.
|
||||
new BigNumber(2).pow(255).minus(1),
|
||||
// Min that does overflow when doubled.
|
||||
new BigNumber(2).pow(255),
|
||||
// Max that does not overflow.
|
||||
new BigNumber(2).pow(256).minus(1),
|
||||
];
|
||||
|
||||
// A set of values corresponding to the bytes32 type in Solidity.
|
||||
export const bytes32Values = [
|
||||
// Min
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000001',
|
||||
'0x0000000000000000000000000000000000000000000000000000000000000002',
|
||||
// Non-trivial big number.
|
||||
'0x000000000000f000000000000000000000000000000000000000000000000000',
|
||||
// Max
|
||||
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
||||
];
|
||||
|
||||
export function testCombinatoriallyWithReferenceFunc<P0, P1, R>(
|
||||
name: string,
|
||||
referenceFunc: (p0: P0, p1: P1) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1) => Promise<R>,
|
||||
allValues: [P0[], P1[]],
|
||||
): void;
|
||||
export function testCombinatoriallyWithReferenceFunc<P0, P1, P2, R>(
|
||||
name: string,
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2) => Promise<R>,
|
||||
allValues: [P0[], P1[], P2[]],
|
||||
): void;
|
||||
export function testCombinatoriallyWithReferenceFunc<P0, P1, P2, P3, R>(
|
||||
name: string,
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2, p3: P3) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2, p3: P3) => Promise<R>,
|
||||
allValues: [P0[], P1[], P2[], P3[]],
|
||||
): void;
|
||||
export function testCombinatoriallyWithReferenceFunc<P0, P1, P2, P3, P4, R>(
|
||||
name: string,
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => Promise<R>,
|
||||
allValues: [P0[], P1[], P2[], P3[], P4[]],
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Uses combinatorics to test the behavior of a test function by comparing it to
|
||||
* the expected behavior (defined by a reference function) for a large number of
|
||||
* possible input values.
|
||||
*
|
||||
* First generates test cases by taking the cartesian product of the given
|
||||
* values. Each test case is a set of N values corresponding to the N arguments
|
||||
* for the test func and the reference func. For each test case, first the
|
||||
* reference function will be called to obtain an "expected result", or if the
|
||||
* reference function throws/rejects, an "expected error". Next, the test
|
||||
* function will be called to obtain an "actual result", or if the test function
|
||||
* throws/rejects, an "actual error". Each test case passes if at least one of
|
||||
* the following conditions is met:
|
||||
*
|
||||
* 1) Neither the reference function or the test function throw and the
|
||||
* "expected result" equals the "actual result".
|
||||
*
|
||||
* 2) Both the reference function and the test function throw and the "actual
|
||||
* error" message *contains* the "expected error" message.
|
||||
*
|
||||
* The first test case which does not meet one of these conditions will cause
|
||||
* the entire test to fail and this function will throw/reject.
|
||||
*
|
||||
* @param referenceFuncAsync a reference function implemented in pure
|
||||
* JavaScript/TypeScript which accepts N arguments and returns the "expected
|
||||
* result" or "expected error" for a given test case.
|
||||
* @param testFuncAsync a test function which, e.g., makes a call or sends a
|
||||
* transaction to a contract. It accepts the same N arguments returns the
|
||||
* "actual result" or "actual error" for a given test case.
|
||||
* @param values an array of N arrays. Each inner array is a set of possible
|
||||
* values which are passed into both the reference function and the test
|
||||
* function.
|
||||
* @return A Promise that resolves if the test passes and rejects if the test
|
||||
* fails, according to the rules described above.
|
||||
*/
|
||||
export function testCombinatoriallyWithReferenceFunc(
|
||||
name: string,
|
||||
referenceFuncAsync: (...args: any[]) => Promise<any>,
|
||||
testFuncAsync: (...args: any[]) => Promise<any>,
|
||||
allValues: any[],
|
||||
): void {
|
||||
const testCases = combinatorics.cartesianProduct(...allValues);
|
||||
let counter = 0;
|
||||
testCases.forEach(async testCase => {
|
||||
counter += 1;
|
||||
it(`${name} ${counter}/${testCases.length}`, async () => {
|
||||
await testWithReferenceFuncAsync(referenceFuncAsync, testFuncAsync, testCase as any);
|
||||
});
|
||||
});
|
||||
}
|
@@ -1,112 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ExchangeFunctionName } from './types';
|
||||
|
||||
const TESTRPC_PRIVATE_KEYS_STRINGS = [
|
||||
'0xf2f48ee19680706196e2e339e5da3491186e0c4c5030670656b0e0164837257d',
|
||||
'0x5d862464fe9303452126c8bc94274b8c5f9874cbd219789b3eb2128075a76f72',
|
||||
'0xdf02719c4df8b9b8ac7f551fcb5d9ef48fa27eef7a66453879f4d8fdc6e78fb1',
|
||||
'0xff12e391b79415e941a94de3bf3a9aee577aed0731e297d5cfa0b8a1e02fa1d0',
|
||||
'0x752dd9cf65e68cfaba7d60225cbdbc1f4729dd5e5507def72815ed0d8abc6249',
|
||||
'0xefb595a0178eb79a8df953f87c5148402a224cdf725e88c0146727c6aceadccd',
|
||||
'0x83c6d2cc5ddcf9711a6d59b417dc20eb48afd58d45290099e5987e3d768f328f',
|
||||
'0xbb2d3f7c9583780a7d3904a2f55d792707c345f21de1bacb2d389934d82796b2',
|
||||
'0xb2fd4d29c1390b71b8795ae81196bfd60293adf99f9d32a0aff06288fcdac55f',
|
||||
'0x23cb7121166b9a2f93ae0b7c05bde02eae50d64449b2cbb42bc84e9d38d6cc89',
|
||||
'0x5ad34d7f8704ed33ab9e8dc30a76a8c48060649204c1f7b21b973235bba8092f',
|
||||
'0xf18b03c1ae8e3876d76f20c7a5127a169dd6108c55fe9ce78bc7a91aca67dee3',
|
||||
'0x4ccc4e7d7843e0701295e8fd671332a0e2f1e92d0dab16e8792e91cb0b719c9d',
|
||||
'0xd7638ae813450e710e6f1b09921cc1593181073ce2099fb418fc03a933c7f41f',
|
||||
'0xbc7bbca8ca15eb567be60df82e4452b13072dcb60db89747e3c85df63d8270ca',
|
||||
'0x55131517839bf782e6e573bc3ac8f262efd2b6cb0ac86e8f147db26fcbdb15a5',
|
||||
'0x6c2b5a16e327e0c4e7fafca5ae35616141de81f77da66ee0857bc3101d446e68',
|
||||
'0xfd79b71625eec963e6ec42e9b5b10602c938dfec29cbbc7d17a492dd4f403859',
|
||||
'0x3003eace3d4997c52ba69c2ca97a6b5d0d1216d894035a97071590ee284c1023',
|
||||
'0x84a8bb71450a1b82be2b1cdd25d079cbf23dc8054e94c47ad14510aa967f45de',
|
||||
];
|
||||
|
||||
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
|
||||
|
||||
export const constants = {
|
||||
BASE_16: 16,
|
||||
INVALID_OPCODE: 'invalid opcode',
|
||||
TESTRPC_CHAIN_ID: 1337,
|
||||
// Note(albrow): In practice V8 and most other engines limit the minimum
|
||||
// interval for setInterval to 10ms. We still set it to 0 here in order to
|
||||
// ensure we always use the minimum interval.
|
||||
AWAIT_TRANSACTION_MINED_MS: 0,
|
||||
MAX_ETHERTOKEN_WITHDRAW_GAS: 43000,
|
||||
MAX_EXECUTE_TRANSACTION_GAS: 1000000,
|
||||
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
|
||||
MAX_TOKEN_APPROVE_GAS: 60000,
|
||||
MAX_TRANSFER_FROM_GAS: 150000,
|
||||
MAX_MATCH_ORDERS_GAS: 400000,
|
||||
DUMMY_TOKEN_NAME: '',
|
||||
DUMMY_TOKEN_SYMBOL: '',
|
||||
DUMMY_TOKEN_DECIMALS: new BigNumber(18),
|
||||
DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(0),
|
||||
NULL_BYTES: '0x',
|
||||
NUM_DUMMY_ERC20_TO_DEPLOY: 4,
|
||||
NUM_DUMMY_ERC721_TO_DEPLOY: 2,
|
||||
NUM_ERC721_TOKENS_TO_MINT: 4,
|
||||
NUM_DUMMY_ERC1155_CONTRACTS_TO_DEPLOY: 2,
|
||||
NUM_ERC1155_FUNGIBLE_TOKENS_MINT: 4,
|
||||
NUM_ERC1155_NONFUNGIBLE_TOKENS_MINT: 4,
|
||||
NULL_BYTES4: '0x00000000',
|
||||
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
|
||||
NULL_BYTES32: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: MAX_UINT256,
|
||||
MAX_UINT256,
|
||||
MAX_UINT32: new BigNumber(2).pow(32).minus(1),
|
||||
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
|
||||
INITIAL_ERC20_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
|
||||
INITIAL_ERC20_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
|
||||
INITIAL_ERC1155_FUNGIBLE_BALANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
|
||||
INITIAL_ERC1155_FUNGIBLE_ALLOWANCE: Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18),
|
||||
STATIC_ORDER_PARAMS: {
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18),
|
||||
},
|
||||
WORD_LENGTH: 32,
|
||||
ADDRESS_LENGTH: 20,
|
||||
ZERO_AMOUNT: new BigNumber(0),
|
||||
PERCENTAGE_DENOMINATOR: new BigNumber(10).pow(18),
|
||||
TIME_BUFFER: new BigNumber(1000),
|
||||
KECCAK256_NULL: ethUtil.bufferToHex(ethUtil.keccak256(Buffer.alloc(0))),
|
||||
MAX_UINT256_ROOT: new BigNumber('340282366920938463463374607431768211456'),
|
||||
ONE_ETHER: new BigNumber(1e18),
|
||||
EIP712_DOMAIN_NAME: '0x Protocol',
|
||||
EIP712_DOMAIN_VERSION: '3.0.0',
|
||||
DEFAULT_GAS_PRICE: 1,
|
||||
NUM_TEST_ACCOUNTS: 20,
|
||||
PPM_DENOMINATOR: 1e6,
|
||||
PPM_100_PERCENT: 1e6,
|
||||
MAX_CODE_SIZE: 24576,
|
||||
SINGLE_FILL_FN_NAMES: [ExchangeFunctionName.FillOrder, ExchangeFunctionName.FillOrKillOrder],
|
||||
BATCH_FILL_FN_NAMES: [
|
||||
ExchangeFunctionName.BatchFillOrders,
|
||||
ExchangeFunctionName.BatchFillOrKillOrders,
|
||||
ExchangeFunctionName.BatchFillOrdersNoThrow,
|
||||
],
|
||||
MARKET_FILL_FN_NAMES: [
|
||||
ExchangeFunctionName.MarketBuyOrdersFillOrKill,
|
||||
ExchangeFunctionName.MarketSellOrdersFillOrKill,
|
||||
ExchangeFunctionName.MarketBuyOrdersNoThrow,
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
],
|
||||
MATCH_ORDER_FN_NAMES: [ExchangeFunctionName.MatchOrders, ExchangeFunctionName.MatchOrdersWithMaximalFill],
|
||||
BATCH_MATCH_ORDER_FN_NAMES: [
|
||||
ExchangeFunctionName.BatchMatchOrders,
|
||||
ExchangeFunctionName.BatchMatchOrdersWithMaximalFill,
|
||||
],
|
||||
CANCEL_ORDER_FN_NAMES: [
|
||||
ExchangeFunctionName.CancelOrder,
|
||||
ExchangeFunctionName.BatchCancelOrders,
|
||||
ExchangeFunctionName.CancelOrdersUpTo,
|
||||
],
|
||||
};
|
@@ -1,62 +0,0 @@
|
||||
export { AbstractAssetWrapper } from './abstract_asset_wrapper';
|
||||
export { constants } from './constants';
|
||||
export {
|
||||
expectContractCallFailedAsync,
|
||||
expectContractCallFailedWithoutReasonAsync,
|
||||
expectContractCreationFailedAsync,
|
||||
expectContractCreationFailedWithoutReasonAsync,
|
||||
expectInsufficientFundsAsync,
|
||||
expectTransactionFailedAsync,
|
||||
sendTransactionResult,
|
||||
expectTransactionFailedWithoutReasonAsync,
|
||||
getInvalidOpcodeErrorMessageForCallAsync,
|
||||
getRevertReasonOrErrorMessageForSendTransactionAsync,
|
||||
} from './assertions';
|
||||
export { getLatestBlockTimestampAsync, increaseTimeAndMineBlockAsync } from './block_timestamp';
|
||||
export { provider, txDefaults, web3Wrapper } from './web3_wrapper';
|
||||
export { LogDecoder } from './log_decoder';
|
||||
export { filterLogs, filterLogsToArguments, verifyEvents, verifyEventsFromLogs } from './log_utils';
|
||||
export { signingUtils } from './signing_utils';
|
||||
export { orderUtils } from './order_utils';
|
||||
export { typeEncodingUtils } from './type_encoding_utils';
|
||||
export { profiler } from './profiler';
|
||||
export { Web3ProviderEngine } from '@0x/subproviders';
|
||||
export { randomAddress } from './address_utils';
|
||||
export { OrderFactory } from './order_factory';
|
||||
export { bytes32Values, testCombinatoriallyWithReferenceFunc, uint256Values } from './combinatorial_utils';
|
||||
export { TransactionFactory } from './transaction_factory';
|
||||
export { testWithReferenceFuncAsync } from './test_with_reference';
|
||||
export {
|
||||
BatchMatchOrder,
|
||||
ContractName,
|
||||
ERC20BalancesByOwner,
|
||||
EthBalancesByOwner,
|
||||
FillEventArgs,
|
||||
MarketBuyOrders,
|
||||
MarketSellOrders,
|
||||
Numberish,
|
||||
OrderStatus,
|
||||
Token,
|
||||
TokenBalances,
|
||||
TransactionDataParams,
|
||||
ExchangeFunctionName,
|
||||
} from './types';
|
||||
export { blockchainTests, BlockchainTestsEnvironment, describe } from './mocha_blockchain';
|
||||
export { chaiSetup, expect } from './chai_setup';
|
||||
export { getCodesizeFromArtifact } from './codesize';
|
||||
export { replaceKeysDeep, shortZip } from './lang_utils';
|
||||
export {
|
||||
assertIntegerRoughlyEquals,
|
||||
assertRoughlyEquals,
|
||||
fromFixed,
|
||||
getRandomFloat,
|
||||
getRandomInteger,
|
||||
getRandomPortion,
|
||||
getNumericalDivergence,
|
||||
getPercentageOfValue,
|
||||
toBaseUnitAmount,
|
||||
toDecimal,
|
||||
toFixed,
|
||||
} from './number_utils';
|
||||
export { orderHashUtils } from './order_hash';
|
||||
export { transactionHashUtils } from './transaction_hash';
|
@@ -1,24 +0,0 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
/**
|
||||
* _.zip() that clips to the shortest array.
|
||||
*/
|
||||
export function shortZip<T1, T2>(a: T1[], b: T2[]): Array<[T1, T2]> {
|
||||
const minLength = Math.min(a.length, b.length);
|
||||
return _.zip(a.slice(0, minLength), b.slice(0, minLength)) as Array<[T1, T2]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the keys in a deeply nested object. Adapted from https://stackoverflow.com/a/39126851
|
||||
*/
|
||||
export function replaceKeysDeep(
|
||||
obj: Record<string, unknown>,
|
||||
mapKeys: (key: string) => string | void,
|
||||
): _.Dictionary<Record<string, unknown>> {
|
||||
return _.transform(obj, (result, value, key) => {
|
||||
const currentKey = mapKeys(key) || key;
|
||||
result[currentKey] = _.isObject(value)
|
||||
? replaceKeysDeep(value as Record<string, unknown>, mapKeys)
|
||||
: (value as Record<string, unknown>);
|
||||
});
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
import { AbiDecoder, BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import {
|
||||
AbiDefinition,
|
||||
ContractArtifact,
|
||||
DecodedLogArgs,
|
||||
LogEntry,
|
||||
LogWithDecodedArgs,
|
||||
RawLog,
|
||||
TransactionReceipt,
|
||||
TransactionReceiptWithDecodedLogs,
|
||||
} from 'ethereum-types';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export class LogDecoder {
|
||||
private readonly _web3Wrapper: Web3Wrapper;
|
||||
private readonly _abiDecoder: AbiDecoder;
|
||||
public static wrapLogBigNumbers(log: any): any {
|
||||
const argNames = _.keys(log.args);
|
||||
for (const argName of argNames) {
|
||||
const isWeb3BigNumber = _.startsWith(log.args[argName].constructor.toString(), 'function BigNumber(');
|
||||
if (isWeb3BigNumber) {
|
||||
log.args[argName] = new BigNumber(log.args[argName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
constructor(web3Wrapper: Web3Wrapper, artifacts: { [contractName: string]: ContractArtifact }) {
|
||||
this._web3Wrapper = web3Wrapper;
|
||||
const abiArrays: AbiDefinition[][] = [];
|
||||
_.forEach(artifacts, (artifact: ContractArtifact) => {
|
||||
const compilerOutput = artifact.compilerOutput;
|
||||
abiArrays.push(compilerOutput.abi);
|
||||
});
|
||||
this._abiDecoder = new AbiDecoder(abiArrays);
|
||||
}
|
||||
public decodeLogOrThrow<ArgsType extends DecodedLogArgs>(log: LogEntry): LogWithDecodedArgs<ArgsType> | RawLog {
|
||||
const logWithDecodedArgsOrLog = this._abiDecoder.tryToDecodeLogOrNoop(log);
|
||||
if ((logWithDecodedArgsOrLog as LogWithDecodedArgs<ArgsType>).args === undefined) {
|
||||
throw new Error(`Unable to decode log: ${JSON.stringify(log)}`);
|
||||
}
|
||||
LogDecoder.wrapLogBigNumbers(logWithDecodedArgsOrLog);
|
||||
return logWithDecodedArgsOrLog;
|
||||
}
|
||||
public async getTxWithDecodedLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
const receipt = await this._web3Wrapper.awaitTransactionSuccessAsync(txHash);
|
||||
return this.decodeReceiptLogs(receipt);
|
||||
}
|
||||
public decodeReceiptLogs(receipt: TransactionReceipt): TransactionReceiptWithDecodedLogs {
|
||||
const decodedLogs = (receipt.logs as LogEntry[]).map(log => this.decodeLogOrThrow(log));
|
||||
return _.merge({}, receipt, { logs: decodedLogs });
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
import { LogEntry, LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
|
||||
import { expect } from './chai_setup';
|
||||
|
||||
/**
|
||||
* Filter logs by event name/type.
|
||||
*/
|
||||
export function filterLogs<TEventArgs>(logs: LogEntry[], event: string): Array<LogWithDecodedArgs<TEventArgs>> {
|
||||
return (logs as Array<LogWithDecodedArgs<any>>).filter(log => log.event === event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter logs by event name/type and convert to arguments.
|
||||
*/
|
||||
export function filterLogsToArguments<TEventArgs>(logs: LogEntry[], event: string): TEventArgs[] {
|
||||
return filterLogs<TEventArgs>(logs, event).map(log => log.args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that a transaction emitted the expected events of a particular type.
|
||||
*/
|
||||
export function verifyEvents<TEventArgs>(
|
||||
txReceipt: TransactionReceiptWithDecodedLogs,
|
||||
expectedEvents: TEventArgs[],
|
||||
eventName: string,
|
||||
): void {
|
||||
return verifyEventsFromLogs(txReceipt.logs, expectedEvents, eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a collection of logs, verifies that matching events are identical.
|
||||
*/
|
||||
export function verifyEventsFromLogs<TEventArgs>(
|
||||
logs: LogEntry[],
|
||||
expectedEvents: TEventArgs[],
|
||||
eventName: string,
|
||||
): void {
|
||||
const _logs = filterLogsToArguments<TEventArgs>(logs, eventName);
|
||||
expect(_logs.length, `Number of ${eventName} events emitted`).to.eq(expectedEvents.length);
|
||||
_logs.forEach((log, index) => {
|
||||
expect(log, `${eventName} event ${index}`).to.deep.equal({ ...log, ...expectedEvents[index] });
|
||||
});
|
||||
}
|
@@ -1,428 +0,0 @@
|
||||
import { BlockchainLifecycle, web3Factory } from '@0x/dev-utils';
|
||||
import { RPCSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { providerUtils } from '@0x/utils';
|
||||
import { TxData, Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as _ from 'lodash';
|
||||
import * as mocha from 'mocha';
|
||||
import * as process from 'process';
|
||||
|
||||
import { provider, providerConfigs, txDefaults, web3Wrapper } from './web3_wrapper';
|
||||
|
||||
export type ISuite = mocha.ISuite;
|
||||
export type ISuiteCallbackContext = mocha.ISuiteCallbackContext;
|
||||
export type SuiteCallback = (this: ISuiteCallbackContext) => void;
|
||||
export type ContextDefinitionCallback<T> = (description: string, callback: SuiteCallback) => T;
|
||||
export type BlockchainSuiteCallback = (this: ISuiteCallbackContext, env: BlockchainTestsEnvironment) => void;
|
||||
export type BlockchainContextDefinitionCallback<T> = (description: string, callback: BlockchainSuiteCallback) => T;
|
||||
export interface ContextDefinition extends mocha.IContextDefinition {
|
||||
optional: ContextDefinitionCallback<ISuite | void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* `blockchainTests()` config options.
|
||||
*/
|
||||
export interface BlockchainContextConfig {
|
||||
fork: Partial<{
|
||||
// Accounts to unlock on ganache.
|
||||
unlockedAccounts: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
let TEST_ENV_CONFIG: Partial<BlockchainContextConfig> = {};
|
||||
|
||||
/**
|
||||
* Interface for `blockchainTests()`.
|
||||
*/
|
||||
export interface BlockchainContextDefinition {
|
||||
(description: string, callback: BlockchainSuiteCallback): ISuite;
|
||||
configure: (config?: Partial<BlockchainContextConfig>) => void;
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
resets: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
fork: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
resets: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
live: BlockchainContextDefinitionCallback<ISuite | void> & {
|
||||
only: BlockchainContextDefinitionCallback<ISuite>;
|
||||
skip: BlockchainContextDefinitionCallback<void>;
|
||||
optional: BlockchainContextDefinitionCallback<ISuite | void>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the environment object passed into the `blockchainTests()` callback.
|
||||
*/
|
||||
export interface BlockchainTestsEnvironment {
|
||||
blockchainLifecycle: BlockchainLifecycle;
|
||||
provider: Web3ProviderEngine;
|
||||
txDefaults: Partial<TxData>;
|
||||
web3Wrapper: Web3Wrapper;
|
||||
getChainIdAsync(): Promise<number>;
|
||||
getAccountAddressesAsync(): Promise<string[]>;
|
||||
}
|
||||
|
||||
class BlockchainTestsEnvironmentBase {
|
||||
public blockchainLifecycle!: BlockchainLifecycle;
|
||||
public provider!: Web3ProviderEngine;
|
||||
public txDefaults!: Partial<TxData>;
|
||||
public web3Wrapper!: Web3Wrapper;
|
||||
|
||||
public async getChainIdAsync(): Promise<number> {
|
||||
return providerUtils.getChainIdAsync(this.provider);
|
||||
}
|
||||
|
||||
public async getAccountAddressesAsync(): Promise<string[]> {
|
||||
return this.web3Wrapper.getAvailableAddressesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
interface BlockchainEnvironmentFactory {
|
||||
create(): BlockchainTestsEnvironment;
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses the default ganache provider.
|
||||
*/
|
||||
export class StandardBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: StandardBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): StandardBlockchainTestsEnvironmentSingleton {
|
||||
if (StandardBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
StandardBlockchainTestsEnvironmentSingleton._instance = new StandardBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return StandardBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
// Reset the singleton.
|
||||
public static reset(): void {
|
||||
StandardBlockchainTestsEnvironmentSingleton._instance = undefined;
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): StandardBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return StandardBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
this.provider = provider;
|
||||
this.txDefaults = txDefaults;
|
||||
this.web3Wrapper = web3Wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses a forked ganache provider.
|
||||
*/
|
||||
export class ForkedBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: ForkedBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): ForkedBlockchainTestsEnvironmentSingleton {
|
||||
if (ForkedBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
ForkedBlockchainTestsEnvironmentSingleton._instance = new ForkedBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return ForkedBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
// Reset the singleton.
|
||||
public static reset(): void {
|
||||
ForkedBlockchainTestsEnvironmentSingleton._instance = undefined;
|
||||
}
|
||||
|
||||
protected static _createWeb3Provider(forkHost: string): Web3ProviderEngine {
|
||||
const forkConfig = TEST_ENV_CONFIG.fork || {};
|
||||
const unlockedAccounts = forkConfig.unlockedAccounts;
|
||||
return web3Factory.getRpcProvider({
|
||||
...providerConfigs,
|
||||
fork: forkHost,
|
||||
blockTime: 0,
|
||||
...(unlockedAccounts ? { unlocked_accounts: unlockedAccounts } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): ForkedBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return ForkedBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.txDefaults = txDefaults;
|
||||
this.provider = process.env.FORK_RPC_URL
|
||||
? ForkedBlockchainTestsEnvironmentSingleton._createWeb3Provider(process.env.FORK_RPC_URL)
|
||||
: // Create a dummy provider if no RPC backend supplied.
|
||||
createDummyProvider();
|
||||
this.web3Wrapper = new Web3Wrapper(this.provider);
|
||||
this.blockchainLifecycle = new BlockchainLifecycle(this.web3Wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `BlockchainTestsEnvironment` that uses a live web3 provider.
|
||||
*/
|
||||
export class LiveBlockchainTestsEnvironmentSingleton extends BlockchainTestsEnvironmentBase {
|
||||
private static _instance: LiveBlockchainTestsEnvironmentSingleton | undefined;
|
||||
|
||||
// Create or retrieve the singleton instance of this class.
|
||||
public static create(): LiveBlockchainTestsEnvironmentSingleton {
|
||||
if (LiveBlockchainTestsEnvironmentSingleton._instance === undefined) {
|
||||
LiveBlockchainTestsEnvironmentSingleton._instance = new LiveBlockchainTestsEnvironmentSingleton();
|
||||
}
|
||||
return LiveBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
// Reset the singleton.
|
||||
public static reset(): void {
|
||||
LiveBlockchainTestsEnvironmentSingleton._instance = undefined;
|
||||
}
|
||||
|
||||
protected static _createWeb3Provider(rpcHost: string): Web3ProviderEngine {
|
||||
const providerEngine = new Web3ProviderEngine();
|
||||
providerEngine.addProvider(new RPCSubprovider(rpcHost));
|
||||
providerUtils.startProviderEngine(providerEngine);
|
||||
return providerEngine;
|
||||
}
|
||||
|
||||
// Get the singleton instance of this class.
|
||||
public static getInstance(): LiveBlockchainTestsEnvironmentSingleton | undefined {
|
||||
return LiveBlockchainTestsEnvironmentSingleton._instance;
|
||||
}
|
||||
|
||||
protected constructor() {
|
||||
super();
|
||||
this.txDefaults = txDefaults;
|
||||
this.provider = process.env.LIVE_RPC_URL
|
||||
? LiveBlockchainTestsEnvironmentSingleton._createWeb3Provider(process.env.LIVE_RPC_URL)
|
||||
: // Create a dummy provider if no RPC backend supplied.
|
||||
createDummyProvider();
|
||||
this.web3Wrapper = new Web3Wrapper(this.provider);
|
||||
const snapshotHandlerAsync = async (): Promise<void> => {
|
||||
throw new Error('Snapshots are not supported with a live provider.');
|
||||
};
|
||||
this.blockchainLifecycle = {
|
||||
startAsync: snapshotHandlerAsync,
|
||||
revertAsync: snapshotHandlerAsync,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
|
||||
// The original `describe()` global provided by mocha.
|
||||
const mochaDescribe = (global as any).describe as mocha.IContextDefinition;
|
||||
|
||||
/**
|
||||
* An augmented version of mocha's `describe()`.
|
||||
*/
|
||||
export const describe = _.assign(mochaDescribe, {
|
||||
optional(description: string, callback: SuiteCallback): ISuite | void {
|
||||
const describeCall = process.env.TEST_ALL ? mochaDescribe : mochaDescribe.skip;
|
||||
return describeCall(description, callback);
|
||||
},
|
||||
}) as ContextDefinition;
|
||||
|
||||
/**
|
||||
* Like mocha's `describe()`, but sets up a blockchain environment for you.
|
||||
*/
|
||||
export const blockchainTests: BlockchainContextDefinition = _.assign(
|
||||
function (description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(StandardBlockchainTestsEnvironmentSingleton, description, callback, describe);
|
||||
},
|
||||
{
|
||||
configure(config?: Partial<BlockchainContextConfig>): void {
|
||||
// Update the global config and reset all environment singletons.
|
||||
TEST_ENV_CONFIG = {
|
||||
...TEST_ENV_CONFIG,
|
||||
...config,
|
||||
};
|
||||
ForkedBlockchainTestsEnvironmentSingleton.reset();
|
||||
StandardBlockchainTestsEnvironmentSingleton.reset();
|
||||
LiveBlockchainTestsEnvironmentSingleton.reset();
|
||||
},
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.only,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.TEST_ALL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
fork: _.assign(
|
||||
function (description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe.only : describe.skip,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe.optional : describe.skip,
|
||||
);
|
||||
},
|
||||
resets(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineResetsBlockchainSuite(
|
||||
ForkedBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.FORK_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
live: _.assign(
|
||||
function (description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe : describe.skip,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe.only : describe.skip,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineBlockchainSuite(
|
||||
LiveBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
process.env.LIVE_RPC_URL ? describe.optional : describe.skip,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
resets: _.assign(
|
||||
function (description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe,
|
||||
);
|
||||
},
|
||||
{
|
||||
only(description: string, callback: BlockchainSuiteCallback): ISuite {
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.only,
|
||||
);
|
||||
},
|
||||
skip(description: string, callback: BlockchainSuiteCallback): void {
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.skip,
|
||||
);
|
||||
},
|
||||
optional(description: string, callback: BlockchainSuiteCallback): ISuite | void {
|
||||
return defineResetsBlockchainSuite(
|
||||
StandardBlockchainTestsEnvironmentSingleton,
|
||||
description,
|
||||
callback,
|
||||
describe.optional,
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
) as BlockchainContextDefinition;
|
||||
|
||||
function defineBlockchainSuite<T>(
|
||||
envFactory: BlockchainEnvironmentFactory,
|
||||
description: string,
|
||||
callback: BlockchainSuiteCallback,
|
||||
describeCall: ContextDefinitionCallback<T>,
|
||||
): T {
|
||||
return describeCall(description, function (this: ISuiteCallbackContext): void {
|
||||
callback.call(this, envFactory.create());
|
||||
});
|
||||
}
|
||||
|
||||
function defineResetsBlockchainSuite<T>(
|
||||
envFactory: BlockchainEnvironmentFactory,
|
||||
description: string,
|
||||
callback: BlockchainSuiteCallback,
|
||||
describeCall: ContextDefinitionCallback<T>,
|
||||
): T {
|
||||
return describeCall(description, function (this: ISuiteCallbackContext): void {
|
||||
const env = envFactory.create();
|
||||
beforeEach(async () => env.blockchainLifecycle.startAsync());
|
||||
afterEach(async () => env.blockchainLifecycle.revertAsync());
|
||||
callback.call(this, env);
|
||||
});
|
||||
}
|
||||
|
||||
function createDummyProvider(): Web3ProviderEngine {
|
||||
return {
|
||||
addProvider: _.noop,
|
||||
on: _.noop,
|
||||
send: _.noop,
|
||||
sendAsync: _.noop,
|
||||
start: _.noop,
|
||||
stop: _.noop,
|
||||
};
|
||||
}
|
@@ -1,118 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||
import * as crypto from 'crypto';
|
||||
import { Decimal } from 'decimal.js';
|
||||
|
||||
import { expect } from './chai_setup';
|
||||
import { constants } from './constants';
|
||||
import { Numberish } from './types';
|
||||
|
||||
Decimal.set({ precision: 80 });
|
||||
|
||||
/**
|
||||
* Convert `x` to a `Decimal` type.
|
||||
*/
|
||||
export function toDecimal(x: Numberish): Decimal {
|
||||
if (BigNumber.isBigNumber(x)) {
|
||||
return new Decimal(x.toString(10));
|
||||
}
|
||||
return new Decimal(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random integer between `min` and `max`, inclusive.
|
||||
*/
|
||||
export function getRandomInteger(min: Numberish, max: Numberish): BigNumber {
|
||||
const range = new BigNumber(max).minus(min);
|
||||
return getRandomPortion(range).plus(min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random integer between `0` and `total`, inclusive.
|
||||
*/
|
||||
export function getRandomPortion(total: Numberish): BigNumber {
|
||||
return new BigNumber(total).times(getRandomFloat(0, 1)).integerValue(BigNumber.ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random, high-precision decimal between `min` and `max`, inclusive.
|
||||
*/
|
||||
export function getRandomFloat(min: Numberish, max: Numberish): BigNumber {
|
||||
// Generate a really high precision number between [0, 1]
|
||||
const r = new BigNumber(crypto.randomBytes(32).toString('hex'), 16).dividedBy(new BigNumber(2).pow(256).minus(1));
|
||||
return new BigNumber(max).minus(min).times(r).plus(min);
|
||||
}
|
||||
|
||||
export const FIXED_POINT_BASE = new BigNumber(2).pow(127);
|
||||
|
||||
/**
|
||||
* Convert `n` to fixed-point integer represenatation.
|
||||
*/
|
||||
export function toFixed(n: Numberish): BigNumber {
|
||||
return new BigNumber(n).times(FIXED_POINT_BASE).integerValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert `n` from fixed-point integer represenatation.
|
||||
*/
|
||||
export function fromFixed(n: Numberish): BigNumber {
|
||||
return new BigNumber(n).dividedBy(FIXED_POINT_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts two decimal numbers to integers with `precision` digits, then returns
|
||||
* the absolute difference.
|
||||
*/
|
||||
export function getNumericalDivergence(a: Numberish, b: Numberish, precision = 18): number {
|
||||
const _a = new BigNumber(a);
|
||||
const _b = new BigNumber(b);
|
||||
const maxIntegerDigits = Math.max(
|
||||
_a.integerValue(BigNumber.ROUND_DOWN).sd(true),
|
||||
_b.integerValue(BigNumber.ROUND_DOWN).sd(true),
|
||||
);
|
||||
const _toInteger = (n: BigNumber) => {
|
||||
const base = 10 ** (precision - maxIntegerDigits);
|
||||
return n.times(base).integerValue(BigNumber.ROUND_DOWN);
|
||||
};
|
||||
return _toInteger(_a).minus(_toInteger(_b)).abs().toNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two numbers are equal up to `precision` digits.
|
||||
*/
|
||||
export function assertRoughlyEquals(actual: Numberish, expected: Numberish, precision = 18): void {
|
||||
if (getNumericalDivergence(actual, expected, precision) <= 1) {
|
||||
return;
|
||||
}
|
||||
expect(actual).to.bignumber.eq(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that two numbers are equal with up to `maxError` difference between them.
|
||||
*/
|
||||
export function assertIntegerRoughlyEquals(actual: Numberish, expected: Numberish, maxError = 1, msg?: string): void {
|
||||
const diff = new BigNumber(actual).minus(expected).abs().toNumber();
|
||||
if (diff <= maxError) {
|
||||
return;
|
||||
}
|
||||
expect(actual, msg).to.bignumber.eq(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `amount` into a base unit amount with 18 digits.
|
||||
*/
|
||||
export function toBaseUnitAmount(amount: Numberish, decimals?: number): BigNumber {
|
||||
const baseDecimals = decimals !== undefined ? decimals : 18;
|
||||
const amountAsBigNumber = new BigNumber(amount);
|
||||
const baseUnitAmount = Web3Wrapper.toBaseUnitAmount(amountAsBigNumber, baseDecimals);
|
||||
return baseUnitAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a percentage of `value`, first converting `percentage` to be expressed in 18 digits.
|
||||
*/
|
||||
export function getPercentageOfValue(value: Numberish, percentage: Numberish): BigNumber {
|
||||
const numerator = constants.PERCENTAGE_DENOMINATOR.times(percentage).dividedToIntegerBy(100);
|
||||
const newValue = numerator.times(value).dividedToIntegerBy(constants.PERCENTAGE_DENOMINATOR);
|
||||
return newValue;
|
||||
}
|
@@ -1,41 +0,0 @@
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { Order, SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { getLatestBlockTimestampAsync } from './block_timestamp';
|
||||
import { constants } from './constants';
|
||||
import { orderHashUtils } from './order_hash';
|
||||
import { signingUtils } from './signing_utils';
|
||||
|
||||
export class OrderFactory {
|
||||
private readonly _defaultOrderParams: Partial<Order>;
|
||||
private readonly _privateKey: Buffer;
|
||||
|
||||
constructor(privateKey: Buffer, defaultOrderParams: Partial<Order>) {
|
||||
this._defaultOrderParams = defaultOrderParams;
|
||||
this._privateKey = privateKey;
|
||||
}
|
||||
|
||||
public async newSignedOrderAsync(
|
||||
customOrderParams: Partial<Order> = {},
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): Promise<SignedOrder> {
|
||||
const fifteenMinutesInSeconds = 15 * 60;
|
||||
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
|
||||
const order = {
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(fifteenMinutesInSeconds),
|
||||
salt: generatePseudoRandomSalt(),
|
||||
...this._defaultOrderParams,
|
||||
...customOrderParams,
|
||||
} as Order;
|
||||
const orderHashBuff = orderHashUtils.getOrderHashBuffer(order);
|
||||
const signature = signingUtils.signMessage(orderHashBuff, this._privateKey, signatureType);
|
||||
const signedOrder = {
|
||||
...order,
|
||||
signature: `0x${signature.toString('hex')}`,
|
||||
};
|
||||
return signedOrder;
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
import { assert } from '@0x/assert';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { Order, SignedOrder } from '@0x/types';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string';
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
export const orderHashUtils = {
|
||||
/**
|
||||
* Computes the orderHash for a supplied order.
|
||||
* @param order An object that conforms to the Order or SignedOrder interface definitions.
|
||||
* @return Hex encoded string orderHash from hashing the supplied order.
|
||||
*/
|
||||
getOrderHashHex(order: SignedOrder | Order): string {
|
||||
try {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
|
||||
} catch (error) {
|
||||
if (_.includes(error.message, INVALID_TAKER_FORMAT)) {
|
||||
const errMsg = `Order taker must be of type string. If you want anyone to be able to fill an order - pass ${NULL_ADDRESS}`;
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const orderHashBuff = orderHashUtils.getOrderHashBuffer(order);
|
||||
const orderHashHex = `0x${orderHashBuff.toString('hex')}`;
|
||||
return orderHashHex;
|
||||
},
|
||||
/**
|
||||
* Computes the orderHash for a supplied order
|
||||
* @param order An object that conforms to the Order or SignedOrder interface definitions.
|
||||
* @return A Buffer containing the resulting orderHash from hashing the supplied order
|
||||
*/
|
||||
getOrderHashBuffer(order: SignedOrder | Order): Buffer {
|
||||
try {
|
||||
assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
|
||||
} catch (error) {
|
||||
if (_.includes(error.message, INVALID_TAKER_FORMAT)) {
|
||||
const errMsg = `Order taker must be of type string. If you want anyone to be able to fill an order - pass ${NULL_ADDRESS}`;
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const typedData = eip712Utils.createOrderTypedData(order);
|
||||
const orderHashBuff = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return orderHashBuff;
|
||||
},
|
||||
};
|
@@ -1,62 +0,0 @@
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { Order, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from './constants';
|
||||
import { BatchMatchOrder, CancelOrder, MatchOrder } from './types';
|
||||
|
||||
export const orderUtils = {
|
||||
getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber {
|
||||
const partialAmount = numerator.multipliedBy(target).div(denominator).integerValue(BigNumber.ROUND_FLOOR);
|
||||
return partialAmount;
|
||||
},
|
||||
createFill: (signedOrder: SignedOrder, takerAssetFillAmount?: BigNumber) => {
|
||||
const fill = {
|
||||
order: signedOrder,
|
||||
takerAssetFillAmount: takerAssetFillAmount || signedOrder.takerAssetAmount,
|
||||
signature: signedOrder.signature,
|
||||
};
|
||||
return fill;
|
||||
},
|
||||
createCancel(signedOrder: SignedOrder, takerAssetCancelAmount?: BigNumber): CancelOrder {
|
||||
const cancel = {
|
||||
order: signedOrder,
|
||||
takerAssetCancelAmount: takerAssetCancelAmount || signedOrder.takerAssetAmount,
|
||||
};
|
||||
return cancel;
|
||||
},
|
||||
createOrderWithoutSignature(signedOrder: SignedOrder): Order {
|
||||
const { signature, ...order } = signedOrder;
|
||||
return order;
|
||||
},
|
||||
createBatchMatchOrders(signedOrdersLeft: SignedOrder[], signedOrdersRight: SignedOrder[]): BatchMatchOrder {
|
||||
return {
|
||||
leftOrders: signedOrdersLeft.map(order => orderUtils.createOrderWithoutSignature(order)),
|
||||
rightOrders: signedOrdersRight.map(order => {
|
||||
const right = orderUtils.createOrderWithoutSignature(order);
|
||||
right.makerAssetData = constants.NULL_BYTES;
|
||||
right.takerAssetData = constants.NULL_BYTES;
|
||||
return right;
|
||||
}),
|
||||
leftSignatures: signedOrdersLeft.map(order => order.signature),
|
||||
rightSignatures: signedOrdersRight.map(order => order.signature),
|
||||
};
|
||||
},
|
||||
createMatchOrders(signedOrderLeft: SignedOrder, signedOrderRight: SignedOrder): MatchOrder {
|
||||
const fill = {
|
||||
left: orderUtils.createOrderWithoutSignature(signedOrderLeft),
|
||||
right: orderUtils.createOrderWithoutSignature(signedOrderRight),
|
||||
leftSignature: signedOrderLeft.signature,
|
||||
rightSignature: signedOrderRight.signature,
|
||||
};
|
||||
fill.right.makerAssetData = constants.NULL_BYTES;
|
||||
fill.right.takerAssetData = constants.NULL_BYTES;
|
||||
return fill;
|
||||
},
|
||||
generatePseudoRandomOrderHash(): string {
|
||||
const randomBigNum = generatePseudoRandomSalt();
|
||||
const randomHash = hexUtils.hash(randomBigNum);
|
||||
return randomHash;
|
||||
},
|
||||
};
|
@@ -1,26 +0,0 @@
|
||||
import { devConstants } from '@0x/dev-utils';
|
||||
import { ProfilerSubprovider, SolCompilerArtifactAdapter } from '@0x/sol-profiler';
|
||||
|
||||
let profilerSubprovider: ProfilerSubprovider;
|
||||
|
||||
export const profiler = {
|
||||
start(): void {
|
||||
profiler.getProfilerSubproviderSingleton().start();
|
||||
},
|
||||
stop(): void {
|
||||
profiler.getProfilerSubproviderSingleton().stop();
|
||||
},
|
||||
getProfilerSubproviderSingleton(): ProfilerSubprovider {
|
||||
if (profilerSubprovider === undefined) {
|
||||
profilerSubprovider = profiler._getProfilerSubprovider();
|
||||
}
|
||||
return profilerSubprovider;
|
||||
},
|
||||
_getProfilerSubprovider(): ProfilerSubprovider {
|
||||
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
|
||||
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
|
||||
const isVerbose = true;
|
||||
const subprovider = new ProfilerSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
|
||||
return subprovider;
|
||||
},
|
||||
};
|
@@ -1,20 +0,0 @@
|
||||
import { devConstants } from '@0x/dev-utils';
|
||||
import { RevertTraceSubprovider, SolCompilerArtifactAdapter } from '@0x/sol-trace';
|
||||
|
||||
let revertTraceSubprovider: RevertTraceSubprovider;
|
||||
|
||||
export const revertTrace = {
|
||||
getRevertTraceSubproviderSingleton(): RevertTraceSubprovider {
|
||||
if (revertTraceSubprovider === undefined) {
|
||||
revertTraceSubprovider = revertTrace._getRevertTraceSubprovider();
|
||||
}
|
||||
return revertTraceSubprovider;
|
||||
},
|
||||
_getRevertTraceSubprovider(): RevertTraceSubprovider {
|
||||
const defaultFromAddress = devConstants.TESTRPC_FIRST_ADDRESS;
|
||||
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter();
|
||||
const isVerbose = true;
|
||||
const subprovider = new RevertTraceSubprovider(solCompilerArtifactAdapter, defaultFromAddress, isVerbose);
|
||||
return subprovider;
|
||||
},
|
||||
};
|
@@ -1,29 +0,0 @@
|
||||
import { SignatureType } from '@0x/types';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
export const signingUtils = {
|
||||
signMessage(message: Buffer, privateKey: Buffer, signatureType: SignatureType): Buffer {
|
||||
if (signatureType === SignatureType.EthSign) {
|
||||
const prefixedMessage = ethUtil.hashPersonalMessage(message);
|
||||
const ecSignature = ethUtil.ecsign(prefixedMessage, privateKey);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(signatureType),
|
||||
]);
|
||||
return signature;
|
||||
} else if (signatureType === SignatureType.EIP712) {
|
||||
const ecSignature = ethUtil.ecsign(message, privateKey);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(signatureType),
|
||||
]);
|
||||
return signature;
|
||||
} else {
|
||||
throw new Error(`${signatureType} is not a valid signature type`);
|
||||
}
|
||||
},
|
||||
};
|
@@ -1,150 +0,0 @@
|
||||
import { BigNumber, RevertError } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { expect } from './chai_setup';
|
||||
|
||||
export async function testWithReferenceFuncAsync<P0, R>(
|
||||
referenceFunc: (p0: P0) => Promise<R>,
|
||||
testFunc: (p0: P0) => Promise<R>,
|
||||
values: [P0],
|
||||
): Promise<void>;
|
||||
export async function testWithReferenceFuncAsync<P0, P1, R>(
|
||||
referenceFunc: (p0: P0, p1: P1) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1) => Promise<R>,
|
||||
values: [P0, P1],
|
||||
): Promise<void>;
|
||||
export async function testWithReferenceFuncAsync<P0, P1, P2, R>(
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2) => Promise<R>,
|
||||
values: [P0, P1, P2],
|
||||
): Promise<void>;
|
||||
export async function testWithReferenceFuncAsync<P0, P1, P2, P3, R>(
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2, p3: P3) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2, p3: P3) => Promise<R>,
|
||||
values: [P0, P1, P2, P3],
|
||||
): Promise<void>;
|
||||
export async function testWithReferenceFuncAsync<P0, P1, P2, P3, P4, R>(
|
||||
referenceFunc: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => Promise<R>,
|
||||
testFunc: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) => Promise<R>,
|
||||
values: [P0, P1, P2, P3, P4],
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Tests the behavior of a test function by comparing it to the expected
|
||||
* behavior (defined by a reference function).
|
||||
*
|
||||
* First the reference function will be called to obtain an "expected result",
|
||||
* or if the reference function throws/rejects, an "expected error". Next, the
|
||||
* test function will be called to obtain an "actual result", or if the test
|
||||
* function throws/rejects, an "actual error". The test passes if at least one
|
||||
* of the following conditions is met:
|
||||
*
|
||||
* 1) Neither the reference function or the test function throw and the
|
||||
* "expected result" equals the "actual result".
|
||||
*
|
||||
* 2) Both the reference function and the test function throw and the "actual
|
||||
* error" message *contains* the "expected error" message.
|
||||
*
|
||||
* @param referenceFuncAsync a reference function implemented in pure
|
||||
* JavaScript/TypeScript which accepts N arguments and returns the "expected
|
||||
* result" or throws/rejects with the "expected error".
|
||||
* @param testFuncAsync a test function which, e.g., makes a call or sends a
|
||||
* transaction to a contract. It accepts the same N arguments returns the
|
||||
* "actual result" or throws/rejects with the "actual error".
|
||||
* @param values an array of N values, where each value corresponds in-order to
|
||||
* an argument to both the test function and the reference function.
|
||||
* @return A Promise that resolves if the test passes and rejects if the test
|
||||
* fails, according to the rules described above.
|
||||
*/
|
||||
export async function testWithReferenceFuncAsync(
|
||||
referenceFuncAsync: (...args: any[]) => Promise<any>,
|
||||
testFuncAsync: (...args: any[]) => Promise<any>,
|
||||
values: any[],
|
||||
): Promise<void> {
|
||||
// Measure correct behavior
|
||||
let expected: any;
|
||||
let expectedError: Error | undefined;
|
||||
try {
|
||||
expected = await referenceFuncAsync(...values);
|
||||
} catch (err) {
|
||||
expectedError = err;
|
||||
}
|
||||
// Measure actual behavior
|
||||
let actual: any;
|
||||
let actualError: Error | undefined;
|
||||
try {
|
||||
actual = await testFuncAsync(...values);
|
||||
} catch (err) {
|
||||
actualError = err;
|
||||
}
|
||||
|
||||
const testCaseString = _getTestCaseString(referenceFuncAsync, values);
|
||||
// Compare behavior
|
||||
if (expectedError !== undefined) {
|
||||
// Expecting an error.
|
||||
if (actualError === undefined) {
|
||||
return expect.fail(actualError, expectedError, `${testCaseString}: expected failure but instead succeeded`);
|
||||
} else {
|
||||
if (expectedError instanceof RevertError) {
|
||||
// Expecting a RevertError.
|
||||
if (actualError instanceof RevertError) {
|
||||
if (!actualError.equals(expectedError)) {
|
||||
return expect.fail(
|
||||
actualError,
|
||||
expectedError,
|
||||
`${testCaseString}: expected error ${actualError.toString()} to equal ${expectedError.toString()}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return expect.fail(
|
||||
actualError,
|
||||
expectedError,
|
||||
`${testCaseString}: expected a RevertError but received an Error`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Expecing any Error type.
|
||||
if (actualError.message !== expectedError.message) {
|
||||
return expect.fail(
|
||||
actualError,
|
||||
expectedError,
|
||||
`${testCaseString}: expected error message '${actualError.message}' to equal '${expectedError.message}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not expecting an error.
|
||||
if (actualError !== undefined) {
|
||||
return expect.fail(actualError, expectedError, `${testCaseString}: expected success but instead failed`);
|
||||
}
|
||||
if (expected instanceof BigNumber) {
|
||||
// Technically we can do this with `deep.eq`, but this prints prettier
|
||||
// error messages for BigNumbers.
|
||||
expect(actual).to.bignumber.eq(expected, testCaseString);
|
||||
} else {
|
||||
expect(actual).to.deep.eq(expected, testCaseString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getTestCaseString(referenceFuncAsync: (...args: any[]) => Promise<any>, values: any[]): string {
|
||||
const paramNames = _getParameterNames(referenceFuncAsync);
|
||||
while (paramNames.length < values.length) {
|
||||
paramNames.push(`${paramNames.length}`);
|
||||
}
|
||||
return JSON.stringify(_.zipObject(paramNames, values));
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/questions/1007981/how-to-get-function-parameter-names-values-dynamically
|
||||
function _getParameterNames(func: (...args: any[]) => any): string[] {
|
||||
return _.toString(func)
|
||||
.replace(/[/][/].*$/gm, '') // strip single-line comments
|
||||
.replace(/\s+/g, '') // strip white space
|
||||
.replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments
|
||||
.split(/\){|\)=>/, 1)[0]
|
||||
.replace(/^[^(]*[(]/, '') // extract the parameters
|
||||
.replace(/=[^,]+/g, '') // strip any ES6 defaults
|
||||
.split(',')
|
||||
.filter(Boolean); // split & filter [""]
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
import { generatePseudoRandomSalt } from '@0x/order-utils';
|
||||
import { SignatureType, SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { transactionHashUtils } from '../src';
|
||||
|
||||
import { getLatestBlockTimestampAsync } from './block_timestamp';
|
||||
import { constants } from './constants';
|
||||
import { signingUtils } from './signing_utils';
|
||||
|
||||
export class TransactionFactory {
|
||||
private readonly _signerBuff: Buffer;
|
||||
private readonly _exchangeAddress: string;
|
||||
private readonly _privateKey: Buffer;
|
||||
private readonly _chainId: number;
|
||||
|
||||
constructor(privateKey: Buffer, exchangeAddress: string, chainId: number) {
|
||||
this._privateKey = privateKey;
|
||||
this._exchangeAddress = exchangeAddress;
|
||||
this._chainId = chainId;
|
||||
this._signerBuff = ethUtil.privateToAddress(this._privateKey);
|
||||
}
|
||||
public async newSignedTransactionAsync(
|
||||
customTransactionParams: Partial<ZeroExTransaction>,
|
||||
signatureType: SignatureType = SignatureType.EthSign,
|
||||
): Promise<SignedZeroExTransaction> {
|
||||
if (customTransactionParams.data === undefined) {
|
||||
throw new Error('Error: ZeroExTransaction data field must be supplied');
|
||||
}
|
||||
const tenMinutesInSeconds = 10 * 60;
|
||||
const currentBlockTimestamp = await getLatestBlockTimestampAsync();
|
||||
const salt = generatePseudoRandomSalt();
|
||||
const signerAddress = `0x${this._signerBuff.toString('hex')}`;
|
||||
const transaction = {
|
||||
salt,
|
||||
signerAddress,
|
||||
data: customTransactionParams.data,
|
||||
expirationTimeSeconds: new BigNumber(currentBlockTimestamp).plus(tenMinutesInSeconds),
|
||||
gasPrice: new BigNumber(constants.DEFAULT_GAS_PRICE),
|
||||
domain: {
|
||||
verifyingContract: this._exchangeAddress,
|
||||
chainId: this._chainId,
|
||||
},
|
||||
...customTransactionParams,
|
||||
};
|
||||
|
||||
const transactionHashBuffer = transactionHashUtils.getTransactionHashBuffer(transaction);
|
||||
const signature = signingUtils.signMessage(transactionHashBuffer, this._privateKey, signatureType);
|
||||
const signedTransaction = {
|
||||
...transaction,
|
||||
signature: `0x${signature.toString('hex')}`,
|
||||
};
|
||||
return signedTransaction;
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
import { assert } from '@0x/assert';
|
||||
import { schemas } from '@0x/json-schemas';
|
||||
import { eip712Utils } from '@0x/order-utils';
|
||||
import { SignedZeroExTransaction, ZeroExTransaction } from '@0x/types';
|
||||
import { signTypedDataUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export const transactionHashUtils = {
|
||||
/**
|
||||
* Computes the transactionHash for a supplied 0x transaction.
|
||||
* @param transaction An object that conforms to the ZeroExTransaction or SignedZeroExTransaction interface definitions.
|
||||
* @return Hex encoded string transactionHash from hashing the supplied order.
|
||||
*/
|
||||
getTransactionHashHex(transaction: ZeroExTransaction | SignedZeroExTransaction): string {
|
||||
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||
const transactionHashBuff = transactionHashUtils.getTransactionHashBuffer(transaction);
|
||||
const transactionHashHex = `0x${transactionHashBuff.toString('hex')}`;
|
||||
return transactionHashHex;
|
||||
},
|
||||
/**
|
||||
* Computes the transactionHash for a supplied 0x transaction.
|
||||
* @param transaction An object that conforms to the ZeroExTransaction or SignedZeroExTransaction interface definitions.
|
||||
* @return A Buffer containing the resulting transactionHash from hashing the supplied 0x transaction.
|
||||
*/
|
||||
getTransactionHashBuffer(transaction: ZeroExTransaction | SignedZeroExTransaction): Buffer {
|
||||
const typedData = eip712Utils.createZeroExTransactionTypedData(transaction);
|
||||
const transactionHashBuff = signTypedDataUtils.generateTypedDataHash(typedData);
|
||||
return transactionHashBuff;
|
||||
},
|
||||
};
|
@@ -1,20 +0,0 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import BN = require('bn.js');
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
|
||||
import { constants } from './constants';
|
||||
|
||||
export const typeEncodingUtils = {
|
||||
encodeUint256(value: BigNumber): Buffer {
|
||||
const base = 10;
|
||||
const formattedValue = new BN(value.toString(base));
|
||||
const encodedValue = ethUtil.toBuffer(formattedValue);
|
||||
const paddedValue = ethUtil.setLengthLeft(encodedValue, constants.WORD_LENGTH);
|
||||
return paddedValue;
|
||||
},
|
||||
decodeUint256(encodedValue: Buffer): BigNumber {
|
||||
const formattedValue = ethUtil.bufferToHex(encodedValue);
|
||||
const value = new BigNumber(formattedValue, constants.BASE_16);
|
||||
return value;
|
||||
},
|
||||
};
|
@@ -1,162 +0,0 @@
|
||||
import { Order } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { AbiDefinition } from 'ethereum-types';
|
||||
|
||||
export { OrderStatus } from '@0x/types';
|
||||
|
||||
export interface ERC20BalancesByOwner {
|
||||
[ownerAddress: string]: {
|
||||
[tokenAddress: string]: BigNumber;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EthBalancesByOwner {
|
||||
[owner: string]: BigNumber;
|
||||
}
|
||||
|
||||
export interface SubmissionContractEventArgs {
|
||||
transactionId: BigNumber;
|
||||
}
|
||||
|
||||
export interface BatchFillOrders {
|
||||
orders: Order[];
|
||||
signatures: string[];
|
||||
takerAssetFillAmounts: BigNumber[];
|
||||
}
|
||||
|
||||
export interface MarketSellOrders {
|
||||
orders: Order[];
|
||||
signatures: string[];
|
||||
takerAssetFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface MarketBuyOrders {
|
||||
orders: Order[];
|
||||
signatures: string[];
|
||||
makerAssetFillAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface BatchCancelOrders {
|
||||
orders: Order[];
|
||||
}
|
||||
|
||||
export interface CancelOrdersBefore {
|
||||
salt: BigNumber;
|
||||
}
|
||||
|
||||
export interface TransactionDataParams {
|
||||
name: string;
|
||||
abi: AbiDefinition[];
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export interface MultiSigConfig {
|
||||
owners: string[];
|
||||
confirmationsRequired: number;
|
||||
secondsRequired: number;
|
||||
}
|
||||
|
||||
export interface MultiSigConfigByNetwork {
|
||||
[networkName: string]: MultiSigConfig;
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
address?: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
decimals: number;
|
||||
ipfsHash: string;
|
||||
swarmHash: string;
|
||||
}
|
||||
|
||||
export enum ContractName {
|
||||
TokenRegistry = 'TokenRegistry',
|
||||
MultiSigWalletWithTimeLock = 'MultiSigWalletWithTimeLock',
|
||||
Exchange = 'Exchange',
|
||||
DummyERC20Token = 'DummyERC20Token',
|
||||
EtherToken = 'WETH9',
|
||||
DutchAuction = 'DutchAuction',
|
||||
ZeroExGovernor = 'ZeroExGovernor',
|
||||
AccountLevels = 'AccountLevels',
|
||||
EtherDelta = 'EtherDelta',
|
||||
Arbitrage = 'Arbitrage',
|
||||
TestAssetDataDecoders = 'TestAssetDataDecoders',
|
||||
TestAssetProxyDispatcher = 'TestAssetProxyDispatcher',
|
||||
TestLibs = 'TestLibs',
|
||||
TestSignatureValidator = 'TestSignatureValidator',
|
||||
ERC20Proxy = 'ERC20Proxy',
|
||||
ERC721Proxy = 'ERC721Proxy',
|
||||
DummyERC721Receiver = 'DummyERC721Receiver',
|
||||
DummyERC721Token = 'DummyERC721Token',
|
||||
TestLibBytes = 'TestLibBytes',
|
||||
TestWallet = 'TestWallet',
|
||||
Whitelist = 'Whitelist',
|
||||
Forwarder = 'Forwarder',
|
||||
BalanceThresholdFilter = 'BalanceThresholdFilter',
|
||||
}
|
||||
|
||||
export interface CancelOrder {
|
||||
order: Order;
|
||||
takerAssetCancelAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface BatchMatchOrder {
|
||||
leftOrders: Order[];
|
||||
rightOrders: Order[];
|
||||
leftSignatures: string[];
|
||||
rightSignatures: string[];
|
||||
}
|
||||
|
||||
export interface MatchOrder {
|
||||
left: Order;
|
||||
right: Order;
|
||||
leftSignature: string;
|
||||
rightSignature: string;
|
||||
}
|
||||
|
||||
export interface TokenBalances {
|
||||
erc20: ERC20BalancesByOwner;
|
||||
eth: EthBalancesByOwner;
|
||||
}
|
||||
|
||||
export interface FillEventArgs {
|
||||
orderHash: string;
|
||||
makerAddress: string;
|
||||
takerAddress: string;
|
||||
makerAssetFilledAmount: BigNumber;
|
||||
takerAssetFilledAmount: BigNumber;
|
||||
makerFeePaid: BigNumber;
|
||||
takerFeePaid: BigNumber;
|
||||
}
|
||||
|
||||
export type Numberish = BigNumber | string | number;
|
||||
|
||||
export enum ExchangeFunctionName {
|
||||
BatchCancelOrders = 'batchCancelOrders',
|
||||
BatchExecuteTransactions = 'batchExecuteTransactions',
|
||||
BatchFillOrKillOrders = 'batchFillOrKillOrders',
|
||||
BatchFillOrders = 'batchFillOrders',
|
||||
BatchFillOrdersNoThrow = 'batchFillOrdersNoThrow',
|
||||
BatchMatchOrders = 'batchMatchOrders',
|
||||
BatchMatchOrdersWithMaximalFill = 'batchMatchOrdersWithMaximalFill',
|
||||
CancelOrder = 'cancelOrder',
|
||||
CancelOrdersUpTo = 'cancelOrdersUpTo',
|
||||
ExecuteTransaction = 'executeTransaction',
|
||||
FillOrKillOrder = 'fillOrKillOrder',
|
||||
FillOrder = 'fillOrder',
|
||||
FillOrderNoThrow = 'fillOrderNoThrow',
|
||||
MarketBuyOrdersNoThrow = 'marketBuyOrdersNoThrow',
|
||||
MarketSellOrdersNoThrow = 'marketSellOrdersNoThrow',
|
||||
MarketBuyOrdersFillOrKill = 'marketBuyOrdersFillOrKill',
|
||||
MarketSellOrdersFillOrKill = 'marketSellOrdersFillOrKill',
|
||||
MatchOrders = 'matchOrders',
|
||||
MatchOrdersWithMaximalFill = 'matchOrdersWithMaximalFill',
|
||||
PreSign = 'preSign',
|
||||
RegisterAssetProxy = 'registerAssetProxy',
|
||||
SetSignatureValidatorApproval = 'setSignatureValidatorApproval',
|
||||
SimulateDispatchTransferFromCalls = 'simulateDispatchTransferFromCalls',
|
||||
TransferOwnership = 'transferOwnership',
|
||||
SetProtocolFeeMultiplier = 'setProtocolFeeMultiplier',
|
||||
SetProtocolFeeCollectorAddress = 'setProtocolFeeCollectorAddress',
|
||||
DetachProtocolFeeCollector = 'detachProtocolFeeCollector',
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
import { devConstants, env, EnvVars, Web3Config, web3Factory } from '@0x/dev-utils';
|
||||
import { prependSubprovider, Web3ProviderEngine } from '@0x/subproviders';
|
||||
import { logUtils } from '@0x/utils';
|
||||
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,
|
||||
gas: devConstants.GAS_LIMIT,
|
||||
gasPrice: constants.DEFAULT_GAS_PRICE,
|
||||
};
|
||||
|
||||
export const providerConfigs: Web3Config = {
|
||||
total_accounts: constants.NUM_TEST_ACCOUNTS,
|
||||
shouldUseInProcessGanache: true,
|
||||
shouldAllowUnlimitedContractSize: true,
|
||||
hardfork: 'istanbul',
|
||||
gasLimit: 100e6,
|
||||
unlocked_accounts: [
|
||||
'0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
'0x55dc8f21d20d4c6ed3c82916a438a413ca68e335',
|
||||
'0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0', // ERC20BridgeProxy
|
||||
'0xf977814e90da44bfa03b6295a0616a897441acec', // Binance: USDC, TUSD
|
||||
],
|
||||
};
|
||||
|
||||
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;
|
||||
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,102 +0,0 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as process from 'process';
|
||||
|
||||
import { expect } from '../src/chai_setup';
|
||||
import { constants } from '../src/constants';
|
||||
import { blockchainTests, describe } from '../src/mocha_blockchain';
|
||||
import { append } from './subtests/mocha_blockchain_1';
|
||||
|
||||
blockchainTests('mocha blockchain extensions', env => {
|
||||
describe('blockchainTests()', () => {
|
||||
it('passes a valid environment object', () => {
|
||||
expect(env.blockchainLifecycle).to.exist('');
|
||||
expect(env.provider).to.exist('');
|
||||
expect(env.txDefaults).to.exist('');
|
||||
expect(env.web3Wrapper).to.exist('');
|
||||
expect(typeof env.getChainIdAsync).to.eq('function');
|
||||
expect(typeof env.getAccountAddressesAsync).to.eq('function');
|
||||
});
|
||||
|
||||
it('initializes the test environment', async () => {
|
||||
expect(await env.getChainIdAsync()).to.eq(constants.TESTRPC_CHAIN_ID);
|
||||
expect(await env.getAccountAddressesAsync()).to.be.not.empty('');
|
||||
});
|
||||
|
||||
describe('modifiers', () => {
|
||||
blockchainTests.skip('skip', () => {
|
||||
it('does not execute this test', () => {
|
||||
expect.fail();
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests('only', () => {
|
||||
it.skip("can't test `only` :-(", () => {
|
||||
// no-op.
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.optional('optional', () => {
|
||||
it('only runs this with `TEST_ALL` environment flag set', () => {
|
||||
expect(process.env.TEST_ALL).to.be.ok('');
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests.resets('resets', () => {
|
||||
const originalBlockhainLifecycle = env.blockchainLifecycle;
|
||||
const blockchainLifecycleCalls = [] as string[];
|
||||
|
||||
before(() => {
|
||||
// Replace `blockchainLifecycle` with a hooked version.
|
||||
env.blockchainLifecycle = createHookedObject(
|
||||
originalBlockhainLifecycle,
|
||||
methodName => blockchainLifecycleCalls.push(methodName),
|
||||
['startAsync', 'revertAsync'],
|
||||
);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
// Undo the hook.
|
||||
env.blockchainLifecycle = originalBlockhainLifecycle;
|
||||
});
|
||||
|
||||
it('calls `blockchainLifecycle.startAsync()` before this test', () => {
|
||||
expect(blockchainLifecycleCalls).to.eql(['startAsync']);
|
||||
});
|
||||
|
||||
it('calls `blockchainLifecycle.revertAsync()` after the last test', () => {
|
||||
expect(blockchainLifecycleCalls).to.eql(['startAsync', 'revertAsync', 'startAsync']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
blockchainTests('nested tests', nestedEnv => {
|
||||
it('shares the same environment object', () => {
|
||||
expect(nestedEnv).to.eq(env);
|
||||
});
|
||||
});
|
||||
|
||||
describe('subtests', () => {
|
||||
append(env);
|
||||
});
|
||||
});
|
||||
|
||||
describe('describe extensions', () => {
|
||||
describe('modifiers', () => {
|
||||
describe.optional('optional', () => {
|
||||
it('only runs this with `TEST_ALL` environment flag set', () => {
|
||||
expect(process.env.TEST_ALL).to.be.ok('');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createHookedObject(obj: any, handler: (name: string) => void, methods: string[]): any {
|
||||
const hookedMethods = _.map(methods, methodName => {
|
||||
return function (this: any, ...args: any[]): any {
|
||||
handler(methodName);
|
||||
return obj[methodName].call(this, ...args);
|
||||
};
|
||||
});
|
||||
return _.assign(_.clone(obj), _.zipObject(methods, hookedMethods));
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
import { chaiSetup } from '@0x/dev-utils';
|
||||
import { Order } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { orderHashUtils } from '../src';
|
||||
|
||||
import { constants } from '../src/constants';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('Order hashing', () => {
|
||||
describe('#getOrderHashHex', () => {
|
||||
const expectedOrderHash = '0x331cb7e07a757bae130702da6646c26531798c92bcfaf671817268fd2c188531';
|
||||
const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48';
|
||||
const fakeChainID = 50;
|
||||
const order: Order = {
|
||||
makerAddress: constants.NULL_ADDRESS,
|
||||
takerAddress: constants.NULL_ADDRESS,
|
||||
senderAddress: constants.NULL_ADDRESS,
|
||||
feeRecipientAddress: constants.NULL_ADDRESS,
|
||||
makerAssetData: constants.NULL_ADDRESS,
|
||||
takerAssetData: constants.NULL_ADDRESS,
|
||||
makerFeeAssetData: constants.NULL_ADDRESS,
|
||||
takerFeeAssetData: constants.NULL_ADDRESS,
|
||||
salt: new BigNumber(0),
|
||||
makerFee: new BigNumber(0),
|
||||
takerFee: new BigNumber(0),
|
||||
makerAssetAmount: new BigNumber(0),
|
||||
takerAssetAmount: new BigNumber(0),
|
||||
expirationTimeSeconds: new BigNumber(0),
|
||||
exchangeAddress: fakeExchangeContractAddress,
|
||||
chainId: fakeChainID,
|
||||
};
|
||||
it('calculates the order hash', async () => {
|
||||
const orderHash = orderHashUtils.getOrderHashHex(order);
|
||||
expect(orderHash).to.be.equal(expectedOrderHash);
|
||||
});
|
||||
it('calculates the order hash if amounts are strings', async () => {
|
||||
// It's common for developers using javascript to provide the amounts
|
||||
// as strings. Since we eventually toString() the BigNumber
|
||||
// before encoding we should result in the same orderHash in this scenario
|
||||
const orderHash = orderHashUtils.getOrderHashHex({
|
||||
...order,
|
||||
makerAssetAmount: '0',
|
||||
takerAssetAmount: '0',
|
||||
makerFee: '0',
|
||||
takerFee: '0',
|
||||
} as any);
|
||||
expect(orderHash).to.be.equal(expectedOrderHash);
|
||||
});
|
||||
it('throws a readable error message if taker format is invalid', async () => {
|
||||
const orderWithInvalidtakerFormat = {
|
||||
...order,
|
||||
takerAddress: null as any as string,
|
||||
};
|
||||
const expectedErrorMessage = `Expected order to conform to schema`;
|
||||
expect(() => orderHashUtils.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,10 +0,0 @@
|
||||
import { expect } from '../../src/chai_setup';
|
||||
import { blockchainTests, BlockchainTestsEnvironment } from '../../src/mocha_blockchain';
|
||||
|
||||
export function append(env: BlockchainTestsEnvironment): void {
|
||||
blockchainTests('imported subtests', subtestsEnv => {
|
||||
it('shares the same environment object', () => {
|
||||
expect(subtestsEnv).to.eq(env);
|
||||
});
|
||||
});
|
||||
}
|
@@ -1,89 +0,0 @@
|
||||
import { AnyRevertError, StringRevertError } from '@0x/utils';
|
||||
|
||||
import { expect } from '../src/chai_setup';
|
||||
|
||||
import { testWithReferenceFuncAsync } from '../src/test_with_reference';
|
||||
|
||||
async function divAsync(x: number, y: number): Promise<number> {
|
||||
if (y === 0) {
|
||||
throw new Error('MathError: divide by zero');
|
||||
}
|
||||
return x / y;
|
||||
}
|
||||
|
||||
// returns an async function that always returns the given value.
|
||||
function alwaysValueFunc(value: number): (x: number, y: number) => Promise<number> {
|
||||
return async (x: number, y: number) => value;
|
||||
}
|
||||
|
||||
// returns an async function which always throws/rejects with the given error.
|
||||
function alwaysFailFunc(error: Error): (x: number, y: number) => Promise<number> {
|
||||
return async (x: number, y: number) => {
|
||||
throw error;
|
||||
};
|
||||
}
|
||||
|
||||
describe('testWithReferenceFuncAsync', () => {
|
||||
it('passes when both succeed and actual == expected', async () => {
|
||||
return testWithReferenceFuncAsync(alwaysValueFunc(0.5), divAsync, [1, 2]);
|
||||
});
|
||||
|
||||
it('fails when both succeed and actual != expected', async () => {
|
||||
return expect(testWithReferenceFuncAsync(alwaysValueFunc(3), divAsync, [1, 2])).to.be.rejectedWith(
|
||||
'{"x":1,"y":2}: expected 0.5 to deeply equal 3',
|
||||
);
|
||||
});
|
||||
|
||||
it('passes when both fail and error messages are the same', async () => {
|
||||
const err = new Error('whoopsie');
|
||||
return testWithReferenceFuncAsync(alwaysFailFunc(err), alwaysFailFunc(err), [1, 1]);
|
||||
});
|
||||
|
||||
it('fails when both fail and error messages are not identical', async () => {
|
||||
const errorMessage = 'whoopsie';
|
||||
const notErrorMessage = 'not whoopsie';
|
||||
const error = new Error(errorMessage);
|
||||
const notError = new Error(notErrorMessage);
|
||||
return expect(
|
||||
testWithReferenceFuncAsync(alwaysFailFunc(notError), alwaysFailFunc(error), [1, 2]),
|
||||
).to.be.rejectedWith(`{"x":1,"y":2}: expected error message '${errorMessage}' to equal '${notErrorMessage}'`);
|
||||
});
|
||||
|
||||
it('passes when both fail with compatible RevertErrors', async () => {
|
||||
const error1 = new StringRevertError('whoopsie');
|
||||
const error2 = new AnyRevertError();
|
||||
return testWithReferenceFuncAsync(alwaysFailFunc(error1), alwaysFailFunc(error2), [1, 1]);
|
||||
});
|
||||
|
||||
it('fails when both fail with incompatible RevertErrors', async () => {
|
||||
const error1 = new StringRevertError('whoopsie');
|
||||
const error2 = new StringRevertError('not whoopsie');
|
||||
return expect(
|
||||
testWithReferenceFuncAsync(alwaysFailFunc(error1), alwaysFailFunc(error2), [1, 1]),
|
||||
).to.be.rejectedWith(
|
||||
`{"x":1,"y":1}: expected error StringRevertError({ message: 'not whoopsie' }) to equal StringRevertError({ message: 'whoopsie' })`,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when reference function fails with a RevertError but test function fails with a regular Error', async () => {
|
||||
const error1 = new StringRevertError('whoopsie');
|
||||
const error2 = new Error('whoopsie');
|
||||
return expect(
|
||||
testWithReferenceFuncAsync(alwaysFailFunc(error1), alwaysFailFunc(error2), [1, 1]),
|
||||
).to.be.rejectedWith(`{"x":1,"y":1}: expected a RevertError but received an Error`);
|
||||
});
|
||||
|
||||
it('fails when referenceFunc succeeds and testFunc fails', async () => {
|
||||
const error = new Error('whoopsie');
|
||||
return expect(testWithReferenceFuncAsync(alwaysValueFunc(0), alwaysFailFunc(error), [1, 2])).to.be.rejectedWith(
|
||||
`{"x":1,"y":2}: expected success but instead failed`,
|
||||
);
|
||||
});
|
||||
|
||||
it('fails when referenceFunc fails and testFunc succeeds', async () => {
|
||||
const error = new Error('whoopsie');
|
||||
return expect(testWithReferenceFuncAsync(alwaysFailFunc(error), divAsync, [1, 2])).to.be.rejectedWith(
|
||||
'{"x":1,"y":2}: expected failure but instead succeeded',
|
||||
);
|
||||
});
|
||||
});
|
@@ -1,47 +0,0 @@
|
||||
import { chaiSetup } from '@0x/dev-utils';
|
||||
import { ZeroExTransaction } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { transactionHashUtils } from '../src';
|
||||
|
||||
import { constants } from '../src/constants';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
describe('0x transaction hashing', () => {
|
||||
describe('#getTransactionHashHex', () => {
|
||||
const expectedTransactionHash = '0x7845d260300acfbebaff52f0462f984016473290b9eb865fb6ffac0503cab364';
|
||||
const fakeVerifyingContractAddress = '0x5e72914535f202659083db3a02c984188fa26e9f';
|
||||
const fakeChainId = 1337;
|
||||
const transaction: ZeroExTransaction = {
|
||||
signerAddress: constants.NULL_ADDRESS,
|
||||
salt: new BigNumber(0),
|
||||
expirationTimeSeconds: new BigNumber(0),
|
||||
gasPrice: new BigNumber(0),
|
||||
data: constants.NULL_BYTES,
|
||||
domain: {
|
||||
verifyingContract: fakeVerifyingContractAddress,
|
||||
chainId: fakeChainId,
|
||||
},
|
||||
};
|
||||
it('calculates the transaction hash', async () => {
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
|
||||
expect(transactionHash).to.be.equal(expectedTransactionHash);
|
||||
});
|
||||
it('calculates the transaction hash if amounts are strings', async () => {
|
||||
// It's common for developers using javascript to provide the amounts
|
||||
// as strings. Since we eventually toString() the BigNumber
|
||||
// before encoding we should result in the same orderHash in this scenario
|
||||
const transactionHash = transactionHashUtils.getTransactionHashHex({
|
||||
...transaction,
|
||||
salt: '0',
|
||||
expirationTimeSeconds: '0',
|
||||
gasPrice: '0',
|
||||
} as any);
|
||||
expect(transactionHash).to.be.equal(expectedTransactionHash);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
"overrides": [],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"ignorePatterns": [
|
||||
"lib/**/*",
|
||||
"contracts/**/*",
|
||||
"generated-wrappers/**/*",
|
||||
"generated-artifacts/**/*",
|
||||
"test/generated-wrappers/**/*",
|
||||
"test/generated-artifacts/**/*"
|
||||
|
||||
],
|
||||
"rules": {}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
# Blacklist all files
|
||||
.*
|
||||
*
|
||||
# Whitelist lib
|
||||
!lib/**/*
|
||||
# Whitelist Solidity contracts
|
||||
!contracts/src/**/*
|
||||
# Blacklist tests in lib
|
||||
/lib/test/*
|
||||
# Package specific ignore
|
@@ -1,575 +0,0 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1675210931,
|
||||
"version": "1.4.40",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1675105183,
|
||||
"version": "1.4.39",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1674517560,
|
||||
"version": "1.4.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1670879498,
|
||||
"version": "1.4.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1669235113,
|
||||
"version": "1.4.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1668477029,
|
||||
"version": "1.4.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1667607537,
|
||||
"version": "1.4.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1667427402,
|
||||
"version": "1.4.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1666645023,
|
||||
"version": "1.4.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1666381417,
|
||||
"version": "1.4.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1665670315,
|
||||
"version": "1.4.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1665531940,
|
||||
"version": "1.4.29",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.28",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Migrate from TSLint to ESLint and fix linting errors",
|
||||
"pr": 589
|
||||
}
|
||||
],
|
||||
"timestamp": 1665013355
|
||||
},
|
||||
{
|
||||
"timestamp": 1663786955,
|
||||
"version": "1.4.27",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1662998180,
|
||||
"version": "1.4.26",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1662559804,
|
||||
"version": "1.4.25",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1662147076,
|
||||
"version": "1.4.24",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1662046042,
|
||||
"version": "1.4.23",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1661462289,
|
||||
"version": "1.4.22",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1661459661,
|
||||
"version": "1.4.21",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1661145612,
|
||||
"version": "1.4.20",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1660093941,
|
||||
"version": "1.4.19",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1660073235,
|
||||
"version": "1.4.18",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1659750766,
|
||||
"version": "1.4.17",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1658950329,
|
||||
"version": "1.4.16",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1655244958,
|
||||
"version": "1.4.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1654284040,
|
||||
"version": "1.4.14",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1652919697,
|
||||
"version": "1.4.13",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1650611093,
|
||||
"version": "1.4.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "1.4.11",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "1.4.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "1.4.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1640364306,
|
||||
"version": "1.4.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1638390144,
|
||||
"version": "1.4.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1637102971,
|
||||
"version": "1.4.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1635903615,
|
||||
"version": "1.4.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1634668033,
|
||||
"version": "1.4.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1634147078,
|
||||
"version": "1.4.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1633374058,
|
||||
"version": "1.4.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1632957537,
|
||||
"version": "1.4.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Support cast vote by signature in Treasury"
|
||||
}
|
||||
],
|
||||
"timestamp": 1631710679
|
||||
},
|
||||
{
|
||||
"timestamp": 1631120757,
|
||||
"version": "1.3.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1630459879,
|
||||
"version": "1.3.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1629353596,
|
||||
"version": "1.3.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "1.3.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "1.3.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added proposal 1 params and test",
|
||||
"pr": 298
|
||||
}
|
||||
],
|
||||
"timestamp": 1628225642
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "1.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "1.2.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "1.2.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added proposal 0 params and test",
|
||||
"pr": 252
|
||||
}
|
||||
],
|
||||
"timestamp": 1622154125
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "1.1.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "1.1.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "1.1.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Patched votingPower logic",
|
||||
"pr": 214
|
||||
}
|
||||
],
|
||||
"timestamp": 1619825976
|
||||
},
|
||||
{
|
||||
"timestamp": 1619596077,
|
||||
"version": "1.1.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1619481586,
|
||||
"version": "1.1.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1618259868,
|
||||
"version": "1.1.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1617311315,
|
||||
"version": "1.1.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Make the proposal/quorum thresholds updatable",
|
||||
"pr": 165
|
||||
}
|
||||
],
|
||||
"timestamp": 1616005394
|
||||
},
|
||||
{
|
||||
"timestamp": 1614141718,
|
||||
"version": "1.0.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1612950500,
|
||||
"version": "1.0.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Create this package",
|
||||
"pr": 120
|
||||
}
|
||||
],
|
||||
"timestamp": 1611869778
|
||||
}
|
||||
]
|
@@ -1,258 +0,0 @@
|
||||
<!--
|
||||
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
|
||||
Edit the package's CHANGELOG.json file only.
|
||||
-->
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.40 - _February 1, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.39 - _January 30, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.38 - _January 23, 2023_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.37 - _December 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.36 - _November 23, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.35 - _November 15, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.34 - _November 5, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.33 - _November 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.32 - _October 24, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.31 - _October 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.30 - _October 13, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.29 - _October 11, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.28 - _October 5, 2022_
|
||||
|
||||
* Migrate from TSLint to ESLint and fix linting errors (#589)
|
||||
|
||||
## v1.4.27 - _September 21, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.26 - _September 12, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.25 - _September 7, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.24 - _September 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.23 - _September 1, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.22 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.21 - _August 25, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.20 - _August 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.19 - _August 10, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.18 - _August 9, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.17 - _August 6, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.16 - _July 27, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.15 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.14 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.13 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.12 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.11 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.10 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.9 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.8 - _December 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.7 - _December 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.6 - _November 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.5 - _November 3, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.4 - _October 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.3 - _October 13, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.2 - _October 4, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.1 - _September 29, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.0 - _September 15, 2021_
|
||||
|
||||
* Support cast vote by signature in Treasury
|
||||
|
||||
## v1.3.5 - _September 8, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.4 - _September 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.3 - _August 19, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.2 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.1 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.0 - _August 6, 2021_
|
||||
|
||||
* Added proposal 1 params and test (#298)
|
||||
|
||||
## v1.2.3 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.2 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.1 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.0 - _May 27, 2021_
|
||||
|
||||
* Added proposal 0 params and test (#252)
|
||||
|
||||
## v1.1.8 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.7 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.6 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.5 - _April 30, 2021_
|
||||
|
||||
* Patched votingPower logic (#214)
|
||||
|
||||
## v1.1.4 - _April 28, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.3 - _April 26, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.2 - _April 12, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.1 - _April 1, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.0 - _March 17, 2021_
|
||||
|
||||
* Make the proposal/quorum thresholds updatable (#165)
|
||||
|
||||
## v1.0.2 - _February 24, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.1 - _February 10, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.0.0 - _January 28, 2021_
|
||||
|
||||
* Create this package (#120)
|
@@ -1 +0,0 @@
|
||||
[]
|
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"artifactsDir": "./test/generated-artifacts",
|
||||
"contractsDir": "./contracts",
|
||||
"useDockerisedSolc": false,
|
||||
"isOfflineMode": false,
|
||||
"shouldSaveStandardInput": true,
|
||||
"shouldCompileIndependently": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 1000000,
|
||||
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"devdoc",
|
||||
"evm.bytecode.object",
|
||||
"evm.bytecode.sourceMap",
|
||||
"evm.deployedBytecode.object",
|
||||
"evm.deployedBytecode.sourceMap",
|
||||
"evm.methodIdentifiers"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,66 +0,0 @@
|
||||
pragma solidity ^0.6.12;
|
||||
|
||||
/**
|
||||
* @title ISablier
|
||||
* @author Sablier
|
||||
*/
|
||||
interface ISablier {
|
||||
/**
|
||||
* @notice Emits when a stream is successfully created.
|
||||
*/
|
||||
event CreateStream(
|
||||
uint256 indexed streamId,
|
||||
address indexed sender,
|
||||
address indexed recipient,
|
||||
uint256 deposit,
|
||||
address tokenAddress,
|
||||
uint256 startTime,
|
||||
uint256 stopTime
|
||||
);
|
||||
|
||||
/**
|
||||
* @notice Emits when the recipient of a stream withdraws a portion or all their pro rata share of the stream.
|
||||
*/
|
||||
event WithdrawFromStream(uint256 indexed streamId, address indexed recipient, uint256 amount);
|
||||
|
||||
/**
|
||||
* @notice Emits when a stream is successfully cancelled and tokens are transferred back on a pro rata basis.
|
||||
*/
|
||||
event CancelStream(
|
||||
uint256 indexed streamId,
|
||||
address indexed sender,
|
||||
address indexed recipient,
|
||||
uint256 senderBalance,
|
||||
uint256 recipientBalance
|
||||
);
|
||||
|
||||
function balanceOf(uint256 streamId, address who) external view returns (uint256 balance);
|
||||
|
||||
function getStream(
|
||||
uint256 streamId
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 deposit,
|
||||
address token,
|
||||
uint256 startTime,
|
||||
uint256 stopTime,
|
||||
uint256 remainingBalance,
|
||||
uint256 ratePerSecond
|
||||
);
|
||||
|
||||
function createStream(
|
||||
address recipient,
|
||||
uint256 deposit,
|
||||
address tokenAddress,
|
||||
uint256 startTime,
|
||||
uint256 stopTime
|
||||
) external returns (uint256 streamId);
|
||||
|
||||
function withdrawFromStream(uint256 streamId, uint256 funds) external returns (bool);
|
||||
|
||||
function cancelStream(uint256 streamId) external returns (bool);
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
// 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.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/src/IERC20Token.sol";
|
||||
import "./IStaking.sol";
|
||||
|
||||
contract DefaultPoolOperator {
|
||||
// Immutables
|
||||
IStaking public immutable stakingProxy;
|
||||
IERC20Token public immutable weth;
|
||||
bytes32 public immutable poolId;
|
||||
|
||||
/// @dev Initializes this contract and creates a staking pool.
|
||||
/// @param stakingProxy_ The 0x staking proxy contract.
|
||||
/// @param weth_ The WETH token contract.
|
||||
constructor(IStaking stakingProxy_, IERC20Token weth_) public {
|
||||
stakingProxy = stakingProxy_;
|
||||
weth = weth_;
|
||||
// operator share = 100%
|
||||
poolId = stakingProxy_.createStakingPool(10 ** 6, false);
|
||||
}
|
||||
|
||||
/// @dev Sends this contract's entire WETH balance to the
|
||||
/// staking proxy contract. This function exists in case
|
||||
/// someone joins the default staking pool and starts
|
||||
/// market making for some reason, thus earning this contract
|
||||
/// some staking rewards. Note that anyone can call this
|
||||
/// function at any time.
|
||||
function returnStakingRewards() external {
|
||||
uint256 wethBalance = weth.balanceOf(address(this));
|
||||
weth.transfer(address(stakingProxy), wethBalance);
|
||||
}
|
||||
}
|
@@ -1,103 +0,0 @@
|
||||
// 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.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IStaking {
|
||||
/// @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 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 Holds the metadata for a staking pool.
|
||||
/// @param operator Operator of the pool.
|
||||
/// @param operatorShare Fraction of the total balance owned by the operator, in ppm.
|
||||
struct Pool {
|
||||
address operator;
|
||||
uint32 operatorShare;
|
||||
}
|
||||
|
||||
/// @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 Returns the current staking epoch number.
|
||||
/// @return epoch The current epoch.
|
||||
function currentEpoch() external view returns (uint256 epoch);
|
||||
|
||||
/// @dev Returns the time (in seconds) at which the current staking epoch started.
|
||||
/// @return startTime The start time of the current epoch, in seconds.
|
||||
function currentEpochStartTimeInSeconds() external view returns (uint256 startTime);
|
||||
|
||||
/// @dev Returns the duration of an epoch in seconds. This value can be updated.
|
||||
/// @return duration The duration of an epoch, in seconds.
|
||||
function epochDurationInSeconds() external view returns (uint256 duration);
|
||||
|
||||
/// @dev Returns a staking pool
|
||||
/// @param poolId Unique id of pool.
|
||||
function getStakingPool(bytes32 poolId) external view returns (Pool memory);
|
||||
|
||||
/// @dev Gets global stake for a given status.
|
||||
/// @param stakeStatus UNDELEGATED or DELEGATED
|
||||
/// @return balance Global stake for given status.
|
||||
function getGlobalStakeByStatus(StakeStatus stakeStatus) external view returns (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,
|
||||
StakeStatus stakeStatus
|
||||
) external view returns (StoredBalance memory balance);
|
||||
|
||||
/// @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 (StoredBalance memory balance);
|
||||
|
||||
/// @dev Returns the stake delegated to a specific staking pool, by a given staker.
|
||||
/// @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 (StoredBalance memory balance);
|
||||
}
|
@@ -1,162 +0,0 @@
|
||||
// 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.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./DefaultPoolOperator.sol";
|
||||
import "./IStaking.sol";
|
||||
|
||||
interface IZrxTreasury {
|
||||
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 (IStaking);
|
||||
|
||||
function defaultPoolOperator() external view returns (DefaultPoolOperator);
|
||||
|
||||
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);
|
||||
}
|
@@ -1,340 +0,0 @@
|
||||
// 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.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-zero-ex/contracts/src/features/libs/LibSignature.sol";
|
||||
import "./IZrxTreasury.sol";
|
||||
|
||||
contract ZrxTreasury is IZrxTreasury {
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
using LibBytesV06 for bytes;
|
||||
|
||||
/// Contract name
|
||||
string private constant CONTRACT_NAME = "Zrx Treasury";
|
||||
|
||||
/// Contract version
|
||||
string private constant CONTRACT_VERSION = "1.0.0";
|
||||
|
||||
/// The EIP-712 typehash for the contract's domain
|
||||
bytes32 private constant DOMAIN_TYPEHASH =
|
||||
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
|
||||
|
||||
/// The EIP-712 typehash for the vote struct
|
||||
bytes32 private constant VOTE_TYPEHASH =
|
||||
keccak256("TreasuryVote(uint256 proposalId,bool support,bytes32[] operatedPoolIds)");
|
||||
|
||||
// Immutables
|
||||
IStaking public immutable override stakingProxy;
|
||||
DefaultPoolOperator public immutable override defaultPoolOperator;
|
||||
bytes32 public immutable override defaultPoolId;
|
||||
uint256 public immutable override votingPeriod;
|
||||
bytes32 immutable domainSeparator;
|
||||
|
||||
uint256 public override proposalThreshold;
|
||||
uint256 public override quorumThreshold;
|
||||
|
||||
// Storage
|
||||
Proposal[] public proposals;
|
||||
mapping(uint256 => mapping(address => bool)) public hasVoted;
|
||||
|
||||
/// @dev Initializes the ZRX treasury and creates the default
|
||||
/// staking pool.
|
||||
/// @param stakingProxy_ The 0x staking proxy contract.
|
||||
/// @param params Immutable treasury parameters.
|
||||
constructor(IStaking stakingProxy_, TreasuryParameters memory params) public {
|
||||
require(params.votingPeriod < stakingProxy_.epochDurationInSeconds(), "VOTING_PERIOD_TOO_LONG");
|
||||
stakingProxy = stakingProxy_;
|
||||
votingPeriod = params.votingPeriod;
|
||||
proposalThreshold = params.proposalThreshold;
|
||||
quorumThreshold = params.quorumThreshold;
|
||||
defaultPoolId = params.defaultPoolId;
|
||||
IStaking.Pool memory defaultPool = stakingProxy_.getStakingPool(params.defaultPoolId);
|
||||
defaultPoolOperator = DefaultPoolOperator(defaultPool.operator);
|
||||
domainSeparator = keccak256(
|
||||
abi.encode(
|
||||
DOMAIN_TYPEHASH,
|
||||
keccak256(bytes(CONTRACT_NAME)),
|
||||
_getChainId(),
|
||||
keccak256(bytes(CONTRACT_VERSION)),
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Allows this contract to receive ether.
|
||||
receive() external payable {}
|
||||
|
||||
/// @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 override {
|
||||
require(msg.sender == address(this), "updateThresholds/ONLY_SELF");
|
||||
proposalThreshold = newProposalThreshold;
|
||||
quorumThreshold = newQuorumThreshold;
|
||||
}
|
||||
|
||||
/// @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 the signer. 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[] memory actions,
|
||||
uint256 executionEpoch,
|
||||
string memory description,
|
||||
bytes32[] memory operatedPoolIds
|
||||
) public override returns (uint256 proposalId) {
|
||||
require(getVotingPower(msg.sender, operatedPoolIds) >= proposalThreshold, "propose/INSUFFICIENT_VOTING_POWER");
|
||||
require(actions.length > 0, "propose/NO_ACTIONS_PROPOSED");
|
||||
uint256 currentEpoch = stakingProxy.currentEpoch();
|
||||
require(executionEpoch >= currentEpoch + 2, "propose/INVALID_EXECUTION_EPOCH");
|
||||
|
||||
proposalId = proposalCount();
|
||||
Proposal storage newProposal = proposals.push();
|
||||
newProposal.actionsHash = keccak256(abi.encode(actions));
|
||||
newProposal.executionEpoch = executionEpoch;
|
||||
newProposal.voteEpoch = currentEpoch + 2;
|
||||
|
||||
emit ProposalCreated(msg.sender, operatedPoolIds, proposalId, actions, executionEpoch, description);
|
||||
}
|
||||
|
||||
/// @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[] memory operatedPoolIds) public override {
|
||||
return _castVote(msg.sender, proposalId, support, operatedPoolIds);
|
||||
}
|
||||
|
||||
/// @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 voter. 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
|
||||
) public override {
|
||||
bytes32 structHash = keccak256(
|
||||
abi.encode(VOTE_TYPEHASH, proposalId, support, keccak256(abi.encodePacked(operatedPoolIds)))
|
||||
);
|
||||
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
|
||||
address signatory = ecrecover(digest, v, r, s);
|
||||
|
||||
return _castVote(signatory, proposalId, support, operatedPoolIds);
|
||||
}
|
||||
|
||||
/// @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) public payable override {
|
||||
if (proposalId >= proposalCount()) {
|
||||
revert("execute/INVALID_PROPOSAL_ID");
|
||||
}
|
||||
Proposal memory proposal = proposals[proposalId];
|
||||
_assertProposalExecutable(proposal, actions);
|
||||
|
||||
proposals[proposalId].executed = true;
|
||||
|
||||
for (uint256 i = 0; i != actions.length; i++) {
|
||||
ProposedAction memory action = actions[i];
|
||||
(bool didSucceed, ) = action.target.call{value: action.value}(action.data);
|
||||
require(didSucceed, "execute/ACTION_EXECUTION_FAILED");
|
||||
}
|
||||
|
||||
emit ProposalExecuted(proposalId);
|
||||
}
|
||||
|
||||
/// @dev Returns the total number of proposals.
|
||||
/// @return count The number of proposals.
|
||||
function proposalCount() public view override returns (uint256 count) {
|
||||
return proposals.length;
|
||||
}
|
||||
|
||||
/// @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[] memory operatedPoolIds
|
||||
) public view override returns (uint256 votingPower) {
|
||||
uint256 delegatedBalance = stakingProxy
|
||||
.getOwnerStakeByStatus(account, IStaking.StakeStatus.DELEGATED)
|
||||
.currentEpochBalance;
|
||||
uint256 balanceDelegatedToDefaultPool = stakingProxy
|
||||
.getStakeDelegatedToPoolByOwner(account, defaultPoolId)
|
||||
.currentEpochBalance;
|
||||
|
||||
// Voting power for ZRX delegated to the default pool is not diluted,
|
||||
// so we double-count the balance delegated to the default pool before
|
||||
// dividing by 2.
|
||||
votingPower = delegatedBalance.safeAdd(balanceDelegatedToDefaultPool).safeDiv(2);
|
||||
|
||||
// Add voting power for operated staking pools.
|
||||
for (uint256 i = 0; i != operatedPoolIds.length; i++) {
|
||||
for (uint256 j = 0; j != i; j++) {
|
||||
require(operatedPoolIds[i] != operatedPoolIds[j], "getVotingPower/DUPLICATE_POOL_ID");
|
||||
}
|
||||
IStaking.Pool memory pool = stakingProxy.getStakingPool(operatedPoolIds[i]);
|
||||
require(pool.operator == account, "getVotingPower/POOL_NOT_OPERATED_BY_ACCOUNT");
|
||||
uint96 stakeDelegatedToPool = stakingProxy
|
||||
.getTotalStakeDelegatedToPool(operatedPoolIds[i])
|
||||
.currentEpochBalance;
|
||||
uint256 poolVotingPower = uint256(stakeDelegatedToPool).safeDiv(2);
|
||||
votingPower = votingPower.safeAdd(poolVotingPower);
|
||||
}
|
||||
|
||||
return votingPower;
|
||||
}
|
||||
|
||||
/// @dev Checks whether the given proposal is executable.
|
||||
/// Reverts if not.
|
||||
/// @param proposal The proposal to check.
|
||||
function _assertProposalExecutable(Proposal memory proposal, ProposedAction[] memory actions) private view {
|
||||
require(keccak256(abi.encode(actions)) == proposal.actionsHash, "_assertProposalExecutable/INVALID_ACTIONS");
|
||||
require(_hasProposalPassed(proposal), "_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED");
|
||||
require(!proposal.executed, "_assertProposalExecutable/PROPOSAL_ALREADY_EXECUTED");
|
||||
require(
|
||||
stakingProxy.currentEpoch() == proposal.executionEpoch,
|
||||
"_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Checks whether the given proposal has passed or not.
|
||||
/// @param proposal The proposal to check.
|
||||
/// @return hasPassed Whether the proposal has passed.
|
||||
function _hasProposalPassed(Proposal memory proposal) private view returns (bool hasPassed) {
|
||||
// Proposal is not passed until the vote is over.
|
||||
if (!_hasVoteEnded(proposal.voteEpoch)) {
|
||||
return false;
|
||||
}
|
||||
// Must have >50% support.
|
||||
if (proposal.votesFor <= proposal.votesAgainst) {
|
||||
return false;
|
||||
}
|
||||
// Must reach quorum threshold.
|
||||
if (proposal.votesFor < quorumThreshold) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Checks whether a vote starting at the given
|
||||
/// epoch has ended or not.
|
||||
/// @param voteEpoch The epoch at which the vote started.
|
||||
/// @return hasEnded Whether the vote has ended.
|
||||
function _hasVoteEnded(uint256 voteEpoch) private view returns (bool hasEnded) {
|
||||
uint256 currentEpoch = stakingProxy.currentEpoch();
|
||||
if (currentEpoch < voteEpoch) {
|
||||
return false;
|
||||
}
|
||||
if (currentEpoch > voteEpoch) {
|
||||
return true;
|
||||
}
|
||||
// voteEpoch == currentEpoch
|
||||
// Vote ends at currentEpochStartTime + votingPeriod
|
||||
uint256 voteEndTime = stakingProxy.currentEpochStartTimeInSeconds().safeAdd(votingPeriod);
|
||||
return block.timestamp > voteEndTime;
|
||||
}
|
||||
|
||||
/// @dev Casts a vote for the given proposal. Only callable
|
||||
/// during the voting period for that proposal. See
|
||||
/// `getVotingPower` for how voting power is computed.
|
||||
function _castVote(address voter, uint256 proposalId, bool support, bytes32[] memory operatedPoolIds) private {
|
||||
if (proposalId >= proposalCount()) {
|
||||
revert("_castVote/INVALID_PROPOSAL_ID");
|
||||
}
|
||||
if (hasVoted[proposalId][voter]) {
|
||||
revert("_castVote/ALREADY_VOTED");
|
||||
}
|
||||
|
||||
Proposal memory proposal = proposals[proposalId];
|
||||
if (proposal.voteEpoch != stakingProxy.currentEpoch() || _hasVoteEnded(proposal.voteEpoch)) {
|
||||
revert("_castVote/VOTING_IS_CLOSED");
|
||||
}
|
||||
|
||||
uint256 votingPower = getVotingPower(voter, operatedPoolIds);
|
||||
if (votingPower == 0) {
|
||||
revert("_castVote/NO_VOTING_POWER");
|
||||
}
|
||||
|
||||
if (support) {
|
||||
proposals[proposalId].votesFor = proposals[proposalId].votesFor.safeAdd(votingPower);
|
||||
} else {
|
||||
proposals[proposalId].votesAgainst = proposals[proposalId].votesAgainst.safeAdd(votingPower);
|
||||
}
|
||||
hasVoted[proposalId][voter] = true;
|
||||
|
||||
emit VoteCast(voter, operatedPoolIds, proposalId, support, votingPower);
|
||||
}
|
||||
|
||||
/// @dev Gets the Ethereum chain id
|
||||
function _getChainId() private pure returns (uint256) {
|
||||
uint256 chainId;
|
||||
assembly {
|
||||
chainId := chainid()
|
||||
}
|
||||
return chainId;
|
||||
}
|
||||
}
|
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.40",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
"description": "Smart contracts for governing the 0x ZRX treasury",
|
||||
"main": "lib/src/index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "yarn pre_build && yarn build:ts",
|
||||
"build:ci": "yarn build",
|
||||
"build:ts": "tsc -b",
|
||||
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
|
||||
"test": "yarn run_mocha",
|
||||
"rebuild_and_test": "run-s build test",
|
||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
|
||||
"compile": "sol-compiler",
|
||||
"watch": "sol-compiler -w",
|
||||
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"lint": "eslint src test",
|
||||
"fix": "eslint --fix src test",
|
||||
"test:ci": "yarn test",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
|
||||
"publish:private": "yarn build && gitpkg publish"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ZrxTreasury,DefaultPoolOperator,ISablier",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(DefaultPoolOperator|ISablier|IStaking|IZrxTreasury|ZrxTreasury).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/0xProject/protocol.git"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/0xProject/protocol/issues"
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.1",
|
||||
"@0x/contract-addresses": "^8.0.3",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "3.3.57",
|
||||
"@0x/contracts-gen": "^2.0.48",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.47",
|
||||
"@0x/sol-compiler": "^4.8.2",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@types/isomorphic-fetch": "^0.0.35",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/prompts": "^2.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"eslint": "^8.23.1",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"lodash": "^4.17.11",
|
||||
"mocha": "^6.2.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"prompts": "^2.4.0",
|
||||
"shx": "^0.2.2",
|
||||
"typedoc": "~0.16.11",
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^7.0.0",
|
||||
"@0x/protocol-utils": "^11.17.6",
|
||||
"@0x/subproviders": "^7.0.0",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^7.0.0",
|
||||
"@0x/web3-wrapper": "^8.0.0",
|
||||
"ethereum-types": "^3.7.1",
|
||||
"ethereumjs-util": "^7.0.10"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DefaultPoolOperator from '../generated-artifacts/DefaultPoolOperator.json';
|
||||
import * as ISablier from '../generated-artifacts/ISablier.json';
|
||||
import * as ZrxTreasury from '../generated-artifacts/ZrxTreasury.json';
|
||||
export const artifacts = {
|
||||
ZrxTreasury: ZrxTreasury as ContractArtifact,
|
||||
DefaultPoolOperator: DefaultPoolOperator as ContractArtifact,
|
||||
ISablier: ISablier as ContractArtifact,
|
||||
};
|
@@ -1,2 +0,0 @@
|
||||
export { artifacts } from './artifacts';
|
||||
export { DefaultPoolOperatorContract, ZrxTreasuryContract } from './wrappers';
|
File diff suppressed because one or more lines are too long
@@ -1,8 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/default_pool_operator';
|
||||
export * from '../generated-wrappers/i_sablier';
|
||||
export * from '../generated-wrappers/zrx_treasury';
|
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as DefaultPoolOperator from '../test/generated-artifacts/DefaultPoolOperator.json';
|
||||
import * as ISablier from '../test/generated-artifacts/ISablier.json';
|
||||
import * as IStaking from '../test/generated-artifacts/IStaking.json';
|
||||
import * as IZrxTreasury from '../test/generated-artifacts/IZrxTreasury.json';
|
||||
import * as ZrxTreasury from '../test/generated-artifacts/ZrxTreasury.json';
|
||||
export const artifacts = {
|
||||
ISablier: ISablier as ContractArtifact,
|
||||
DefaultPoolOperator: DefaultPoolOperator as ContractArtifact,
|
||||
IStaking: IStaking as ContractArtifact,
|
||||
IZrxTreasury: IZrxTreasury as ContractArtifact,
|
||||
ZrxTreasury: ZrxTreasury as ContractArtifact,
|
||||
};
|
@@ -1,300 +0,0 @@
|
||||
import { artifacts as erc20Artifacts, ERC20TokenEvents } from '@0x/contracts-erc20';
|
||||
import { StakingContract, StakingProxyContract } from '@0x/contracts-staking';
|
||||
import { blockchainTests, constants, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { BigNumber, hexUtils, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { proposals } from '../src/proposals';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { ISablierEvents, ZrxTreasuryContract, ZrxTreasuryEvents } from './wrappers';
|
||||
|
||||
const SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/mzhu25/zeroex-staking';
|
||||
const STAKING_PROXY_ADDRESS = '0xa26e80e7dea86279c6d778d702cc413e6cffa777';
|
||||
const TREASURY_ADDRESS = '0x0bb1810061c2f5b2088054ee184e6c79e1591101';
|
||||
const PROPOSER = process.env.PROPOSER || constants.NULL_ADDRESS;
|
||||
const VOTER = '0xba4f44e774158408e2dc6c5cb65bc995f0a89180';
|
||||
const VOTER_OPERATED_POOLS = ['0x0000000000000000000000000000000000000000000000000000000000000017'];
|
||||
const VOTER_2 = '0x9a4eb1101c0c053505bd71d2ffa27ed902dead85';
|
||||
const VOTER_2_OPERATED_POOLS = ['0x0000000000000000000000000000000000000000000000000000000000000029'];
|
||||
blockchainTests.configure({
|
||||
fork: {
|
||||
unlockedAccounts: [PROPOSER, VOTER, VOTER_2],
|
||||
},
|
||||
});
|
||||
|
||||
async function querySubgraphAsync(operatorAddress: string): Promise<string[]> {
|
||||
const query = `
|
||||
{
|
||||
stakingActor(id: "${operatorAddress}") {
|
||||
operatedPools {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const response = await fetch(SUBGRAPH_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
}),
|
||||
});
|
||||
const {
|
||||
data: { stakingActor },
|
||||
} = await response.json();
|
||||
if (stakingActor) {
|
||||
return stakingActor.operatedPools.map((pool: { id: string }) => hexUtils.leftPad(pool.id));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
blockchainTests.fork.skip('Treasury proposal mainnet fork tests', env => {
|
||||
let staking: StakingContract;
|
||||
let stakingProxy: StakingProxyContract;
|
||||
let treasury: ZrxTreasuryContract;
|
||||
let votingPeriod: BigNumber;
|
||||
|
||||
async function fastForwardToNextEpochAsync(): Promise<void> {
|
||||
const epochEndTime = await staking.getCurrentEpochEarliestEndTimeInSeconds().callAsync();
|
||||
const lastBlockTime = await env.web3Wrapper.getBlockTimestampAsync('latest');
|
||||
const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber());
|
||||
await env.web3Wrapper.increaseTimeAsync(dt);
|
||||
// mine next block
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const lastPoolId = new BigNumber(await staking.lastPoolId().callAsync(), 16);
|
||||
const batchExecuteCalldata = [
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
staking.endEpoch().getABIEncodedTransactionData(),
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
];
|
||||
await stakingProxy.batchExecute(batchExecuteCalldata).awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
const abis = _.mapValues({ ...artifacts, ...erc20Artifacts }, v => v.compilerOutput.abi);
|
||||
treasury = new ZrxTreasuryContract(TREASURY_ADDRESS, env.provider, env.txDefaults, abis);
|
||||
votingPeriod = await treasury.votingPeriod().callAsync();
|
||||
staking = new StakingContract(STAKING_PROXY_ADDRESS, env.provider, env.txDefaults);
|
||||
stakingProxy = new StakingProxyContract(STAKING_PROXY_ADDRESS, env.provider, env.txDefaults);
|
||||
});
|
||||
|
||||
describe('Proposal 0', () => {
|
||||
it('works', async () => {
|
||||
const proposal = proposals[0];
|
||||
let executionEpoch: BigNumber;
|
||||
if (proposal.executionEpoch) {
|
||||
executionEpoch = proposal.executionEpoch;
|
||||
} else {
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
executionEpoch = currentEpoch.plus(2);
|
||||
}
|
||||
const pools = await querySubgraphAsync(PROPOSER);
|
||||
const proposeTx = treasury.propose(proposal.actions, executionEpoch, proposal.description, pools);
|
||||
|
||||
const calldata = proposeTx.getABIEncodedTransactionData();
|
||||
logUtils.log('ZrxTreasury.propose calldata:');
|
||||
logUtils.log(calldata);
|
||||
|
||||
const proposalId = await proposeTx.callAsync({ from: PROPOSER });
|
||||
const receipt = await proposeTx.awaitTransactionSuccessAsync({ from: PROPOSER });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
...proposal,
|
||||
proposalId,
|
||||
executionEpoch,
|
||||
proposer: PROPOSER,
|
||||
operatedPoolIds: pools,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalCreated,
|
||||
);
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury
|
||||
.castVote(proposalId, true, VOTER_OPERATED_POOLS)
|
||||
.awaitTransactionSuccessAsync({ from: VOTER });
|
||||
await env.web3Wrapper.increaseTimeAsync(votingPeriod.plus(1).toNumber());
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const executeTx = await treasury.execute(proposalId, proposal.actions).awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
proposalId,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalExecuted,
|
||||
);
|
||||
const recipient = '0xf9347f751a6a1467abc722ec7d80ba2698dd9d6c';
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
_from: TREASURY_ADDRESS,
|
||||
_to: recipient,
|
||||
_value: new BigNumber(400_000).times('1e18'),
|
||||
},
|
||||
],
|
||||
ERC20TokenEvents.Transfer,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Proposal 1', () => {
|
||||
it('works', async () => {
|
||||
const proposal = proposals[1];
|
||||
let executionEpoch: BigNumber;
|
||||
if (proposal.executionEpoch) {
|
||||
executionEpoch = proposal.executionEpoch;
|
||||
} else {
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
executionEpoch = currentEpoch.plus(2);
|
||||
}
|
||||
const pools = await querySubgraphAsync(PROPOSER);
|
||||
const proposeTx = treasury.propose(proposal.actions, executionEpoch, proposal.description, pools);
|
||||
|
||||
const calldata = proposeTx.getABIEncodedTransactionData();
|
||||
logUtils.log('ZrxTreasury.propose calldata:');
|
||||
logUtils.log(calldata);
|
||||
|
||||
const proposalId = await proposeTx.callAsync({ from: PROPOSER });
|
||||
const receipt = await proposeTx.awaitTransactionSuccessAsync({ from: PROPOSER });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
...proposal,
|
||||
proposalId,
|
||||
executionEpoch,
|
||||
proposer: PROPOSER,
|
||||
operatedPoolIds: pools,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalCreated,
|
||||
);
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury
|
||||
.castVote(proposalId, true, VOTER_OPERATED_POOLS)
|
||||
.awaitTransactionSuccessAsync({ from: VOTER });
|
||||
await env.web3Wrapper.increaseTimeAsync(votingPeriod.plus(1).toNumber());
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const executeTx = await treasury.execute(proposalId, proposal.actions).awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
proposalId,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalExecuted,
|
||||
);
|
||||
const recipient = '0xab66cc8fd10457ebc9d13b9760c835f0a4cbc487';
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
_from: TREASURY_ADDRESS,
|
||||
_to: recipient,
|
||||
_value: new BigNumber(330_813).times('1e18'),
|
||||
},
|
||||
{
|
||||
_from: TREASURY_ADDRESS,
|
||||
_to: recipient,
|
||||
_value: new BigNumber(420000).times('1e18'),
|
||||
},
|
||||
],
|
||||
ERC20TokenEvents.Transfer,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Proposal 2', () => {
|
||||
it('works', async () => {
|
||||
const proposal = proposals[2];
|
||||
let executionEpoch: BigNumber;
|
||||
if (proposal.executionEpoch) {
|
||||
executionEpoch = proposal.executionEpoch;
|
||||
} else {
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
executionEpoch = currentEpoch.plus(2);
|
||||
}
|
||||
const pools = await querySubgraphAsync(PROPOSER);
|
||||
const proposeTx = treasury.propose(proposal.actions, executionEpoch, proposal.description, pools);
|
||||
|
||||
const calldata = proposeTx.getABIEncodedTransactionData();
|
||||
logUtils.log('ZrxTreasury.propose calldata:');
|
||||
logUtils.log(calldata);
|
||||
|
||||
const proposalId = await proposeTx.callAsync({ from: PROPOSER });
|
||||
const receipt = await proposeTx.awaitTransactionSuccessAsync({ from: PROPOSER });
|
||||
verifyEventsFromLogs(
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
...proposal,
|
||||
proposalId,
|
||||
executionEpoch,
|
||||
proposer: PROPOSER,
|
||||
operatedPoolIds: pools,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalCreated,
|
||||
);
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury
|
||||
.castVote(proposalId, true, VOTER_OPERATED_POOLS)
|
||||
.awaitTransactionSuccessAsync({ from: VOTER });
|
||||
await treasury
|
||||
.castVote(proposalId, true, VOTER_2_OPERATED_POOLS)
|
||||
.awaitTransactionSuccessAsync({ from: VOTER_2 });
|
||||
await env.web3Wrapper.increaseTimeAsync(votingPeriod.plus(1).toNumber());
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const executeTx = await treasury.execute(proposalId, proposal.actions).awaitTransactionSuccessAsync();
|
||||
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
proposalId,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalExecuted,
|
||||
);
|
||||
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
recipient: '0x976378445D31D81b15576811450A7b9797206807',
|
||||
deposit: new BigNumber('485392999999999970448000'),
|
||||
tokenAddress: '0xe41d2489571d322189246dafa5ebde1f4699f498',
|
||||
startTime: new BigNumber(1635188400),
|
||||
stopTime: new BigNumber(1666724400),
|
||||
},
|
||||
{
|
||||
recipient: '0x976378445D31D81b15576811450A7b9797206807',
|
||||
deposit: new BigNumber('378035999999999992944000'),
|
||||
tokenAddress: '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0',
|
||||
startTime: new BigNumber(1635188400),
|
||||
stopTime: new BigNumber(1666724400),
|
||||
},
|
||||
],
|
||||
ISablierEvents.CreateStream,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,756 +0,0 @@
|
||||
import { artifacts as assetProxyArtifacts, ERC20ProxyContract } from '@0x/contracts-asset-proxy';
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
artifacts as stakingArtifacts,
|
||||
constants as stakingConstants,
|
||||
StakeInfo,
|
||||
StakeStatus,
|
||||
StakingProxyContract,
|
||||
TestStakingContract,
|
||||
ZrxVaultContract,
|
||||
} from '@0x/contracts-staking';
|
||||
import {
|
||||
blockchainTests,
|
||||
constants,
|
||||
expect,
|
||||
getRandomInteger,
|
||||
randomAddress,
|
||||
verifyEventsFromLogs,
|
||||
} from '@0x/contracts-test-utils';
|
||||
import { TreasuryVote } from '@0x/protocol-utils';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as ethUtil from 'ethereumjs-util';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { DefaultPoolOperatorContract, ZrxTreasuryContract, ZrxTreasuryEvents } from './wrappers';
|
||||
|
||||
blockchainTests.resets('Treasury governance', env => {
|
||||
const TREASURY_PARAMS = {
|
||||
votingPeriod: new BigNumber(3).times(stakingConstants.ONE_DAY_IN_SECONDS),
|
||||
proposalThreshold: new BigNumber(100),
|
||||
quorumThreshold: new BigNumber(1000),
|
||||
defaultPoolId: stakingConstants.INITIAL_POOL_ID,
|
||||
};
|
||||
const PROPOSAL_DESCRIPTION = 'A very compelling proposal!';
|
||||
const TREASURY_BALANCE = constants.INITIAL_ERC20_BALANCE;
|
||||
const INVALID_PROPOSAL_ID = new BigNumber(999);
|
||||
const GRANT_PROPOSALS = [
|
||||
{ recipient: randomAddress(), amount: getRandomInteger(1, TREASURY_BALANCE.dividedToIntegerBy(2)) },
|
||||
{ recipient: randomAddress(), amount: getRandomInteger(1, TREASURY_BALANCE.dividedToIntegerBy(2)) },
|
||||
];
|
||||
|
||||
interface ProposedAction {
|
||||
target: string;
|
||||
data: string;
|
||||
value: BigNumber;
|
||||
}
|
||||
|
||||
let zrx: DummyERC20TokenContract;
|
||||
let weth: DummyERC20TokenContract;
|
||||
let erc20ProxyContract: ERC20ProxyContract;
|
||||
let staking: TestStakingContract;
|
||||
let treasury: ZrxTreasuryContract;
|
||||
let defaultPoolId: string;
|
||||
let defaultPoolOperator: DefaultPoolOperatorContract;
|
||||
let admin: string;
|
||||
let nonDefaultPoolId: string;
|
||||
let poolOperator: string;
|
||||
let delegator: string;
|
||||
let relayer: string;
|
||||
let delegatorPrivateKey: string;
|
||||
let actions: ProposedAction[];
|
||||
|
||||
async function deployStakingAsync(): Promise<void> {
|
||||
erc20ProxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync(
|
||||
assetProxyArtifacts.ERC20Proxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
assetProxyArtifacts,
|
||||
);
|
||||
const zrxVaultContract = await ZrxVaultContract.deployFrom0xArtifactAsync(
|
||||
stakingArtifacts.ZrxVault,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
stakingArtifacts,
|
||||
erc20ProxyContract.address,
|
||||
zrx.address,
|
||||
);
|
||||
await erc20ProxyContract.addAuthorizedAddress(zrxVaultContract.address).awaitTransactionSuccessAsync();
|
||||
await zrxVaultContract.addAuthorizedAddress(admin).awaitTransactionSuccessAsync();
|
||||
const stakingLogic = await TestStakingContract.deployFrom0xArtifactAsync(
|
||||
stakingArtifacts.TestStaking,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
weth.address,
|
||||
zrxVaultContract.address,
|
||||
);
|
||||
const stakingProxyContract = await StakingProxyContract.deployFrom0xArtifactAsync(
|
||||
stakingArtifacts.StakingProxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
stakingLogic.address,
|
||||
);
|
||||
await stakingProxyContract.addAuthorizedAddress(admin).awaitTransactionSuccessAsync();
|
||||
await zrxVaultContract.setStakingProxy(stakingProxyContract.address).awaitTransactionSuccessAsync();
|
||||
staking = new TestStakingContract(stakingProxyContract.address, env.provider, env.txDefaults);
|
||||
}
|
||||
|
||||
async function fastForwardToNextEpochAsync(): Promise<void> {
|
||||
const epochEndTime = await staking.getCurrentEpochEarliestEndTimeInSeconds().callAsync();
|
||||
const lastBlockTime = await env.web3Wrapper.getBlockTimestampAsync('latest');
|
||||
const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber());
|
||||
await env.web3Wrapper.increaseTimeAsync(dt);
|
||||
// mine next block
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
await staking.endEpoch().awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
const accounts = await env.getAccountAddressesAsync();
|
||||
[admin, poolOperator, delegator, relayer] = accounts;
|
||||
delegatorPrivateKey = hexUtils.toHex(constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(delegator)]);
|
||||
|
||||
zrx = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
erc20Artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
weth = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
erc20Artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
constants.DUMMY_TOKEN_DECIMALS,
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
await deployStakingAsync();
|
||||
await zrx.mint(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await zrx.mint(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await zrx
|
||||
.approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE)
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await zrx
|
||||
.approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
|
||||
defaultPoolOperator = await DefaultPoolOperatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.DefaultPoolOperator,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{ ...artifacts, ...erc20Artifacts },
|
||||
staking.address,
|
||||
weth.address,
|
||||
);
|
||||
defaultPoolId = stakingConstants.INITIAL_POOL_ID;
|
||||
|
||||
const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false);
|
||||
nonDefaultPoolId = await createStakingPoolTx.callAsync({ from: poolOperator });
|
||||
await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
|
||||
treasury = await ZrxTreasuryContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ZrxTreasury,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{ ...artifacts, ...erc20Artifacts },
|
||||
staking.address,
|
||||
TREASURY_PARAMS,
|
||||
);
|
||||
|
||||
await zrx.mint(TREASURY_BALANCE).awaitTransactionSuccessAsync();
|
||||
await zrx.transfer(treasury.address, TREASURY_BALANCE).awaitTransactionSuccessAsync();
|
||||
actions = [
|
||||
{
|
||||
target: zrx.address,
|
||||
data: zrx
|
||||
.transfer(GRANT_PROPOSALS[0].recipient, GRANT_PROPOSALS[0].amount)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
{
|
||||
target: zrx.address,
|
||||
data: zrx
|
||||
.transfer(GRANT_PROPOSALS[1].recipient, GRANT_PROPOSALS[1].amount)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
];
|
||||
});
|
||||
describe('getVotingPower()', () => {
|
||||
it('Unstaked ZRX has no voting power', async () => {
|
||||
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(votingPower).to.bignumber.equal(0);
|
||||
});
|
||||
it('Staked but undelegated ZRX has no voting power', async () => {
|
||||
await staking.stake(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: delegator });
|
||||
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(votingPower).to.bignumber.equal(0);
|
||||
});
|
||||
it('ZRX delegated during epoch N has no voting power during Epoch N', async () => {
|
||||
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(votingPower).to.bignumber.equal(0);
|
||||
await fastForwardToNextEpochAsync();
|
||||
});
|
||||
it('ZRX delegated to the default pool retains full voting power', async () => {
|
||||
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(votingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold);
|
||||
});
|
||||
it('ZRX delegated to a non-default pool splits voting power between delegator and pool operator', async () => {
|
||||
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const delegatorVotingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(delegatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2));
|
||||
const operatorVotingPower = await treasury.getVotingPower(poolOperator, [nonDefaultPoolId]).callAsync();
|
||||
expect(operatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2));
|
||||
});
|
||||
it('Reverts if given duplicate pool IDs', async () => {
|
||||
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tx = treasury.getVotingPower(poolOperator, [nonDefaultPoolId, nonDefaultPoolId]).callAsync();
|
||||
return expect(tx).to.revertWith('getVotingPower/DUPLICATE_POOL_ID');
|
||||
});
|
||||
it('Correctly sums voting power delegated to multiple pools', async () => {
|
||||
await staking
|
||||
.stake(TREASURY_PARAMS.proposalThreshold.times(2))
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
// Delegate half of total stake to the default pool.
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
// Delegate the other half to a non-default pool.
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const delegatorVotingPower = await treasury.getVotingPower(delegator, []).callAsync();
|
||||
expect(delegatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.times(1.5));
|
||||
});
|
||||
it('Correctly sums voting power for operator with multiple pools', async () => {
|
||||
const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false);
|
||||
const firstPool = nonDefaultPoolId;
|
||||
const secondPool = await createStakingPoolTx.callAsync({ from: poolOperator });
|
||||
await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
|
||||
const amountDelegatedToDefaultPool = new BigNumber(1337);
|
||||
const amountSelfDelegatedToFirstPool = new BigNumber(420);
|
||||
const amountExternallyDelegatedToSecondPool = new BigNumber(2020);
|
||||
|
||||
await staking
|
||||
.stake(amountDelegatedToDefaultPool.plus(amountSelfDelegatedToFirstPool))
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
amountDelegatedToDefaultPool,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, firstPool),
|
||||
amountSelfDelegatedToFirstPool,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await staking
|
||||
.stake(amountExternallyDelegatedToSecondPool)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, secondPool),
|
||||
amountExternallyDelegatedToSecondPool,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
|
||||
await fastForwardToNextEpochAsync();
|
||||
const votingPower = await treasury.getVotingPower(poolOperator, [firstPool, secondPool]).callAsync();
|
||||
expect(votingPower).to.bignumber.equal(
|
||||
amountDelegatedToDefaultPool
|
||||
.plus(amountSelfDelegatedToFirstPool)
|
||||
.plus(amountExternallyDelegatedToSecondPool.dividedToIntegerBy(2)),
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('propose()', () => {
|
||||
it('Cannot create proposal without sufficient voting power', async () => {
|
||||
const votingPower = TREASURY_PARAMS.proposalThreshold.minus(1);
|
||||
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
votingPower,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
const tx = treasury
|
||||
.propose(actions, currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('propose/INSUFFICIENT_VOTING_POWER');
|
||||
});
|
||||
it('Cannot create proposal with no actions', async () => {
|
||||
const votingPower = TREASURY_PARAMS.proposalThreshold;
|
||||
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
votingPower,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
const tx = treasury
|
||||
.propose([], currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('propose/NO_ACTIONS_PROPOSED');
|
||||
});
|
||||
it('Cannot create proposal with an invalid execution epoch', async () => {
|
||||
const votingPower = TREASURY_PARAMS.proposalThreshold;
|
||||
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
votingPower,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
const tx = treasury
|
||||
.propose(actions, currentEpoch.plus(1), PROPOSAL_DESCRIPTION, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('propose/INVALID_EXECUTION_EPOCH');
|
||||
});
|
||||
it('Can create a valid proposal', async () => {
|
||||
const votingPower = TREASURY_PARAMS.proposalThreshold;
|
||||
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
votingPower,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
const executionEpoch = currentEpoch.plus(2);
|
||||
const tx = await treasury
|
||||
.propose(actions, executionEpoch, PROPOSAL_DESCRIPTION, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
const proposalId = new BigNumber(0);
|
||||
verifyEventsFromLogs(
|
||||
tx.logs,
|
||||
[
|
||||
{
|
||||
proposer: delegator,
|
||||
operatedPoolIds: [],
|
||||
proposalId,
|
||||
actions,
|
||||
executionEpoch,
|
||||
description: PROPOSAL_DESCRIPTION,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.ProposalCreated,
|
||||
);
|
||||
expect(await treasury.proposalCount().callAsync()).to.bignumber.equal(1);
|
||||
});
|
||||
});
|
||||
describe('castVote() and castVoteBySignature()', () => {
|
||||
const VOTE_PROPOSAL_ID = new BigNumber(0);
|
||||
const DELEGATOR_VOTING_POWER = new BigNumber(420);
|
||||
|
||||
before(async () => {
|
||||
await staking.stake(DELEGATOR_VOTING_POWER).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
DELEGATOR_VOTING_POWER,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
await treasury
|
||||
.propose(actions, currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
});
|
||||
// castVote()
|
||||
it('Cannot vote on invalid proposalId', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tx = treasury
|
||||
.castVote(INVALID_PROPOSAL_ID, true, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('_castVote/INVALID_PROPOSAL_ID');
|
||||
});
|
||||
it('Cannot vote before voting period starts', async () => {
|
||||
const tx = treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('_castVote/VOTING_IS_CLOSED');
|
||||
});
|
||||
it('Cannot vote after voting period ends', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await env.web3Wrapper.increaseTimeAsync(TREASURY_PARAMS.votingPeriod.plus(1).toNumber());
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const tx = treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('_castVote/VOTING_IS_CLOSED');
|
||||
});
|
||||
it('Cannot vote twice on same proposal', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
const tx = treasury.castVote(VOTE_PROPOSAL_ID, false, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
return expect(tx).to.revertWith('_castVote/ALREADY_VOTED');
|
||||
});
|
||||
it('Can cast a valid vote', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tx = await treasury
|
||||
.castVote(VOTE_PROPOSAL_ID, true, [])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
verifyEventsFromLogs(
|
||||
tx.logs,
|
||||
[
|
||||
{
|
||||
voter: delegator,
|
||||
operatedPoolIds: [],
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
support: true,
|
||||
votingPower: DELEGATOR_VOTING_POWER,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.VoteCast,
|
||||
);
|
||||
});
|
||||
// castVoteBySignature()
|
||||
it('Cannot vote by signature on invalid proposalId', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
const vote = new TreasuryVote({
|
||||
proposalId: INVALID_PROPOSAL_ID,
|
||||
verifyingContract: admin,
|
||||
});
|
||||
const signature = vote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const tx = treasury
|
||||
.castVoteBySignature(INVALID_PROPOSAL_ID, true, [], signature.v, signature.r, signature.s)
|
||||
.awaitTransactionSuccessAsync({ from: relayer });
|
||||
return expect(tx).to.revertWith('_castVote/INVALID_PROPOSAL_ID');
|
||||
});
|
||||
it('Cannot vote by signature before voting period starts', async () => {
|
||||
const vote = new TreasuryVote({
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
verifyingContract: admin,
|
||||
});
|
||||
const signature = vote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const tx = treasury
|
||||
.castVoteBySignature(VOTE_PROPOSAL_ID, true, [], signature.v, signature.r, signature.s)
|
||||
.awaitTransactionSuccessAsync({ from: relayer });
|
||||
return expect(tx).to.revertWith('_castVote/VOTING_IS_CLOSED');
|
||||
});
|
||||
it('Cannot vote by signature after voting period ends', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await env.web3Wrapper.increaseTimeAsync(TREASURY_PARAMS.votingPeriod.plus(1).toNumber());
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
|
||||
const vote = new TreasuryVote({
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
verifyingContract: admin,
|
||||
});
|
||||
const signature = vote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const tx = treasury
|
||||
.castVoteBySignature(VOTE_PROPOSAL_ID, true, [], signature.v, signature.r, signature.s)
|
||||
.awaitTransactionSuccessAsync({ from: relayer });
|
||||
return expect(tx).to.revertWith('_castVote/VOTING_IS_CLOSED');
|
||||
});
|
||||
it('Can recover the address from signature correctly', async () => {
|
||||
const vote = new TreasuryVote({
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
verifyingContract: admin,
|
||||
});
|
||||
const signature = vote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const publicKey = ethUtil.ecrecover(
|
||||
ethUtil.toBuffer(vote.getEIP712Hash()),
|
||||
signature.v,
|
||||
ethUtil.toBuffer(signature.r),
|
||||
ethUtil.toBuffer(signature.s),
|
||||
);
|
||||
const address = ethUtil.publicToAddress(publicKey);
|
||||
|
||||
expect(ethUtil.bufferToHex(address)).to.be.equal(delegator);
|
||||
});
|
||||
it('Can cast a valid vote by signature', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
|
||||
const vote = new TreasuryVote({
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
verifyingContract: treasury.address,
|
||||
chainId: 1337,
|
||||
support: false,
|
||||
});
|
||||
const signature = vote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const tx = await treasury
|
||||
.castVoteBySignature(VOTE_PROPOSAL_ID, false, [], signature.v, signature.r, signature.s)
|
||||
.awaitTransactionSuccessAsync({ from: relayer });
|
||||
|
||||
verifyEventsFromLogs(
|
||||
tx.logs,
|
||||
[
|
||||
{
|
||||
voter: delegator,
|
||||
operatedPoolIds: [],
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
support: vote.support,
|
||||
votingPower: DELEGATOR_VOTING_POWER,
|
||||
},
|
||||
],
|
||||
ZrxTreasuryEvents.VoteCast,
|
||||
);
|
||||
});
|
||||
it('Cannot vote by signature twice on same proposal', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
|
||||
const secondVote = new TreasuryVote({
|
||||
proposalId: VOTE_PROPOSAL_ID,
|
||||
verifyingContract: treasury.address,
|
||||
chainId: 1337,
|
||||
support: false,
|
||||
});
|
||||
const signature = secondVote.getSignatureWithKey(delegatorPrivateKey);
|
||||
const secondVoteTx = treasury
|
||||
.castVoteBySignature(VOTE_PROPOSAL_ID, false, [], signature.v, signature.r, signature.s)
|
||||
.awaitTransactionSuccessAsync({ from: relayer });
|
||||
return expect(secondVoteTx).to.revertWith('_castVote/ALREADY_VOTED');
|
||||
});
|
||||
});
|
||||
describe('execute()', () => {
|
||||
let passedProposalId: BigNumber;
|
||||
let failedProposalId: BigNumber;
|
||||
let defeatedProposalId: BigNumber;
|
||||
let ongoingVoteProposalId: BigNumber;
|
||||
|
||||
before(async () => {
|
||||
// Operator has enough ZRX to create and pass a proposal
|
||||
await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.quorumThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
// Delegator only has enough ZRX to create a proposal
|
||||
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.proposalThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
// Proposal 0
|
||||
let tx = treasury.propose(actions, currentEpoch.plus(4), PROPOSAL_DESCRIPTION, []);
|
||||
passedProposalId = await tx.callAsync({ from: delegator });
|
||||
await tx.awaitTransactionSuccessAsync({ from: delegator });
|
||||
// Proposal 1
|
||||
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
|
||||
failedProposalId = await tx.callAsync({ from: delegator });
|
||||
await tx.awaitTransactionSuccessAsync({ from: delegator });
|
||||
// Proposal 2
|
||||
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
|
||||
defeatedProposalId = await tx.callAsync({ from: delegator });
|
||||
await tx.awaitTransactionSuccessAsync({ from: delegator });
|
||||
|
||||
await fastForwardToNextEpochAsync();
|
||||
// Proposal 3
|
||||
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
|
||||
ongoingVoteProposalId = await tx.callAsync({ from: delegator });
|
||||
await tx.awaitTransactionSuccessAsync({ from: delegator });
|
||||
|
||||
await fastForwardToNextEpochAsync();
|
||||
/********** Start Vote Epoch for Proposals 0, 1, 2 **********/
|
||||
// Proposal 0 passes
|
||||
await treasury.castVote(passedProposalId, true, []).awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
// Proposal 1 fails to reach quorum
|
||||
await treasury.castVote(failedProposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
// Proposal 2 is voted down
|
||||
await treasury.castVote(defeatedProposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await treasury.castVote(defeatedProposalId, false, []).awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
/********** End Vote Epoch for Proposals 0, 1, 2 **********/
|
||||
|
||||
await fastForwardToNextEpochAsync();
|
||||
/********** Start Execution Epoch for Proposals 1, 2, 3 **********/
|
||||
/********** Start Vote Epoch for Proposal 3 **********************/
|
||||
// Proposal 3 has enough votes to pass, but the vote is ongoing
|
||||
await treasury
|
||||
.castVote(ongoingVoteProposalId, true, [])
|
||||
.awaitTransactionSuccessAsync({ from: poolOperator });
|
||||
});
|
||||
it('Cannot execute an invalid proposalId', async () => {
|
||||
const tx = treasury.execute(INVALID_PROPOSAL_ID, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('execute/INVALID_PROPOSAL_ID');
|
||||
});
|
||||
it('Cannot execute a proposal whose vote is ongoing', async () => {
|
||||
const tx = treasury.execute(ongoingVoteProposalId, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
|
||||
});
|
||||
it('Cannot execute a proposal that failed to reach quorum', async () => {
|
||||
const tx = treasury.execute(failedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
|
||||
});
|
||||
it('Cannot execute a proposal that was defeated in its vote', async () => {
|
||||
const tx = treasury.execute(defeatedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
|
||||
});
|
||||
it('Cannot execute before or after the execution epoch', async () => {
|
||||
const tooEarly = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
await expect(tooEarly).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
|
||||
await fastForwardToNextEpochAsync();
|
||||
// Proposal 0 is executable here
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tooLate = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tooLate).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
|
||||
});
|
||||
it('Cannot execute the same proposal twice', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
const tx = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_ALREADY_EXECUTED');
|
||||
});
|
||||
it('Cannot execute actions that do not match the proposal `actionsHash`', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tx = treasury
|
||||
.execute(passedProposalId, [
|
||||
{
|
||||
target: zrx.address,
|
||||
data: zrx.transfer(randomAddress(), GRANT_PROPOSALS[0].amount).getABIEncodedTransactionData(),
|
||||
value: constants.ZERO_AMOUNT,
|
||||
},
|
||||
])
|
||||
.awaitTransactionSuccessAsync();
|
||||
return expect(tx).to.revertWith('_assertProposalExecutable/INVALID_ACTIONS');
|
||||
});
|
||||
it('Can execute a valid proposal', async () => {
|
||||
await fastForwardToNextEpochAsync();
|
||||
const tx = await treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
|
||||
verifyEventsFromLogs(tx.logs, [{ proposalId: passedProposalId }], ZrxTreasuryEvents.ProposalExecuted);
|
||||
expect(await zrx.balanceOf(GRANT_PROPOSALS[0].recipient).callAsync()).to.bignumber.equal(
|
||||
GRANT_PROPOSALS[0].amount,
|
||||
);
|
||||
expect(await zrx.balanceOf(GRANT_PROPOSALS[1].recipient).callAsync()).to.bignumber.equal(
|
||||
GRANT_PROPOSALS[1].amount,
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('Default pool operator contract', () => {
|
||||
it('Returns WETH to the staking proxy', async () => {
|
||||
const wethAmount = new BigNumber(1337);
|
||||
await weth.mint(wethAmount).awaitTransactionSuccessAsync();
|
||||
// Some amount of WETH ends up in the default pool operator
|
||||
// contract, e.g. from errant staking rewards.
|
||||
await weth.transfer(defaultPoolOperator.address, wethAmount).awaitTransactionSuccessAsync();
|
||||
// This function should send all the WETH to the staking proxy.
|
||||
await defaultPoolOperator.returnStakingRewards().awaitTransactionSuccessAsync();
|
||||
expect(await weth.balanceOf(defaultPoolOperator.address).callAsync()).to.bignumber.equal(0);
|
||||
expect(await weth.balanceOf(staking.address).callAsync()).to.bignumber.equal(wethAmount);
|
||||
});
|
||||
});
|
||||
describe('Can update thresholds via proposal', () => {
|
||||
it('Updates proposal and quorum thresholds', async () => {
|
||||
// Delegator has enough ZRX to create and pass a proposal
|
||||
await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await staking
|
||||
.moveStake(
|
||||
new StakeInfo(StakeStatus.Undelegated),
|
||||
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
|
||||
TREASURY_PARAMS.quorumThreshold,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
const currentEpoch = await staking.currentEpoch().callAsync();
|
||||
const newProposalThreshold = new BigNumber(420);
|
||||
const newQuorumThreshold = new BigNumber(1337);
|
||||
const updateThresholdsAction = {
|
||||
target: treasury.address,
|
||||
data: treasury
|
||||
.updateThresholds(newProposalThreshold, newQuorumThreshold)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: constants.ZERO_AMOUNT,
|
||||
};
|
||||
const tx = treasury.propose(
|
||||
[updateThresholdsAction],
|
||||
currentEpoch.plus(3),
|
||||
`Updates proposal threshold to ${newProposalThreshold} and quorum threshold to ${newQuorumThreshold}`,
|
||||
[],
|
||||
);
|
||||
const proposalId = await tx.callAsync({ from: delegator });
|
||||
await tx.awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury.castVote(proposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
|
||||
await fastForwardToNextEpochAsync();
|
||||
await treasury
|
||||
.execute(proposalId, [updateThresholdsAction])
|
||||
.awaitTransactionSuccessAsync({ from: delegator });
|
||||
const proposalThreshold = await treasury.proposalThreshold().callAsync();
|
||||
const quorumThreshold = await treasury.quorumThreshold().callAsync();
|
||||
expect(proposalThreshold).to.bignumber.equal(newProposalThreshold);
|
||||
expect(quorumThreshold).to.bignumber.equal(newQuorumThreshold);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/default_pool_operator';
|
||||
export * from '../test/generated-wrappers/i_sablier';
|
||||
export * from '../test/generated-wrappers/i_staking';
|
||||
export * from '../test/generated-wrappers/i_zrx_treasury';
|
||||
export * from '../test/generated-wrappers/zrx_treasury';
|
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig",
|
||||
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/DefaultPoolOperator.json",
|
||||
"generated-artifacts/ISablier.json",
|
||||
"generated-artifacts/ZrxTreasury.json",
|
||||
"test/generated-artifacts/DefaultPoolOperator.json",
|
||||
"test/generated-artifacts/ISablier.json",
|
||||
"test/generated-artifacts/IStaking.json",
|
||||
"test/generated-artifacts/IZrxTreasury.json",
|
||||
"test/generated-artifacts/ZrxTreasury.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
|
||||
"overrides": [],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"ignorePatterns": [
|
||||
"lib/**/*",
|
||||
"contracts/**/*",
|
||||
"generated-wrappers/**/*",
|
||||
"generated-artifacts/**/*",
|
||||
"test/generated-wrappers/**/*",
|
||||
"test/generated-artifacts/**/*"
|
||||
|
||||
],
|
||||
"rules": {}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user