Compare commits

..

48 Commits

Author SHA1 Message Date
Github Actions
d9a16ed1f9 Publish
- @0x/contracts-treasury@1.4.2
 - @0x/contracts-zero-ex@0.29.1
 - @0x/asset-swapper@16.29.1
 - @0x/migrations@8.1.8
 - @0x/protocol-utils@1.9.2
2021-10-04 19:01:10 +00:00
Github Actions
57a1120997 Updated CHANGELOGS & MD docs 2021-10-04 19:01:06 +00:00
Romain Butteaud
0945d4cef2 fix: removing Clipper custom integration (to add it later as a real PLP) (#335)
* fix: removing Clipper custom integration (to add it later as a real PLP)

* fix: update CHHANGELOG

* fix: keep Clipper as BridgeProtocols so we dont have to redeploy and comment this is not used

* fix: prettier
2021-10-04 11:39:07 -07:00
Github Actions
8f6f7ad453 Publish
- @0x/asset-swapper@16.29.0
2021-10-04 12:21:46 +00:00
Github Actions
bb4fad37fa Updated CHANGELOGS & MD docs 2021-10-04 12:21:43 +00:00
Kim Persson
d06daf2957 feat: initial integration of new router (#295)
* feat: integrate Rust router with asset-swapper WIP

* fix: produce outputFees in the format the Rust router expects

* fix: correct output fee calc and only use the rust router for sells

* fix: make sure numbers sent to the rust router are integers

* hack: try to debug why rust router output is being overestimated WIP

* refactor: clean up router debugging code

* fix: don't use negative output fees for sells

* feat: try VIP sources in isolation and compare with routing all sources

* fix: adjust for FQT overhead when choosing between VIP, all sources WIP

* fix: pass gasPrice to path_optimizer for EP overhead calculations

* feat: buy support with the Rust Router WIP

* chore: WIP commit trying to get buys working

* refactor: use samples instead of fills for the Rust router

* feat: add vip handling hack to sample based routing

* fix: revert to 200 samplings for rust router when using pure samples

* refactor: remove old hacky Path based Rust code, add back feature toggle

* fix: scale both fill output and adjustedOutput my same factor as input

* feat: initial plumbing for supporting RFQ/Limit orders

* fix: incorrect bump of input amount by one base unit before routing

* fix: add fake samples for rfq/limit orders to fulfill the 3 sample req

* fix pass rfq orders in the correct format to the rust router

* chore: remove debugging logs and clean up code & comments

* fix: use published version of @0x/neon-router

* hack: scale routed amounts to account for precision loss of number/f64

* refactor: clean up code and address initial review comments

* fix: only remove trailing 0 output samples before passing to the router

* refactor: consolidate eth to output token calc into ethToOutputAmount fn

* fix: interpolate input between samples on output amount instead of price

* fix: return no path when we have no samples, add sanity asserts

* refactor: fix interpolation comment wording

* fix: remove double adjusted source route input amount

* chore: update changelog for asset-swapper
2021-10-04 12:09:54 +02:00
Megan
34314960ef Updated docs for zero protocol fees (#337) 2021-10-01 13:04:25 +02:00
Github Actions
832ba737ec Publish
- @0x/contracts-treasury@1.4.1
 - @0x/contracts-zero-ex@0.29.0
 - @0x/asset-swapper@16.28.0
 - @0x/contract-artifacts@3.16.0
 - @0x/contract-wrappers@13.18.0
 - @0x/migrations@8.1.7
 - @0x/protocol-utils@1.9.1
2021-09-29 23:19:10 +00:00
Github Actions
6950fdbebb Updated CHANGELOGS & MD docs 2021-09-29 23:19:06 +00:00
mzhu25
1849b1bb9a Update ExchangeProxySwapQuoteConsumer for MultiplexV2 (#282)
* Update ExchangeProxySwapQuoteConsumer for MultiplexV2

* Move TransformERC20FeatureContract instance into private readonly
2021-09-29 16:02:13 -07:00
Megan
a883139220 feat: remove protocol fees for V4 in AssetSwapper WIP (#333)
* Set AssetSwapper protocol fee multiplier to zero

* Set market_operation_utils protocol fee multiplier to zero

* Updated CHANGELOG.json

* Removed whitespace in CHANGELOG.json

* Remove unnecessary timestamp in packages/asset-swapper/CHANGELOG.json

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>

* Updated  param for quote simulation test

* Updated quote simulation test

* fix failing tests

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-09-29 12:37:42 -04:00
Kevin Li
bb2c26cb94 fix(docs): pin Sphinx version to 3.x to generate CSS
Custom CSS seems to [have broken in Sphinx 4](https://github.com/sphinx-doc/sphinx/issues/2442).
Guessing readthedocs.io or their package repository upgraded the default
version of Sphinx to 4 and that caused our builds to start breaking.

They [recommend pinning dependencies](https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html#pinning-dependencies), let's do that here for Sphinx!
2021-09-15 18:18:44 -07:00
Github Actions
ba09a0b2bf Publish
- @0x/contracts-erc20@3.3.20
 - @0x/contracts-test-utils@5.4.11
 - @0x/contracts-treasury@1.4.0
 - @0x/contracts-utils@4.8.1
 - @0x/contracts-zero-ex@0.28.5
 - @0x/asset-swapper@16.27.4
 - @0x/contract-artifacts@3.15.1
 - @0x/contract-wrappers@13.17.7
 - @0x/migrations@8.1.6
 - @0x/protocol-utils@1.9.0
2021-09-15 12:58:17 +00:00
Github Actions
b2d54f0238 Updated CHANGELOGS & MD docs 2021-09-15 12:58:13 +00:00
Daniel Pyrathon
4a3096495b Mocked RFQ (#326) 2021-09-15 08:39:10 -04:00
Lawrence Forman
23f6e9e53c fix mixed @0x deps (#328)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-09-15 00:01:15 -04:00
Cece Z
d7dbc0576d Support cast vote by signature in ZrxTreasury (#297)
* Support cast vote by signature in ZrxTreasury

* Address comments and fix existing tests

* test that doesnt work

* test file format

* updates

* address some of the comments

* Remove unused const

* get rid of vote_factory

* unit test for castVoteBySignature p1

* unit test for castVoteBySignature p2

* Add version to domain, and one more test

* unit test for castVoteBySignature p3

* unit test for castVoteBySignature p4

* bump utils version

* remove debug code

* address some comments

* address more pr comments

* move Vote class to protocol-utils

* Address pr comments and update changelogs
2021-09-14 16:12:51 -04:00
Github Actions
15fb00e958 Publish
- @0x/asset-swapper@16.27.3
2021-09-14 19:04:09 +00:00
Github Actions
1d9295cc94 Updated CHANGELOGS & MD docs 2021-09-14 19:04:05 +00:00
Daniel Pyrathon
79f36cf6fb fix: Remove API key whitelist field (#323)
* Refactor integrator ID and add Prometheus metrics

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Added documentation and fixed some minor requests

* Added more metrics

* more docs

* lint fix

* added new Integrator ID addition

* refactor tests

* Refactor new types

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>
2021-09-14 14:43:02 -04:00
Github Actions
6ce4458a5d Publish
- @0x/asset-swapper@16.27.2
2021-09-14 17:13:46 +00:00
Github Actions
fad6e65c07 Updated CHANGELOGS & MD docs 2021-09-14 17:13:42 +00:00
Daniel Pyrathon
840c85373e fix: Refactor integrator ID and add Prometheus metrics (#322)
* Refactor integrator ID and add Prometheus metrics

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Update packages/asset-swapper/src/swap_quoter.ts

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>

* Added documentation and fixed some minor requests

* Added more metrics

* more docs

* lint fix

* added new Integrator ID addition

* refactor tests

Co-authored-by: David Walsh <5778036+rhinodavid@users.noreply.github.com>
2021-09-14 12:45:41 -04:00
Github Actions
0479bb5fe1 Publish
- @0x/contracts-erc20@3.3.19
 - @0x/contracts-treasury@1.3.5
 - @0x/contracts-utils@4.8.0
 - @0x/contracts-zero-ex@0.28.4
 - @0x/asset-swapper@16.27.1
 - @0x/migrations@8.1.5
2021-09-08 17:06:09 +00:00
Github Actions
9ecc31ed54 Updated CHANGELOGS & MD docs 2021-09-08 17:06:06 +00:00
Lawrence Forman
eb29abd36c Fix ApproximateBuys sampler to terminate if the buy amount is not met (#319)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-09-08 01:17:42 -04:00
Lawrence Forman
d202e01522 update package mainfests (#318)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-09-07 23:09:30 -04:00
Daniel Pyrathon
e838a6801b fix: Exclusive API keys for select integrations (#317)
* Initial commit of changes

* Added unit tests for filtering process

* linting

* Update packages/asset-swapper/src/utils/quote_requestor.ts

Co-authored-by: phil-ociraptor <philipliao@gmail.com>

* lint and refactor based on feedback

Co-authored-by: phil-ociraptor <philipliao@gmail.com>
2021-09-07 14:26:40 -04:00
Noah Khamliche
7439871aa0 Final Rebase 2021-09-01 17:07:16 -04:00
Noah Khamliche
317f2138c5 feat: fixed json 2021-09-01 17:07:16 -04:00
Noah Khamliche
e5834f1901 m: Fixed json formatting 2021-09-01 17:07:16 -04:00
Noah Khamliche
5063446f93 feat: Added FundRecoveryFeature to CHANGELOG.json 2021-09-01 17:07:16 -04:00
Noah Khamliche
0caf495a1a fixed prettier error with tests 2021-09-01 17:07:16 -04:00
Noah Khamliche
a20de0fc69 Added support for TestMintableERC20TokenContract instead of DummyERC20 2021-09-01 17:07:16 -04:00
Noah Khamliche
9aa0065d2d fixed package.json rebase bug 2021-09-01 17:07:16 -04:00
Noah Khamliche
c24855e627 fixed rebase issues 2021-09-01 17:07:16 -04:00
Noah Khamliche
6bb72dd775 ran yarn prettier to fix issues 2021-09-01 17:07:16 -04:00
Noah Khamliche
77d1ed257c removed unsued bal variable, and removed .only modifier on blockchain tests 2021-09-01 17:07:16 -04:00
Noah Khamliche
5d265360c4 fixed linting and EP transfering less than the total amount of ETH in the wallet 2021-09-01 17:07:16 -04:00
Noah Khamliche
c9097f6e8b fixed rebase issues 2021-09-01 17:07:16 -04:00
Noah Khamliche
d3df985a42 fixed michaels comments and finished off writing the test 2021-09-01 17:07:16 -04:00
Noah Khamliche
7267420874 fixed rebase issues 2021-09-01 17:07:16 -04:00
Noah Khamliche
17e81432f1 Fixed PR comments, now onto writing tests 2021-09-01 17:07:16 -04:00
Noah Khamliche
57c767c3b1 fixed package.json 2021-09-01 17:07:16 -04:00
Noah Khamliche
dbb1c88ad9 initial EpFundRecoveryFeature implementation without tests 2021-09-01 17:07:16 -04:00
Github Actions
254b850a8b Publish
- @0x/contracts-erc20@3.3.18
 - @0x/contracts-test-utils@5.4.10
 - @0x/contracts-treasury@1.3.4
 - @0x/contracts-utils@4.7.18
 - @0x/contracts-zero-ex@0.28.3
 - @0x/asset-swapper@16.27.0
 - @0x/contract-addresses@6.7.0
 - @0x/contract-wrappers@13.17.6
 - @0x/migrations@8.1.4
 - @0x/protocol-utils@1.8.4
2021-09-01 01:31:37 +00:00
Github Actions
e2242e5955 Updated CHANGELOGS & MD docs 2021-09-01 01:31:34 +00:00
Jacob Evans
7a0255fee7 feat: 🔺 (#312)
* feat: Avalanche

* Update Addresses for Avalanche deployment

* Cleanup

* Rebased development
2021-09-01 11:11:56 +10:00
87 changed files with 4408 additions and 1086 deletions

3
.gitignore vendored
View File

@@ -75,8 +75,9 @@ generated_docs/
TODO.md
# VSCode file
# IDE file
.vscode
.idea
# generated contract artifacts/
contracts/broker/generated-artifacts/

View File

@@ -1,4 +1,31 @@
[
{
"timestamp": 1631710679,
"version": "3.3.20",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631120757,
"version": "3.3.19",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1630459879,
"version": "3.3.18",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629353596,
"version": "3.3.17",

View File

@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.17",
"version": "3.3.20",
"engines": {
"node": ">=6.12"
},
@@ -51,18 +51,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.9",
"@0x/contracts-utils": "^4.7.17",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.11",
"@0x/contracts-utils": "^4.8.1",
"@0x/dev-utils": "^4.2.9",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -70,7 +70,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.5.0",
"ethereum-types": "^3.6.0",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
@@ -82,7 +82,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/base-contract": "^6.4.2",
"ethers": "~4.0.4"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1631710679,
"version": "5.4.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1630459879,
"version": "5.4.10",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629353596,
"version": "5.4.9",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.11 - _September 15, 2021_
* Dependencies updated
## v5.4.10 - _September 1, 2021_
* Dependencies updated
## v5.4.9 - _August 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.9",
"version": "5.4.11",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
"devDependencies": {
"@0x/sol-compiler": "^4.7.3",
"@0x/sol-compiler": "^4.7.5",
"@0x/tslint-config": "^4.1.4",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -42,20 +42,20 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/dev-utils": "^4.2.7",
"@0x/json-schemas": "^6.1.3",
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/order-utils": "^10.4.28",
"@0x/sol-coverage": "^4.0.37",
"@0x/sol-profiler": "^4.1.27",
"@0x/sol-trace": "^3.0.37",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/sol-coverage": "^4.0.39",
"@0x/sol-profiler": "^4.1.29",
"@0x/sol-trace": "^3.0.39",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",
@@ -67,7 +67,7 @@
"chai-bignumber": "^3.0.0",
"decimal.js": "^10.2.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.5.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4",
"js-combinatorics": "^0.5.3",

View File

@@ -1,4 +1,49 @@
[
{
"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",

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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

View File

@@ -136,8 +136,9 @@ interface IZrxTreasury {
returns (uint256 proposalId);
/// @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.
/// 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
@@ -150,6 +151,28 @@ interface IZrxTreasury {
)
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.

View File

@@ -34,11 +34,25 @@ contract ZrxTreasury is
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;
@@ -67,6 +81,15 @@ contract ZrxTreasury is
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)
)
);
}
// solhint-disable
@@ -105,7 +128,7 @@ contract ZrxTreasury is
/// 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
/// @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.
@@ -150,8 +173,9 @@ contract ZrxTreasury is
}
/// @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.
/// 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
@@ -165,43 +189,39 @@ contract ZrxTreasury is
public
override
{
if (proposalId >= proposalCount()) {
revert("castVote/INVALID_PROPOSAL_ID");
}
if (hasVoted[proposalId][msg.sender]) {
revert("castVote/ALREADY_VOTED");
}
return _castVote(msg.sender, proposalId, support, operatedPoolIds);
}
Proposal memory proposal = proposals[proposalId];
if (
proposal.voteEpoch != stakingProxy.currentEpoch() ||
_hasVoteEnded(proposal.voteEpoch)
) {
revert("castVote/VOTING_IS_CLOSED");
}
uint256 votingPower = getVotingPower(msg.sender, operatedPoolIds);
if (votingPower == 0) {
revert("castVote/NO_VOTING_POWER");
}
if (support) {
proposals[proposalId].votesFor = proposals[proposalId].votesFor
.safeAdd(votingPower);
hasVoted[proposalId][msg.sender] = true;
} else {
proposals[proposalId].votesAgainst = proposals[proposalId].votesAgainst
.safeAdd(votingPower);
hasVoted[proposalId][msg.sender] = true;
}
emit VoteCast(
msg.sender,
operatedPoolIds,
proposalId,
support,
votingPower
/// @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
@@ -373,4 +393,60 @@ contract ZrxTreasury is
.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;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.3.3",
"version": "1.4.2",
"engines": {
"node": ">=6.12"
},
@@ -46,14 +46,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/abi-gen": "^5.6.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.17",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-erc20": "^3.3.20",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-test-utils": "^5.4.9",
"@0x/sol-compiler": "^4.7.3",
"@0x/contracts-test-utils": "^5.4.11",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -72,14 +72,14 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.8.3",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"@0x/base-contract": "^6.4.2",
"@0x/protocol-utils": "^1.9.2",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10"
},
"publishConfig": {

View File

@@ -17,8 +17,9 @@ import {
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
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';
@@ -55,6 +56,8 @@ blockchainTests.resets('Treasury governance', env => {
let nonDefaultPoolId: string;
let poolOperator: string;
let delegator: string;
let relayer: string;
let delegatorPrivateKey: string;
let actions: ProposedAction[];
async function deployStakingAsync(): Promise<void> {
@@ -105,7 +108,10 @@ blockchainTests.resets('Treasury governance', env => {
}
before(async () => {
[admin, poolOperator, delegator] = await env.getAccountAddressesAsync();
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,
@@ -399,7 +405,7 @@ blockchainTests.resets('Treasury governance', env => {
expect(await treasury.proposalCount().callAsync()).to.bignumber.equal(1);
});
});
describe('castVote()', () => {
describe('castVote() and castVoteBySignature()', () => {
const VOTE_PROPOSAL_ID = new BigNumber(0);
const DELEGATOR_VOTING_POWER = new BigNumber(420);
@@ -418,17 +424,18 @@ blockchainTests.resets('Treasury governance', env => {
.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');
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');
return expect(tx).to.revertWith('_castVote/VOTING_IS_CLOSED');
});
it('Cannot vote after voting period ends', async () => {
await fastForwardToNextEpochAsync();
@@ -436,14 +443,14 @@ blockchainTests.resets('Treasury governance', env => {
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');
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');
return expect(tx).to.revertWith('_castVote/ALREADY_VOTED');
});
it('Can cast a valid vote', async () => {
await fastForwardToNextEpochAsync();
@@ -465,6 +472,109 @@ blockchainTests.resets('Treasury governance', env => {
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;
@@ -473,7 +583,7 @@ blockchainTests.resets('Treasury governance', env => {
let ongoingVoteProposalId: BigNumber;
before(async () => {
// OPerator has enough ZRX to create and pass a proposal
// Operator has enough ZRX to create and pass a proposal
await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: poolOperator });
await staking
.moveStake(
@@ -549,7 +659,7 @@ blockchainTests.resets('Treasury governance', env => {
});
it('Cannot execute before or after the execution epoch', async () => {
const tooEarly = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
expect(tooEarly).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
await expect(tooEarly).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
await fastForwardToNextEpochAsync();
// Proposal 0 is executable here
await fastForwardToNextEpochAsync();

View File

@@ -1,4 +1,32 @@
[
{
"timestamp": 1631710679,
"version": "4.8.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "4.8.0",
"changes": [
{
"note": "Added FundRecoveryFeature to the 0x EP",
"pr": 306
}
],
"timestamp": 1631120757
},
{
"timestamp": 1630459879,
"version": "4.7.18",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629353596,
"version": "4.7.17",

View File

@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.8.1 - _September 15, 2021_
* Dependencies updated
## v4.8.0 - _September 8, 2021_
* Added FundRecoveryFeature to the 0x EP (#306)
## v4.7.18 - _September 1, 2021_
* Dependencies updated
## v4.7.17 - _August 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.7.17",
"version": "4.8.1",
"engines": {
"node": ">=6.12"
},
@@ -50,15 +50,15 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.9",
"@0x/dev-utils": "^4.2.7",
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.11",
"@0x/dev-utils": "^4.2.9",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.7.3",
"@0x/sol-compiler": "^4.7.5",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/types": "^3.3.4",
"@0x/web3-wrapper": "^7.6.0",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -79,11 +79,11 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/base-contract": "^6.4.2",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"bn.js": "^4.11.8",
"ethereum-types": "^3.5.0"
"ethereum-types": "^3.6.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,50 @@
[
{
"timestamp": 1633374058,
"version": "0.29.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.29.0",
"changes": [
{
"note": "Export TransformERC20FeatureContract",
"pr": 282
}
],
"timestamp": 1632957537
},
{
"timestamp": 1631710679,
"version": "0.28.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631120757,
"version": "0.28.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1630459879,
"version": "0.28.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.28.2",
"changes": [

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.29.1 - _October 4, 2021_
* Dependencies updated
## v0.29.0 - _September 29, 2021_
* Export TransformERC20FeatureContract (#282)
## v0.28.5 - _September 15, 2021_
* Dependencies updated
## v0.28.4 - _September 8, 2021_
* Dependencies updated
## v0.28.3 - _September 1, 2021_
* Dependencies updated
## v0.28.2 - _August 19, 2021_
* Add ethers as an explicit dependency (#310)

View File

@@ -33,6 +33,7 @@ import "./features/interfaces/INativeOrdersFeature.sol";
import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
import "./features/interfaces/IMultiplexFeature.sol";
import "./features/interfaces/IOtcOrdersFeature.sol";
import "./features/interfaces/IFundRecoveryFeature.sol";
/// @dev Interface for a fully featured Exchange Proxy.
@@ -48,7 +49,8 @@ interface IZeroEx is
INativeOrdersFeature,
IBatchFillNativeOrdersFeature,
IMultiplexFeature,
IOtcOrdersFeature
IOtcOrdersFeature,
IFundRecoveryFeature
{
// solhint-disable state-visibility

View File

@@ -0,0 +1,66 @@
// 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.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../migrations/LibMigrate.sol";
import "../fixins/FixinCommon.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IFundRecoveryFeature.sol";
import "../transformers/LibERC20Transformer.sol";
contract FundRecoveryFeature is
IFeature,
IFundRecoveryFeature,
FixinCommon
{
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "FundRecoveryFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.
/// @return success `LibMigrate.SUCCESS` on success.
function migrate()
external
returns (bytes4 success)
{
_registerFeatureFunction(this.transferTrappedTokensTo.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Recovers ERC20 tokens or ETH from the 0x Exchange Proxy contract
/// @param erc20 ERC20 Token Address. (You can also pass in `0xeeeee...` to indicate ETH)
/// @param amountOut Amount of tokens to withdraw.
/// @param recipientWallet Recipient wallet address.
function transferTrappedTokensTo(
IERC20TokenV06 erc20,
uint256 amountOut,
address payable recipientWallet
)
external
override
onlyOwner
{
if(amountOut == uint256(-1)) {
amountOut = LibERC20Transformer.getTokenBalanceOf(erc20, address(this));
}
LibERC20Transformer.transformerTransfer(erc20, recipientWallet, amountOut);
}
}

View File

@@ -0,0 +1,35 @@
// 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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
/// @dev Exchange Proxy Recovery Functions
interface IFundRecoveryFeature {
/// @dev calledFrom FundRecoveryFeature.transferTrappedTokensTo() This will be delegatecalled
/// in the context of the Exchange Proxy instance being used.
/// @param erc20 ERC20 Token Address.
/// @param amountOut Amount of tokens to withdraw.
/// @param recipientWallet Recipient wallet address.
function transferTrappedTokensTo(
IERC20TokenV06 erc20,
uint256 amountOut,
address payable recipientWallet
)
external;
}

View File

@@ -25,7 +25,6 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinClipper.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
@@ -51,7 +50,6 @@ contract BridgeAdapter is
MixinBalancer,
MixinBalancerV2,
MixinBancor,
MixinClipper,
MixinCoFiX,
MixinCurve,
MixinCurveV2,
@@ -77,7 +75,6 @@ contract BridgeAdapter is
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinClipper(weth)
MixinCoFiX()
MixinCurve(weth)
MixinCurveV2()
@@ -248,13 +245,6 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.CLIPPER) {
boughtAmount = _tradeClipper(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,

View File

@@ -49,5 +49,5 @@ library BridgeProtocols {
uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
}

View File

@@ -1,148 +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.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../IBridgeAdapter.sol";
import "../../../vendor/ILiquidityProvider.sol";
contract MixinClipper {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
function _tradeClipper(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// We can only use ETH with Clipper, no WETH available
(ILiquidityProvider clipper, bytes memory auxiliaryData) =
abi.decode(bridgeData, (ILiquidityProvider, bytes));
if (sellToken == WETH) {
boughtAmount = _executeSellEthForToken(
clipper,
buyToken,
sellAmount,
auxiliaryData
);
} else if (buyToken == WETH) {
boughtAmount = _executeSellTokenForEth(
clipper,
sellToken,
sellAmount,
auxiliaryData
);
} else {
boughtAmount = _executeSellTokenForToken(
clipper,
sellToken,
buyToken,
sellAmount,
auxiliaryData
);
}
return boughtAmount;
}
function _executeSellEthForToken(
ILiquidityProvider clipper,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Clipper requires ETH and doesn't support WETH
WETH.withdraw(sellAmount);
boughtAmount = clipper.sellEthForToken{ value: sellAmount }(
buyToken,
address(this),
1,
auxiliaryData
);
}
function _executeSellTokenForEth(
ILiquidityProvider clipper,
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Optimization: We can transfer the tokens into clipper rather than
// have an allowance updated
sellToken.compatTransfer(address(clipper), sellAmount);
boughtAmount = clipper.sellTokenForEth(
sellToken,
payable(address(this)),
1,
auxiliaryData
);
// we want WETH for possible future trades
WETH.deposit{ value: boughtAmount }();
}
function _executeSellTokenForToken(
ILiquidityProvider clipper,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Optimization: We can transfer the tokens into clipper rather than
// have an allowance updated
sellToken.compatTransfer(address(clipper), sellAmount);
boughtAmount = clipper.sellTokenForToken(
sellToken,
buyToken,
address(this),
1,
auxiliaryData
);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.28.2",
"version": "0.29.1",
"engines": {
"node": ">=6.12"
},
@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinClipper|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@@ -55,14 +55,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/contracts-erc20": "^3.3.17",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.9",
"@0x/dev-utils": "^4.2.7",
"@0x/abi-gen": "^5.6.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/contracts-erc20": "^3.3.20",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.11",
"@0x/dev-utils": "^4.2.9",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.7.3",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -82,14 +82,14 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.8.3",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"@0x/base-contract": "^6.4.2",
"@0x/protocol-utils": "^1.9.2",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4"
},

View File

@@ -47,6 +47,7 @@ export {
MultiplexFeatureContract,
PayTakerTransformerContract,
PositiveSlippageFeeTransformerContract,
TransformERC20FeatureContract,
WethTransformerContract,
ZeroExContract,
} from './wrappers';

View File

@@ -21,6 +21,7 @@ import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentran
import * as FixinTokenSpender from '../test/generated-artifacts/FixinTokenSpender.json';
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
import * as FundRecoveryFeature from '../test/generated-artifacts/FundRecoveryFeature.json';
import * as IBatchFillNativeOrdersFeature from '../test/generated-artifacts/IBatchFillNativeOrdersFeature.json';
import * as IBootstrapFeature from '../test/generated-artifacts/IBootstrapFeature.json';
import * as IBridgeAdapter from '../test/generated-artifacts/IBridgeAdapter.json';
@@ -28,6 +29,7 @@ import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
import * as IERC20Transformer from '../test/generated-artifacts/IERC20Transformer.json';
import * as IFeature from '../test/generated-artifacts/IFeature.json';
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
import * as IFundRecoveryFeature from '../test/generated-artifacts/IFundRecoveryFeature.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
import * as ILiquidityProviderFeature from '../test/generated-artifacts/ILiquidityProviderFeature.json';
import * as ILiquidityProviderSandbox from '../test/generated-artifacts/ILiquidityProviderSandbox.json';
@@ -82,7 +84,6 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinClipper from '../test/generated-artifacts/MixinClipper.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
@@ -198,6 +199,7 @@ export const artifacts = {
TransformerDeployer: TransformerDeployer as ContractArtifact,
BatchFillNativeOrdersFeature: BatchFillNativeOrdersFeature as ContractArtifact,
BootstrapFeature: BootstrapFeature as ContractArtifact,
FundRecoveryFeature: FundRecoveryFeature as ContractArtifact,
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
@@ -211,6 +213,7 @@ export const artifacts = {
IBatchFillNativeOrdersFeature: IBatchFillNativeOrdersFeature as ContractArtifact,
IBootstrapFeature: IBootstrapFeature as ContractArtifact,
IFeature: IFeature as ContractArtifact,
IFundRecoveryFeature: IFundRecoveryFeature as ContractArtifact,
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
IMetaTransactionsFeature: IMetaTransactionsFeature as ContractArtifact,
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
@@ -272,7 +275,6 @@ export const artifacts = {
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinClipper: MixinClipper as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,

View File

@@ -0,0 +1,96 @@
import { blockchainTests, constants, expect, randomAddress } from '@0x/contracts-test-utils';
import { BigNumber, OwnableRevertErrors } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { IOwnableFeatureContract, IZeroExContract } from '../../src/wrappers';
import { artifacts } from '../artifacts';
import { FundRecoveryFeatureContract } from '../generated-wrappers/fund_recovery_feature';
import { abis } from '../utils/abis';
import { fullMigrateAsync } from '../utils/migration';
import { TestMintableERC20TokenContract } from '../wrappers';
blockchainTests('FundRecovery', async env => {
let owner: string;
let zeroEx: IZeroExContract;
let token: TestMintableERC20TokenContract;
before(async () => {
const INITIAL_ERC20_BALANCE = Web3Wrapper.toBaseUnitAmount(new BigNumber(10000), 18);
[owner] = await env.getAccountAddressesAsync();
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {});
token = await TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestMintableERC20Token,
env.provider,
env.txDefaults,
{},
);
await token.mint(zeroEx.address, INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync();
const featureImpl = await FundRecoveryFeatureContract.deployFrom0xArtifactAsync(
artifacts.FundRecoveryFeature,
env.provider,
env.txDefaults,
artifacts,
);
await new IOwnableFeatureContract(zeroEx.address, env.provider, env.txDefaults, abis)
.migrate(featureImpl.address, featureImpl.migrate().getABIEncodedTransactionData(), owner)
.awaitTransactionSuccessAsync({ from: owner });
});
blockchainTests.resets('Should delegatecall `transferTrappedTokensTo` from the exchange proxy', () => {
const ETH_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
const recipientAddress = randomAddress();
it('Tranfers an arbitrary ERC-20 Token', async () => {
const amountOut = Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18);
await zeroEx
.transferTrappedTokensTo(token.address, amountOut, recipientAddress)
.awaitTransactionSuccessAsync({ from: owner });
const recipientAddressBalanceAferTransfer = await token.balanceOf(recipientAddress).callAsync();
return expect(recipientAddressBalanceAferTransfer).to.bignumber.equal(amountOut);
});
it('Amount -1 transfers entire balance of ERC-20', async () => {
const balanceOwner = await token.balanceOf(zeroEx.address).callAsync();
await zeroEx
.transferTrappedTokensTo(token.address, constants.MAX_UINT256, recipientAddress)
.awaitTransactionSuccessAsync({ from: owner });
const recipientAddressBalanceAferTransfer = await token.balanceOf(recipientAddress).callAsync();
return expect(recipientAddressBalanceAferTransfer).to.bignumber.equal(balanceOwner);
});
it('Amount -1 transfers entire balance of ETH', async () => {
const amountOut = new BigNumber(20);
await env.web3Wrapper.awaitTransactionMinedAsync(
await env.web3Wrapper.sendTransactionAsync({
from: owner,
to: zeroEx.address,
value: amountOut,
}),
);
const balanceOwner = await env.web3Wrapper.getBalanceInWeiAsync(zeroEx.address);
await zeroEx
.transferTrappedTokensTo(ETH_TOKEN_ADDRESS, constants.MAX_UINT256, recipientAddress)
.awaitTransactionSuccessAsync({ from: owner });
const recipientAddressBalanceAferTransfer = await env.web3Wrapper.getBalanceInWeiAsync(recipientAddress);
return expect(recipientAddressBalanceAferTransfer).to.bignumber.equal(balanceOwner);
});
it('Transfers ETH ', async () => {
const amountOut = new BigNumber(20);
await env.web3Wrapper.awaitTransactionMinedAsync(
await env.web3Wrapper.sendTransactionAsync({
from: owner,
to: zeroEx.address,
value: amountOut,
}),
);
await zeroEx
.transferTrappedTokensTo(ETH_TOKEN_ADDRESS, amountOut.minus(1), recipientAddress)
.awaitTransactionSuccessAsync({ from: owner });
const recipientAddressBalance = await env.web3Wrapper.getBalanceInWeiAsync(recipientAddress);
return expect(recipientAddressBalance).to.bignumber.be.equal(amountOut.minus(1));
});
it('Feature `transferTrappedTokensTo` can only be called by owner', async () => {
const notOwner = randomAddress();
return expect(
zeroEx
.transferTrappedTokensTo(ETH_TOKEN_ADDRESS, constants.MAX_UINT256, recipientAddress)
.awaitTransactionSuccessAsync({ from: notOwner }),
).to.revertWith(new OwnableRevertErrors.OnlyOwnerError(notOwner, owner));
});
});
});

View File

@@ -19,6 +19,7 @@ export * from '../test/generated-wrappers/fixin_reentrancy_guard';
export * from '../test/generated-wrappers/fixin_token_spender';
export * from '../test/generated-wrappers/flash_wallet';
export * from '../test/generated-wrappers/full_migration';
export * from '../test/generated-wrappers/fund_recovery_feature';
export * from '../test/generated-wrappers/i_batch_fill_native_orders_feature';
export * from '../test/generated-wrappers/i_bootstrap_feature';
export * from '../test/generated-wrappers/i_bridge_adapter';
@@ -26,6 +27,7 @@ export * from '../test/generated-wrappers/i_erc20_bridge';
export * from '../test/generated-wrappers/i_erc20_transformer';
export * from '../test/generated-wrappers/i_feature';
export * from '../test/generated-wrappers/i_flash_wallet';
export * from '../test/generated-wrappers/i_fund_recovery_feature';
export * from '../test/generated-wrappers/i_liquidity_provider';
export * from '../test/generated-wrappers/i_liquidity_provider_feature';
export * from '../test/generated-wrappers/i_liquidity_provider_sandbox';
@@ -80,7 +82,6 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_clipper';
export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';

View File

@@ -52,6 +52,7 @@
"test/generated-artifacts/FixinTokenSpender.json",
"test/generated-artifacts/FlashWallet.json",
"test/generated-artifacts/FullMigration.json",
"test/generated-artifacts/FundRecoveryFeature.json",
"test/generated-artifacts/IBatchFillNativeOrdersFeature.json",
"test/generated-artifacts/IBootstrapFeature.json",
"test/generated-artifacts/IBridgeAdapter.json",
@@ -59,6 +60,7 @@
"test/generated-artifacts/IERC20Transformer.json",
"test/generated-artifacts/IFeature.json",
"test/generated-artifacts/IFlashWallet.json",
"test/generated-artifacts/IFundRecoveryFeature.json",
"test/generated-artifacts/ILiquidityProvider.json",
"test/generated-artifacts/ILiquidityProviderFeature.json",
"test/generated-artifacts/ILiquidityProviderSandbox.json",
@@ -113,7 +115,6 @@
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinClipper.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",

View File

@@ -3,10 +3,14 @@ Protocol Fees
###############################
An ETH protocol fee is paid by the Taker each time a `Limit Order <./orders.html#limit-orders>`_ is `filled <./functions.html>`_.
The fee is proportional to the gas cost of filling an order and scales linearly with gas price. The cost is currently ``70k * tx.gasprice``.
The fee is proportional to the gas cost of filling an order and scales linearly with gas price. The cost is currently ``0 * tx.gasprice``.
At the end of every Staking Epoch, these fees are aggregated and distributed to the makers as a liquidity reward: the reward is proportional to the maker's collected fees and staked ZRX relative to other makers.
To learn more about protocol fees and liquidity incentives, see the `Official Spec <https://github.com/0xProject/0x-protocol-specification/blob/master/staking/staking-specification.md>`_.
.. note::
As of September 29, 2021, protocol fees have been removed for all order types in both Exchange V4 and V3 in accordance with `ZEIP-91 <https://0x.org/zrx/vote/zeip-91>`_.
.. note::
`RFQ Orders <./orders.html#rfq-orders>`_ are introduced in Exchange V4, and there is currently no protocol fee for filling this type of order.

View File

@@ -1,2 +1,3 @@
six
sphinx-markdown-tables
sphinx==3.5.4
sphinx-markdown-tables

View File

@@ -46,12 +46,12 @@
"test:generate_docs:circleci": "for i in ${npm_package_config_packagesWithDocPages}; do yarn generate_doc --package $i || break -1; done;",
"bundlewatch": "bundlewatch",
"lint": "wsrun --fast-exit --parallel --exclude-missing -p $PKG -c lint",
"upgrade_org_deps": "node node_modules/@0x/monorepo-scripts/lib/upgrade_deps.js -p '@0x|ethereum-types'",
"upgrade_org_deps": "node node_modules/@0x/monorepo-scripts/lib/upgrade_deps.js -p '@0x/|ethereum-types'",
"upgrade_deps": "node node_modules/@0x/monorepo-scripts/lib/upgrade_deps.js",
"verdaccio": "docker run --rm -i -p 4873:4873 0xorg/verdaccio"
},
"config": {
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
"nonContractPackages": "@0x/migrations @0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
"ignoreTestsForPackages": "",
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",

View File

@@ -1,4 +1,90 @@
[
{
"version": "16.29.1",
"changes": [
{
"note": "Remove `Clipper` as a custom liquidity source",
"pr": 335
}
],
"timestamp": 1633374058
},
{
"version": "16.29.0",
"changes": [
{
"note": "Initial integration of neon-router (behind feature flag)",
"pr": 295
}
],
"timestamp": 1633350101
},
{
"version": "16.28.0",
"changes": [
{
"note": "Update ExchangeProxySwapQuoteConsumer for Multiplex V2 and friends",
"pr": 282
}
],
"timestamp": 1632957537
},
{
"version": "16.27.5",
"changes": [
{
"note": "Remove protocol fees by setting `PROTOCOL_FEE_MULTIPLIER` to 0",
"pr": 333
}
]
},
{
"timestamp": 1631710679,
"version": "16.27.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631646242,
"version": "16.27.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631639620,
"version": "16.27.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "16.27.1",
"changes": [
{
"note": "Fix ApproximateBuys sampler to terminate if the buy amount is not met",
"pr": 319
}
],
"timestamp": 1631120757
},
{
"version": "16.27.0",
"changes": [
{
"note": "Avalanche deployment",
"pr": 312
}
],
"timestamp": 1630459879
},
{
"version": "16.26.2",
"changes": [

View File

@@ -5,6 +5,42 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v16.29.1 - _October 4, 2021_
* Remove `Clipper` as a custom liquidity source (#335)
## v16.29.0 - _October 4, 2021_
* Initial integration of neon-router (behind feature flag) (#295)
## v16.28.0 - _September 29, 2021_
* Update ExchangeProxySwapQuoteConsumer for Multiplex V2 and friends (#282)
## v16.27.5 - _Invalid date_
* Remove protocol fees by setting `PROTOCOL_FEE_MULTIPLIER` to 0 (#333)
## v16.27.4 - _September 15, 2021_
* Dependencies updated
## v16.27.3 - _September 14, 2021_
* Dependencies updated
## v16.27.2 - _September 14, 2021_
* Dependencies updated
## v16.27.1 - _September 8, 2021_
* Fix ApproximateBuys sampler to terminate if the buy amount is not met (#319)
## v16.27.0 - _September 1, 2021_
* Avalanche deployment (#312)
## v16.26.2 - _August 31, 2021_
* chore: Curve new pools (CVX-CRX, MIM, atricrypto3)

View File

@@ -77,6 +77,7 @@ contract ApproximateBuys {
}
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
uint256 eps = 0;
for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
sellAmount = _safeGetPartialAmountCeil(
@@ -108,7 +109,7 @@ contract ApproximateBuys {
buyAmount = _buyAmount;
// If we've reached our goal, exit early
if (buyAmount >= makerTokenAmounts[i]) {
uint256 eps =
eps =
(buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
makerTokenAmounts[i];
if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
@@ -116,6 +117,9 @@ contract ApproximateBuys {
}
}
}
if (eps == 0 || eps > APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
break;
}
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
// if we hit a max iteration limit
// We scale the sell amount to get the approximate target

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.26.2",
"version": "16.29.1",
"engines": {
"node": ">=6.12"
},
@@ -58,20 +58,21 @@
"registry": "git@github.com:0xProject/gitpkg-registry.git"
},
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/contract-wrappers": "^13.17.5",
"@0x/contracts-erc20": "^3.3.17",
"@0x/contracts-zero-ex": "^0.27.0",
"@0x/dev-utils": "^4.2.7",
"@0x/json-schemas": "^6.1.3",
"@0x/protocol-utils": "^1.8.3",
"@0x/quote-server": "^6.0.2",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/contract-wrappers": "^13.18.0",
"@0x/contracts-erc20": "^3.3.20",
"@0x/contracts-zero-ex": "^0.29.1",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/neon-router": "^0.1.3",
"@0x/protocol-utils": "^1.9.2",
"@0x/quote-server": "^6.0.6",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"@balancer-labs/sor": "0.3.2",
"@bancor/sdk": "0.2.9",
"@ethersproject/abi": "^5.0.1",
@@ -83,7 +84,7 @@
"axios-mock-adapter": "^1.19.0",
"cream-sor": "^0.3.3",
"decimal.js": "^10.2.0",
"ethereum-types": "^3.5.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"fast-abi": "^0.0.2",
"graphql": "^15.4.0",
@@ -92,20 +93,20 @@
"lodash": "^4.17.11"
},
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-libs": "^4.3.37",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.9",
"@0x/contracts-utils": "^4.7.17",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.11",
"@0x/contracts-utils": "^4.8.1",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.1.3",
"@0x/sol-compiler": "^4.7.3",
"@0x/subproviders": "^6.5.3",
"@0x/migrations": "^8.1.8",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/types": "^3.3.4",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",

View File

@@ -38,7 +38,7 @@ const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
// 6 seconds polling interval
const PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS = 6000;
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(70000);
const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
// default 50% buffer for selecting native orders to be aggregated with other sources
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
@@ -50,7 +50,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
samplerGasLimit: 500e6,
ethGasStationUrl: ETH_GAS_STATION_API_URL,
rfqt: {
takerApiKeyWhitelist: [],
integratorsWhitelist: [],
makerAssetOfferings: {},
txOriginBlacklist: new Set(),
},

View File

@@ -88,6 +88,7 @@ export {
ExchangeProxyContractOpts,
ExchangeProxyRefundReceiver,
GetExtensionContractTypeOpts,
Integrator,
LogFunction,
MarketBuySwapQuote,
MarketOperation,

View File

@@ -1,6 +1,6 @@
import { ChainId, ContractAddresses } from '@0x/contract-addresses';
import { IZeroExContract, WETH9Contract } from '@0x/contract-wrappers';
import { MultiplexFeatureContract } from '@0x/contracts-zero-ex';
import { IZeroExContract } from '@0x/contract-wrappers';
import { TransformERC20FeatureContract } from '@0x/contracts-zero-ex';
import {
encodeAffiliateFeeTransformerData,
encodeCurveLiquidityProviderData,
@@ -51,6 +51,7 @@ import {
import {
multiplexPlpEncoder,
multiplexRfqEncoder,
MultiplexSubcall,
multiplexTransformERC20Encoder,
multiplexUniswapEncoder,
} from './multiplex_encoders';
@@ -82,7 +83,6 @@ const FAKE_PROVIDER: any = {
return;
},
};
const DUMMY_WETH_CONTRACT = new WETH9Contract(NULL_ADDRESS, FAKE_PROVIDER);
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly chainId: ChainId;
@@ -95,7 +95,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
private readonly _exchangeProxy: IZeroExContract;
private readonly _multiplex: MultiplexFeatureContract;
private readonly _transformERC20Feature: TransformERC20FeatureContract;
constructor(public readonly contractAddresses: ContractAddresses, options: Partial<SwapQuoteConsumerOpts> = {}) {
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
@@ -103,7 +103,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId = chainId;
this.contractAddresses = contractAddresses;
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
this._multiplex = new MultiplexFeatureContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
this._transformERC20Feature = new TransformERC20FeatureContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
this.transformerNonces = {
wethTransformer: findTransformerNonce(
contractAddresses.transformers.wethTransformer,
@@ -338,7 +338,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata({ ...quote, orders: slippedOrders }),
calldataHexString: this._encodeMultiplexBatchFillCalldata(
{ ...quote, orders: slippedOrders },
optsWithDefaults,
),
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
@@ -473,17 +476,27 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}
}
// Return any unspent sell tokens.
const payTakerTokens = [sellToken];
// Return any unspent intermediate tokens for two-hop swaps.
if (quote.isTwoHop) {
payTakerTokens.push(intermediateToken);
}
// Return any unspent ETH. If ETH is the buy token, it will
// be returned in TransformERC20Feature rather than PayTakerTransformer.
if (!isToETH) {
payTakerTokens.push(ETH_TOKEN_ADDRESS);
}
// The final transformer will send all funds to the taker.
transforms.push({
deploymentNonce: this.transformerNonces.payTakerTransformer,
data: encodePayTakerTransformerData({
tokens: [sellToken, buyToken, ETH_TOKEN_ADDRESS].concat(quote.isTwoHop ? intermediateToken : []),
tokens: payTakerTokens,
amounts: [],
}),
});
const calldataHexString = this._exchangeProxy
.transformERC20(
const calldataHexString = this._transformERC20Feature
.transformERC20Staging(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
shouldSellEntireBalance ? MAX_UINT256 : sellAmount,
@@ -509,8 +522,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
throw new Error('Execution not supported for Exchange Proxy quotes');
}
private _encodeMultiplexBatchFillCalldata(quote: SwapQuote): string {
const wrappedBatchCalls = [];
private _encodeMultiplexBatchFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string {
const subcalls = [];
for_loop: for (const [i, order] of quote.orders.entries()) {
switch_statement: switch (order.source) {
case ERC20BridgeSource.Native:
@@ -519,8 +532,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// before calling this function.
throw new Error('Multiplex batch fill only supported for RFQ native orders');
}
wrappedBatchCalls.push({
selector: this._exchangeProxy.getSelector('_fillRfqOrder'),
subcalls.push({
id: MultiplexSubcall.Rfq,
sellAmount: order.takerAmount,
data: multiplexRfqEncoder.encode({
order: order.fillData.order,
@@ -530,8 +543,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
break switch_statement;
case ERC20BridgeSource.UniswapV2:
case ERC20BridgeSource.SushiSwap:
wrappedBatchCalls.push({
selector: this._multiplex.getSelector('_sellToUniswap'),
subcalls.push({
id: MultiplexSubcall.UniswapV2,
sellAmount: order.takerAmount,
data: multiplexUniswapEncoder.encode({
tokens: (order.fillData as UniswapV2FillData).tokenAddressPath,
@@ -540,8 +553,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
});
break switch_statement;
case ERC20BridgeSource.LiquidityProvider:
wrappedBatchCalls.push({
selector: this._multiplex.getSelector('_sellToLiquidityProvider'),
subcalls.push({
id: MultiplexSubcall.LiquidityProvider,
sellAmount: order.takerAmount,
data: multiplexPlpEncoder.encode({
provider: (order.fillData as LiquidityProviderFillData).poolAddress,
@@ -551,8 +564,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
break switch_statement;
case ERC20BridgeSource.UniswapV3:
const fillData = (order as OptimizedMarketBridgeOrder<FinalUniswapV3FillData>).fillData;
wrappedBatchCalls.push({
selector: this._exchangeProxy.getSelector('sellTokenForTokenToUniswapV3'),
subcalls.push({
id: MultiplexSubcall.UniswapV3,
sellAmount: order.takerAmount,
data: fillData.uniswapPath,
});
@@ -571,54 +584,59 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
{
deploymentNonce: this.transformerNonces.payTakerTransformer,
data: encodePayTakerTransformerData({
tokens: [quote.takerToken, quote.makerToken],
tokens: [quote.takerToken],
amounts: [],
}),
},
];
wrappedBatchCalls.push({
selector: this._exchangeProxy.getSelector('_transformERC20'),
subcalls.push({
id: MultiplexSubcall.TransformERC20,
sellAmount: BigNumber.sum(...quote.orders.slice(i).map(o => o.takerAmount)),
data: multiplexTransformERC20Encoder.encode({
transformations,
ethValue: constants.ZERO_AMOUNT,
}),
});
break for_loop;
}
}
return this._exchangeProxy
.batchFill(
{
inputToken: quote.takerToken,
outputToken: quote.makerToken,
sellAmount: quote.worstCaseQuoteInfo.totalTakerAmount,
calls: wrappedBatchCalls,
},
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
if (opts.isFromETH) {
return this._exchangeProxy
.multiplexBatchSellEthForToken(quote.makerToken, subcalls, quote.worstCaseQuoteInfo.makerAmount)
.getABIEncodedTransactionData();
} else if (opts.isToETH) {
return this._exchangeProxy
.multiplexBatchSellTokenForEth(
quote.takerToken,
subcalls,
quote.worstCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
} else {
return this._exchangeProxy
.multiplexBatchSellTokenForToken(
quote.takerToken,
quote.makerToken,
subcalls,
quote.worstCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
}
}
private _encodeMultiplexMultiHopFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string {
const wrappedMultiHopCalls = [];
const tokens: string[] = [];
if (opts.isFromETH) {
wrappedMultiHopCalls.push({
selector: DUMMY_WETH_CONTRACT.getSelector('deposit'),
data: NULL_BYTES,
});
tokens.push(ETH_TOKEN_ADDRESS);
}
const subcalls = [];
const [firstHopOrder, secondHopOrder] = quote.orders;
const intermediateToken = firstHopOrder.makerToken;
tokens.push(quote.takerToken, intermediateToken, quote.makerToken);
const tokens = [quote.takerToken, intermediateToken, quote.makerToken];
for (const order of [firstHopOrder, secondHopOrder]) {
switch (order.source) {
case ERC20BridgeSource.UniswapV2:
case ERC20BridgeSource.SushiSwap:
wrappedMultiHopCalls.push({
selector: this._multiplex.getSelector('_sellToUniswap'),
subcalls.push({
id: MultiplexSubcall.UniswapV2,
data: multiplexUniswapEncoder.encode({
tokens: (order.fillData as UniswapV2FillData).tokenAddressPath,
isSushi: order.source === ERC20BridgeSource.SushiSwap,
@@ -626,39 +644,49 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
});
break;
case ERC20BridgeSource.LiquidityProvider:
wrappedMultiHopCalls.push({
selector: this._multiplex.getSelector('_sellToLiquidityProvider'),
subcalls.push({
id: MultiplexSubcall.LiquidityProvider,
data: multiplexPlpEncoder.encode({
provider: (order.fillData as LiquidityProviderFillData).poolAddress,
auxiliaryData: NULL_BYTES,
}),
});
break;
case ERC20BridgeSource.UniswapV3:
subcalls.push({
id: MultiplexSubcall.UniswapV3,
data: (order.fillData as FinalUniswapV3FillData).uniswapPath,
});
break;
default:
// Note: we'll need to redeploy TransformERC20Feature before we can
// use other sources
// Should never happen because we check `isMultiplexMultiHopFillCompatible`
// before calling this function.
throw new Error(`Multiplex multi-hop unsupported source: ${order.source}`);
}
}
if (opts.isToETH) {
wrappedMultiHopCalls.push({
selector: DUMMY_WETH_CONTRACT.getSelector('withdraw'),
data: NULL_BYTES,
});
tokens.push(ETH_TOKEN_ADDRESS);
}
return this._exchangeProxy
.multiHopFill(
{
if (opts.isFromETH) {
return this._exchangeProxy
.multiplexMultiHopSellEthForToken(tokens, subcalls, quote.worstCaseQuoteInfo.makerAmount)
.getABIEncodedTransactionData();
} else if (opts.isToETH) {
return this._exchangeProxy
.multiplexMultiHopSellTokenForEth(
tokens,
sellAmount: quote.worstCaseQuoteInfo.totalTakerAmount,
calls: wrappedMultiHopCalls,
},
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
subcalls,
quote.worstCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
} else {
return this._exchangeProxy
.multiplexMultiHopSellTokenForToken(
tokens,
subcalls,
quote.worstCaseQuoteInfo.totalTakerAmount,
quote.worstCaseQuoteInfo.makerAmount,
)
.getABIEncodedTransactionData();
}
}
}

View File

@@ -1,6 +1,17 @@
import { RfqOrder, SIGNATURE_ABI } from '@0x/protocol-utils';
import { AbiEncoder } from '@0x/utils';
export enum MultiplexSubcall {
Invalid,
Rfq,
Otc,
UniswapV2,
UniswapV3,
LiquidityProvider,
TransformERC20,
BatchSell,
MultiHopSell,
}
export const multiplexTransformERC20Encoder = AbiEncoder.create([
{
name: 'transformations',
@@ -10,7 +21,6 @@ export const multiplexTransformERC20Encoder = AbiEncoder.create([
{ name: 'data', type: 'bytes' },
],
},
{ name: 'ethValue', type: 'uint256' },
]);
export const multiplexRfqEncoder = AbiEncoder.create([
{ name: 'order', type: 'tuple', components: RfqOrder.STRUCT_ABI },

View File

@@ -32,10 +32,6 @@ export function isMultiplexBatchFillCompatible(quote: SwapQuote, opts: ExchangeP
if (quote.isTwoHop) {
return false;
}
// batchFill does not support WETH wrapping/unwrapping at the moment
if (opts.isFromETH || opts.isToETH) {
return false;
}
if (quote.orders.map(o => o.type).includes(FillQuoteTransformerOrderType.Limit)) {
return false;
}
@@ -49,6 +45,7 @@ const MULTIPLEX_MULTIHOP_FILL_SOURCES = [
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.UniswapV3,
];
/**

View File

@@ -75,6 +75,7 @@ export class SwapQuoter {
private readonly _marketOperationUtils: MarketOperationUtils;
private readonly _rfqtOptions?: SwapQuoterRfqOpts;
private readonly _quoteRequestorHttpClient: AxiosInstance;
private readonly _integratorIdsSet: Set<string>;
/**
* Instantiates a new SwapQuoter instance
@@ -164,6 +165,9 @@ export class SwapQuoter {
httpsAgent: new HttpsAgent({ keepAlive: true, timeout: KEEP_ALIVE_TTL }),
...(rfqt ? rfqt.axiosInstanceOpts : {}),
});
const integratorIds = this._rfqtOptions?.integratorsWhitelist.map(integrator => integrator.integratorId) || [];
this._integratorIdsSet = new Set(integratorIds);
}
public async getBatchMarketBuySwapQuoteAsync(
@@ -359,6 +363,7 @@ export class SwapQuoter {
const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;
const calcOpts: GetMarketOrdersOpts = {
...cloneOpts,
gasPrice,
feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData: FillData) =>
gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)),
),
@@ -414,12 +419,11 @@ export class SwapQuoter {
return isOpenOrder && !willOrderExpire && isFeeTypeAllowed;
}; // tslint:disable-line:semicolon
private _isApiKeyWhitelisted(apiKey: string | undefined): boolean {
if (!apiKey) {
private _isIntegratorIdWhitelisted(integratorId: string | undefined): boolean {
if (!integratorId) {
return false;
}
const whitelistedApiKeys = this._rfqtOptions ? this._rfqtOptions.takerApiKeyWhitelist : [];
return whitelistedApiKeys.includes(apiKey);
return this._integratorIdsSet.has(integratorId);
}
private _isTxOriginBlacklisted(txOrigin: string | undefined): boolean {
@@ -438,19 +442,19 @@ export class SwapQuoter {
return rfqt;
}
// tslint:disable-next-line: boolean-naming
const { apiKey, nativeExclusivelyRFQ, intentOnFilling, txOrigin } = rfqt;
const { integrator, nativeExclusivelyRFQ, intentOnFilling, txOrigin } = rfqt;
// If RFQ-T is enabled and `nativeExclusivelyRFQ` is set, then `ERC20BridgeSource.Native` should
// never be excluded.
if (nativeExclusivelyRFQ === true && !sourceFilters.isAllowed(ERC20BridgeSource.Native)) {
throw new Error('Native liquidity cannot be excluded if "rfqt.nativeExclusivelyRFQ" is set');
}
// If an API key was provided, but the key is not whitelisted, raise a warning and disable RFQ
if (!this._isApiKeyWhitelisted(apiKey)) {
// If an integrator ID was provided, but the ID is not whitelisted, raise a warning and disable RFQ
if (!this._isIntegratorIdWhitelisted(integrator.integratorId)) {
if (this._rfqtOptions && this._rfqtOptions.warningLogger) {
this._rfqtOptions.warningLogger(
{
apiKey,
...integrator,
},
'Attempt at using an RFQ API key that is not whitelisted. Disabling RFQ for the request lifetime.',
);
@@ -474,7 +478,7 @@ export class SwapQuoter {
// Otherwise check other RFQ options
if (
intentOnFilling && // The requestor is asking for a firm quote
this._isApiKeyWhitelisted(apiKey) && // A valid API key was provided
this._isIntegratorIdWhitelisted(integrator.integratorId) && // A valid API key was provided
sourceFilters.isAllowed(ERC20BridgeSource.Native) // Native liquidity is not excluded
) {
if (!txOrigin || txOrigin === constants.NULL_ADDRESS) {

View File

@@ -243,7 +243,7 @@ export interface RfqmRequestOptions extends RfqRequestOpts {
export interface RfqRequestOpts {
takerAddress: string;
txOrigin: string;
apiKey: string;
integrator: Integrator;
intentOnFilling: boolean;
isIndicative?: boolean;
makerEndpointMaxResponseTimeMs?: number;
@@ -256,7 +256,7 @@ export interface RfqRequestOpts {
/**
* gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
*/
export interface SwapQuoteRequestOpts extends GetMarketOrdersOpts {
export interface SwapQuoteRequestOpts extends Omit<GetMarketOrdersOpts, 'gasPrice'> {
gasPrice?: BigNumber;
rfqt?: RfqRequestOpts;
}
@@ -293,8 +293,14 @@ export interface RfqFirmQuoteValidator {
getRfqtTakerFillableAmountsAsync(quotes: RfqOrder[]): Promise<BigNumber[]>;
}
export interface Integrator {
integratorId: string;
label: string;
whitelistIntegratorUrls?: string[];
}
export interface SwapQuoterRfqOpts {
takerApiKeyWhitelist: string[];
integratorsWhitelist: Integrator[];
makerAssetOfferings: RfqMakerAssetOfferings;
txOriginBlacklist: Set<string>;
altRfqCreds?: {

View File

@@ -31,6 +31,7 @@ import {
NULL_ADDRESS,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
PANGOLIN_ROUTER_BY_CHAIN_ID,
POLYDEX_ROUTER_BY_CHAIN_ID,
QUICKSWAP_ROUTER_BY_CHAIN_ID,
SADDLE_MAINNET_INFOS,
@@ -41,6 +42,7 @@ import {
SNOWSWAP_MAINNET_INFOS,
SUSHISWAP_ROUTER_BY_CHAIN_ID,
SWERVE_MAINNET_INFOS,
TRADER_JOE_ROUTER_BY_CHAIN_ID,
UNISWAPV2_ROUTER_BY_CHAIN_ID,
WAULTSWAP_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
@@ -439,7 +441,9 @@ export function uniswapV2LikeRouterAddress(
| ERC20BridgeSource.WaultSwap
| ERC20BridgeSource.Polydex
| ERC20BridgeSource.ShibaSwap
| ERC20BridgeSource.JetSwap,
| ERC20BridgeSource.JetSwap
| ERC20BridgeSource.TraderJoe
| ERC20BridgeSource.Pangolin,
): string {
switch (source) {
case ERC20BridgeSource.UniswapV2:
@@ -476,6 +480,10 @@ export function uniswapV2LikeRouterAddress(
return SHIBASWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.JetSwap:
return JETSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.Pangolin:
return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.TraderJoe:
return TRADER_JOE_ROUTER_BY_CHAIN_ID[chainId];
default:
throw new Error(`Unknown UniswapV2 like source ${source}`);
}

View File

@@ -56,6 +56,7 @@ function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue:
[ChainId.BSC]: defaultValue,
[ChainId.Polygon]: defaultValue,
[ChainId.PolygonMumbai]: defaultValue,
[ChainId.Avalanche]: defaultValue,
...(rest || {}),
};
}
@@ -98,7 +99,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Clipper,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -157,6 +157,12 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.JetSwap,
ERC20BridgeSource.IronSwap,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Pangolin,
ERC20BridgeSource.TraderJoe,
ERC20BridgeSource.SushiSwap,
]),
},
new SourceFilters([]),
);
@@ -199,7 +205,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Clipper,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -258,6 +263,12 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.JetSwap,
ERC20BridgeSource.IronSwap,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Pangolin,
ERC20BridgeSource.TraderJoe,
ERC20BridgeSource.SushiSwap,
]),
},
new SourceFilters([]),
);
@@ -265,7 +276,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
/**
* 0x Protocol Fee Multiplier
*/
export const PROTOCOL_FEE_MULTIPLIER = new BigNumber(70000);
export const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
/**
* Sources to poll for ETH fee price estimates.
@@ -276,6 +287,7 @@ export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>
[ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap],
[ChainId.Ropsten]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap],
[ChainId.Polygon]: [ERC20BridgeSource.QuickSwap, ERC20BridgeSource.SushiSwap],
[ChainId.Avalanche]: [ERC20BridgeSource.Pangolin, ERC20BridgeSource.TraderJoe, ERC20BridgeSource.SushiSwap],
},
[],
);
@@ -416,6 +428,13 @@ export const POLYGON_TOKENS = {
WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb',
};
export const AVALANCHE_TOKENS = {
WAVAX: '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7',
WETH: '0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab',
USDT: '0xc7198437980c041c805a1edcba50c1ce5db95118',
DAI: '0xd586e7f844cea2f87f50152665bcbc2c279d8d70',
};
export const CURVE_POOLS = {
compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound
// 1.USDT is dead
@@ -569,6 +588,12 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
POLYGON_TOKENS.USDT,
POLYGON_TOKENS.WBTC,
],
[ChainId.Avalanche]: [
AVALANCHE_TOKENS.WAVAX,
AVALANCHE_TOKENS.WETH,
AVALANCHE_TOKENS.DAI,
AVALANCHE_TOKENS.USDT,
],
},
[],
);
@@ -594,6 +619,9 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
[ChainId.Polygon]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon],
}).build(),
[ChainId.Avalanche]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Avalanche],
}).build(),
},
new TokenAdjacencyGraphBuilder({ default: [] }).build(),
);
@@ -607,6 +635,7 @@ export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
[ChainId.Rinkeby]: getContractAddressesForChainOrThrow(ChainId.Rinkeby).etherToken,
[ChainId.Kovan]: getContractAddressesForChainOrThrow(ChainId.Kovan).etherToken,
[ChainId.Polygon]: getContractAddressesForChainOrThrow(ChainId.Polygon).etherToken,
[ChainId.Avalanche]: getContractAddressesForChainOrThrow(ChainId.Avalanche).etherToken,
},
NULL_ADDRESS,
);
@@ -1219,6 +1248,7 @@ export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
[ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Ropsten]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Polygon]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Avalanche]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
},
NULL_ADDRESS,
);
@@ -1450,22 +1480,6 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId<LidoInfo>(
},
);
export const CLIPPER_INFO_BY_CHAIN = valueByChainId(
{
[ChainId.Mainnet]: {
poolAddress: '0xe82906b6b1b04f631d126c974af57a3a7b6a99d9',
tokens: [
MAINNET_TOKENS.WETH, // technically ETH but our sampler and mixin handle this
MAINNET_TOKENS.WBTC,
MAINNET_TOKENS.USDC,
MAINNET_TOKENS.USDT,
MAINNET_TOKENS.DAI,
],
},
},
{ poolAddress: NULL_ADDRESS, tokens: [] },
);
export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
@@ -1591,6 +1605,37 @@ export const JETSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
NULL_ADDRESS,
);
export const PANGOLIN_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Avalanche]: '0xe54ca86531e17ef3616d22ca28b0d458b6c89106',
},
NULL_ADDRESS,
);
export const TRADER_JOE_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Avalanche]: '0x60ae616a2155ee3d9a68541ba4544862310933d4',
},
NULL_ADDRESS,
);
export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
{
[ChainId.Mainnet]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3],
[ChainId.BSC]: [
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.PancakeSwapV2,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.ApeSwap,
ERC20BridgeSource.CafeSwap,
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
],
},
[],
);
const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
@@ -1696,7 +1741,6 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
return gas;
},
[ERC20BridgeSource.Lido]: () => 226e3,
[ERC20BridgeSource.Clipper]: () => 170e3,
//
// BSC
@@ -1719,6 +1763,12 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Dfyn]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.Polydex]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.JetSwap]: uniswapV2CloneGasSchedule,
//
// Avalanche
//
[ERC20BridgeSource.Pangolin]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.TraderJoe]: uniswapV2CloneGasSchedule,
};
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };
@@ -1727,7 +1777,7 @@ export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
// tslint:enable:custom-no-magic-numbers
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice'> = {
// tslint:disable-next-line: custom-no-magic-numbers
runLimit: 2 ** 15,
excludedSources: [],

View File

@@ -71,7 +71,25 @@ function hasLiquidity(fills: Fill[]): boolean {
return true;
}
function nativeOrdersToFills(
export function ethToOutputAmount({
input,
output,
ethAmount,
inputAmountPerEth,
outputAmountPerEth,
}: {
input: BigNumber;
output: BigNumber;
inputAmountPerEth: BigNumber;
outputAmountPerEth: BigNumber;
ethAmount: BigNumber | number;
}): BigNumber {
return !outputAmountPerEth.isZero()
? outputAmountPerEth.times(ethAmount)
: inputAmountPerEth.times(ethAmount).times(output.dividedToIntegerBy(input));
}
export function nativeOrdersToFills(
side: MarketOperation,
orders: NativeOrderWithFillableAmounts[],
targetInput: BigNumber = POSITIVE_INF,
@@ -89,9 +107,13 @@ function nativeOrdersToFills(
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
const outputPenalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(fee)
: inputAmountPerEth.times(fee).times(output.dividedToIntegerBy(input));
const outputPenalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
// targetInput can be less than the order size
// whilst the penalty is constant, it affects the adjusted output
// only up until the target has been exhausted.
@@ -132,7 +154,7 @@ function nativeOrdersToFills(
return fills;
}
function dexSamplesToFills(
export function dexSamplesToFills(
side: MarketOperation,
samples: DexSample[],
outputAmountPerEth: BigNumber,
@@ -156,9 +178,13 @@ function dexSamplesToFills(
let penalty = ZERO_AMOUNT;
if (i === 0) {
// Only the first fill in a DEX path incurs a penalty.
penalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(fee)
: inputAmountPerEth.times(fee).times(output.dividedToIntegerBy(input));
penalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
}
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);

View File

@@ -39,7 +39,7 @@ import { createFills } from './fills';
import { getBestTwoHopQuote } from './multihop_utils';
import { createOrdersFromTwoHopSample } from './orders';
import { Path, PathPenaltyOpts } from './path';
import { fillsToSortedPaths, findOptimalPathAsync } from './path_optimizer';
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler';
import { SourceFilters } from './source_filters';
import {
@@ -48,7 +48,6 @@ import {
DexSample,
ERC20BridgeSource,
Fill,
FillData,
GenerateOptimizedOrdersOpts,
GetMarketOrdersOpts,
MarketSideLiquidity,
@@ -57,6 +56,8 @@ import {
OrderDomain,
} from './types';
const SHOULD_USE_RUST_ROUTER = process.env.RUST_ROUTER === 'true';
// tslint:disable:boolean-naming
export class MarketOperationUtils {
@@ -326,12 +327,12 @@ export class MarketOperationUtils {
public async getBatchMarketBuyOrdersAsync(
batchNativeOrders: SignedNativeOrder[][],
makerAmounts: BigNumber[],
opts?: Partial<GetMarketOrdersOpts>,
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
): Promise<Array<OptimizerResult | undefined>> {
if (batchNativeOrders.length === 0) {
throw new Error(AggregationError.EmptyOrders);
}
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
const requestFilters = new SourceFilters().exclude(_opts.excludedSources).include(_opts.includedSources);
const quoteSourceFilters = this._buySources.merge(requestFilters);
@@ -409,6 +410,7 @@ export class MarketOperationUtils {
excludedSources: _opts.excludedSources,
feeSchedule: _opts.feeSchedule,
allowFallback: _opts.allowFallback,
gasPrice: _opts.gasPrice,
},
);
return optimizerResult;
@@ -475,6 +477,7 @@ export class MarketOperationUtils {
outputAmountPerEth,
inputAmountPerEth,
exchangeProxyOverhead: opts.exchangeProxyOverhead || (() => ZERO_AMOUNT),
gasPrice: opts.gasPrice,
};
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
@@ -485,8 +488,22 @@ export class MarketOperationUtils {
const _unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, penaltyOpts)[0];
const unoptimizedPath = _unoptimizedPath ? _unoptimizedPath.collapse(orderOpts) : undefined;
// Find the optimal path
const optimalPath = await findOptimalPathAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
let optimalPath: Path | undefined;
if (SHOULD_USE_RUST_ROUTER) {
optimalPath = findOptimalRustPathFromSamples(
side,
dexQuotes,
[...nativeOrders, ...augmentedRfqtIndicativeQuotes],
inputAmount,
penaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
);
} else {
optimalPath = await findOptimalPathJSAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
}
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
const { adjustedRate: bestTwoHopRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
@@ -514,7 +531,7 @@ export class MarketOperationUtils {
}
// Generate a fallback path if required
await this._addOptionalFallbackAsync(side, inputAmount, optimalPath, fills, opts, penaltyOpts);
await this._addOptionalFallbackAsync(side, inputAmount, optimalPath, dexQuotes, fills, opts, penaltyOpts);
const collapsedPath = optimalPath.collapse(orderOpts);
return {
@@ -536,9 +553,9 @@ export class MarketOperationUtils {
nativeOrders: SignedNativeOrder[],
amount: BigNumber,
side: MarketOperation,
opts?: Partial<GetMarketOrdersOpts>,
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
): Promise<OptimizerResultWithReport> {
const _opts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
const _opts: GetMarketOrdersOpts = { ...DEFAULT_GET_MARKET_ORDERS_OPTS, ...opts };
const optimizerOpts: GenerateOptimizedOrdersOpts = {
bridgeSlippage: _opts.bridgeSlippage,
maxFallbackSlippage: _opts.maxFallbackSlippage,
@@ -546,6 +563,7 @@ export class MarketOperationUtils {
feeSchedule: _opts.feeSchedule,
allowFallback: _opts.allowFallback,
exchangeProxyOverhead: _opts.exchangeProxyOverhead,
gasPrice: _opts.gasPrice,
};
if (nativeOrders.length === 0) {
@@ -711,7 +729,8 @@ export class MarketOperationUtils {
side: MarketOperation,
inputAmount: BigNumber,
optimalPath: Path,
fills: Array<Array<Fill<FillData>>>,
dexQuotes: DexSample[][],
fills: Fill[][],
opts: GenerateOptimizedOrdersOpts,
penaltyOpts: PathPenaltyOpts,
): Promise<void> {
@@ -725,13 +744,37 @@ export class MarketOperationUtils {
if (opts.allowFallback && fragileFills.length !== 0) {
// We create a fallback path that is exclusive of Native liquidity
// This is the optimal on-chain path for the entire input amount
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
const sturdyOptimalPath = await findOptimalPathAsync(side, sturdyFills, inputAmount, opts.runLimit, {
const sturdyPenaltyOpts = {
...penaltyOpts,
exchangeProxyOverhead: (sourceFlags: bigint) =>
// tslint:disable-next-line: no-bitwise
penaltyOpts.exchangeProxyOverhead(sourceFlags | optimalPath.sourceFlags),
});
};
let sturdyOptimalPath: Path | undefined;
if (SHOULD_USE_RUST_ROUTER) {
const sturdySamples = dexQuotes.filter(
samples => samples.length > 0 && !fragileSources.includes(samples[0].source),
);
sturdyOptimalPath = findOptimalRustPathFromSamples(
side,
sturdySamples,
[],
inputAmount,
sturdyPenaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
);
} else {
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
sturdyOptimalPath = await findOptimalPathJSAsync(
side,
sturdyFills,
inputAmount,
opts.runLimit,
sturdyPenaltyOpts,
);
}
// Calculate the slippage of on-chain sources compared to the most optimal path
// if within an acceptable threshold we enable a fallback to prevent reverts
if (

View File

@@ -3,7 +3,7 @@ import { AbiEncoder, BigNumber } from '@0x/utils';
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
import { MAX_UINT256, NULL_BYTES, ZERO_AMOUNT } from './constants';
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
import {
AggregationError,
BalancerFillData,
@@ -180,8 +180,10 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap');
case ERC20BridgeSource.ACryptos:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'ACryptoS');
case ERC20BridgeSource.Clipper:
return encodeBridgeSourceId(BridgeProtocol.Clipper, 'Clipper');
case ERC20BridgeSource.Pangolin:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Pangolin');
case ERC20BridgeSource.TraderJoe:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'TraderJoe');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -256,6 +258,8 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Polydex:
case ERC20BridgeSource.ShibaSwap:
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
@@ -320,10 +324,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
break;
case ERC20BridgeSource.Clipper:
const clipperFillData = (order as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
bridgeData = encoder.encode([clipperFillData.poolAddress, NULL_BYTES]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -448,6 +448,8 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
[ERC20BridgeSource.Linkswap]: routerAddressPathEncoder,
[ERC20BridgeSource.ShibaSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.Pangolin]: routerAddressPathEncoder,
[ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder,
// BSC
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder,
@@ -481,10 +483,6 @@ export const BRIDGE_ENCODERS: {
]),
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.Clipper]: AbiEncoder.create([
{ name: 'provider', type: 'address' },
{ name: 'data', type: 'bytes' },
]),
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -3,6 +3,7 @@ import { BigNumber } from '@0x/utils';
import { MarketOperation } from '../../types';
import { POSITIVE_INF, ZERO_AMOUNT } from './constants';
import { ethToOutputAmount } from './fills';
import { createBridgeOrder, createNativeOptimizedOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders';
import { getCompleteRate, getRate } from './rate_utils';
import {
@@ -25,12 +26,14 @@ export interface PathPenaltyOpts {
outputAmountPerEth: BigNumber;
inputAmountPerEth: BigNumber;
exchangeProxyOverhead: ExchangeProxyOverhead;
gasPrice: BigNumber;
}
export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = {
outputAmountPerEth: ZERO_AMOUNT,
inputAmountPerEth: ZERO_AMOUNT,
exchangeProxyOverhead: () => ZERO_AMOUNT,
gasPrice: ZERO_AMOUNT,
};
export class Path {
@@ -143,9 +146,13 @@ export class Path {
const { input, output } = this._adjustedSize;
const { exchangeProxyOverhead, outputAmountPerEth, inputAmountPerEth } = this.pathPenaltyOpts;
const gasOverhead = exchangeProxyOverhead(this.sourceFlags);
const pathPenalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(gasOverhead)
: inputAmountPerEth.times(gasOverhead).times(output.dividedToIntegerBy(input));
const pathPenalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: gasOverhead,
});
return {
input,
output: this.side === MarketOperation.Sell ? output.minus(pathPenalty) : output.plus(pathPenalty),

View File

@@ -1,20 +1,377 @@
import { assert } from '@0x/assert';
import { ChainId } from '@0x/contract-addresses';
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { performance } from 'perf_hooks';
import { MarketOperation } from '../../types';
import { DEFAULT_INFO_LOGGER } from '../../constants';
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID } from '../market_operation_utils/constants';
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
import { ERC20BridgeSource, Fill } from './types';
import { getRate } from './rate_utils';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData } from './types';
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
const RUN_LIMIT_DECAY_FACTOR = 0.5;
const RUST_ROUTER_NUM_SAMPLES = 200;
const FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD = new BigNumber(150e3);
// NOTE: The Rust router will panic with less than 3 samples
const MIN_NUM_SAMPLE_INPUTS = 3;
const isDexSample = (obj: DexSample | NativeOrderWithFillableAmounts): obj is DexSample => !!(obj as DexSample).source;
function nativeOrderToNormalizedAmounts(
side: MarketOperation,
nativeOrder: NativeOrderWithFillableAmounts,
): { input: BigNumber; output: BigNumber } {
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount } = nativeOrder;
const makerAmount = fillableMakerAmount;
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
return { input, output };
}
function calculateOuputFee(
side: MarketOperation,
sampleOrNativeOrder: DexSample | NativeOrderWithFillableAmounts,
outputAmountPerEth: BigNumber,
inputAmountPerEth: BigNumber,
fees: FeeSchedule,
): BigNumber {
if (isDexSample(sampleOrNativeOrder)) {
const { input, output, source, fillData } = sampleOrNativeOrder;
const fee = fees[source]?.(fillData) || 0;
const outputFee = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
return outputFee;
} else {
const { input, output } = nativeOrderToNormalizedAmounts(side, sampleOrNativeOrder);
const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder) || 0;
const outputFee = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
return outputFee;
}
}
// Use linear interpolation to approximate the output
// at a certain input somewhere between the two samples
// See https://en.wikipedia.org/wiki/Linear_interpolation
const interpolateOutputFromSamples = (
left: { input: BigNumber; output: BigNumber },
right: { input: BigNumber; output: BigNumber },
targetInput: BigNumber,
): BigNumber =>
left.output.plus(
right.output
.minus(left.output)
.dividedBy(right.input.minus(left.input))
.times(targetInput.minus(left.input)),
);
function findRoutesAndCreateOptimalPath(
side: MarketOperation,
samples: DexSample[][],
nativeOrders: NativeOrderWithFillableAmounts[],
input: BigNumber,
opts: PathPenaltyOpts,
fees: FeeSchedule,
): Path | undefined {
const createFill = (sample: DexSample) =>
dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees)[0];
// Track sample id's to integers (required by rust router)
const sampleIdLookup: { [key: string]: number } = {};
let sampleIdCounter = 0;
const sampleToId = (source: ERC20BridgeSource, index: number): number => {
const key = `${source}-${index}`;
if (sampleIdLookup[key]) {
return sampleIdLookup[key];
} else {
sampleIdLookup[key] = ++sampleIdCounter;
return sampleIdLookup[key];
}
};
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
const serializedPaths: SerializedPath[] = [];
for (const singleSourceSamples of samples) {
if (singleSourceSamples.length === 0) {
continue;
}
const singleSourceSamplesWithOutput = [...singleSourceSamples];
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
if (singleSourceSamples[i].output.isZero()) {
// Remove trailing 0 output samples
singleSourceSamplesWithOutput.pop();
} else {
break;
}
}
if (singleSourceSamplesWithOutput.length < MIN_NUM_SAMPLE_INPUTS) {
continue;
}
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
(memo, sample, sampleIdx) => {
memo.ids.push(sampleToId(sample.source, sampleIdx));
memo.inputs.push(sample.input.integerValue().toNumber());
memo.outputs.push(sample.output.integerValue().toNumber());
memo.outputFees.push(
calculateOuputFee(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
.integerValue()
.toNumber(),
);
return memo;
},
{
ids: [],
inputs: [],
outputs: [],
outputFees: [],
},
);
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
serializedPaths.push(serializedPath);
}
for (const [idx, nativeOrder] of nativeOrders.entries()) {
const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts(
side,
nativeOrder,
);
// NOTE: skip dummy order created in swap_quoter
// TODO: remove dummy order and this logic once we don't need the JS router
if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) {
continue;
}
// HACK: the router requires at minimum 3 samples as a basis for interpolation
const inputs = [
0,
normalizedOrderInput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderInput.integerValue().toNumber(),
];
const outputs = [
0,
normalizedOrderOutput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderOutput.integerValue().toNumber(),
];
// NOTE: same fee no matter if full or partial fill
const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
.integerValue()
.toNumber();
const outputFees = [fee, fee, fee];
// NOTE: ids can be the same for all fake samples
const id = sampleToId(ERC20BridgeSource.Native, idx);
const ids = [id, id, id];
const serializedPath: SerializedPath = {
ids,
inputs,
outputs,
outputFees,
};
samplesAndNativeOrdersWithResults.push([nativeOrder]);
serializedPaths.push(serializedPath);
}
if (serializedPaths.length === 0) {
return undefined;
}
const rustArgs: OptimizerCapture = {
side,
targetInput: input.toNumber(),
pathsIn: serializedPaths,
};
const before = performance.now();
const allSourcesRustRoute = route(rustArgs, RUST_ROUTER_NUM_SAMPLES);
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'real' },
'Rust router real routing performance',
);
assert.assert(
rustArgs.pathsIn.length === allSourcesRustRoute.length,
'different number of sources in the Router output than the input',
);
const routesAndSamples = _.zip(allSourcesRustRoute, samplesAndNativeOrdersWithResults);
const adjustedFills: Fill[] = [];
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
const scale = input.dividedBy(totalRoutedAmount);
for (const [routeInput, routeSamplesAndNativeOrders] of routesAndSamples) {
if (!routeInput || !routeSamplesAndNativeOrders) {
continue;
}
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precisions loss for number/f64
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
const rustInputAdjusted = BigNumber.min(
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
input,
);
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
if (!isDexSample(current)) {
const nativeFill = nativeOrdersToFills(
side,
[current],
rustInputAdjusted,
opts.outputAmountPerEth,
opts.inputAmountPerEth,
fees,
)[0];
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push(nativeFill);
continue;
}
// NOTE: For DexSamples only
let fill = createFill(current);
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
// Descend to approach a closer fill for fillData which may not be consistent
// throughout the path (UniswapV3) and for a closer guesstimate at
// gas used
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
for (let k = routeSamples.length - 1; k >= 0; k--) {
if (k === 0) {
fill = createFill(routeSamples[0]);
}
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
// Between here and the previous fill
// HACK: Use the midpoint between the two
const left = routeSamples[k];
const right = routeSamples[k + 1];
if (left && right) {
// Approximate how much output we get for the input with the surrounding samples
const interpolatedOutput = interpolateOutputFromSamples(
left,
right,
rustInputAdjusted,
).decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
fill = createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: interpolatedOutput,
});
} else {
assert.assert(Boolean(left || right), 'No valid sample to use');
fill = createFill(left || right);
}
break;
}
}
const scaleOutput = (output: BigNumber) =>
output
.dividedBy(fill.input)
.times(rustInputAdjusted)
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
adjustedFills.push({
...fill,
input: rustInputAdjusted,
output: scaleOutput(fill.output),
adjustedOutput: scaleOutput(fill.adjustedOutput),
index: 0,
parent: undefined,
});
}
const pathFromRustInputs = Path.create(side, adjustedFills, input);
return pathFromRustInputs;
}
export function findOptimalRustPathFromSamples(
side: MarketOperation,
samples: DexSample[][],
nativeOrders: NativeOrderWithFillableAmounts[],
input: BigNumber,
opts: PathPenaltyOpts,
fees: FeeSchedule,
chainId: ChainId,
): Path | undefined {
const before = performance.now();
const logPerformance = () =>
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'total' },
'Rust router total routing performance',
);
const allSourcesPath = findRoutesAndCreateOptimalPath(side, samples, nativeOrders, input, opts, fees);
if (!allSourcesPath) {
return undefined;
}
const vipSources = VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId];
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
// we need to try to route them in isolation and compare with the results all sources
if (vipSources.length > 0) {
const vipSourcesSet = new Set(vipSources);
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
if (vipSourcesSamples.length > 0) {
const vipSourcesPath = findRoutesAndCreateOptimalPath(side, vipSourcesSamples, [], input, opts, fees);
const { input: allSourcesInput, output: allSourcesOutput } = allSourcesPath.adjustedSize();
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
const gasCostInWei = FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD.times(opts.gasPrice);
const fqtOverheadInOutputToken = gasCostInWei.times(opts.outputAmountPerEth);
const outputWithFqtOverhead =
side === MarketOperation.Sell
? allSourcesOutput.minus(fqtOverheadInOutputToken)
: allSourcesOutput.plus(fqtOverheadInOutputToken);
const allSourcesAdjustedRateWithFqtOverhead = getRate(side, allSourcesInput, outputWithFqtOverhead);
if (vipSourcesPath?.adjustedRate().isGreaterThan(allSourcesAdjustedRateWithFqtOverhead)) {
logPerformance();
return vipSourcesPath;
}
}
}
logPerformance();
return allSourcesPath;
}
/**
* Find the optimal mixture of fills that maximizes (for sells) or minimizes
* (for buys) output, while meeting the input requirement.
*/
export async function findOptimalPathAsync(
export async function findOptimalPathJSAsync(
side: MarketOperation,
fills: Fill[][],
targetInput: BigNumber,

View File

@@ -20,7 +20,6 @@ import {
import {
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
CLIPPER_INFO_BY_CHAIN,
DODOV1_CONFIG_BY_CHAIN_ID,
DODOV2_FACTORIES_BY_CHAIN_ID,
KYBER_CONFIG_BY_CHAIN_ID,
@@ -1196,6 +1195,8 @@ export class SamplerOperations {
case ERC20BridgeSource.Polydex:
case ERC20BridgeSource.ShibaSwap:
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1414,32 +1415,6 @@ export class SamplerOperations {
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts);
}
case ERC20BridgeSource.Clipper:
const { poolAddress: clipperPoolAddress, tokens: clipperTokens } = CLIPPER_INFO_BY_CHAIN[
this.chainId
];
if (
clipperPoolAddress === NULL_ADDRESS ||
!clipperTokens.includes(makerToken) ||
!clipperTokens.includes(takerToken)
) {
return [];
}
// Clipper requires WETH to be represented as address(0)
const adjustedMakerToken =
makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken;
const adjustedTakerToken =
takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken;
// Supports the PLP interface
return this.getLiquidityProviderSellQuotes(
clipperPoolAddress,
adjustedMakerToken,
adjustedTakerToken,
takerFillAmounts,
// tslint:disable-next-line: custom-no-magic-numbers
0, // Not used for Clipper
ERC20BridgeSource.Clipper,
);
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
@@ -1489,6 +1464,8 @@ export class SamplerOperations {
case ERC20BridgeSource.Polydex:
case ERC20BridgeSource.ShibaSwap:
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1702,32 +1679,6 @@ export class SamplerOperations {
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.Clipper:
const { poolAddress: clipperPoolAddress, tokens: clipperTokens } = CLIPPER_INFO_BY_CHAIN[
this.chainId
];
if (
clipperPoolAddress === NULL_ADDRESS ||
!clipperTokens.includes(makerToken) ||
!clipperTokens.includes(takerToken)
) {
return [];
}
// Clipper requires WETH to be represented as address(0)
const adjustedMakerToken =
makerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : makerToken;
const adjustedTakerToken =
takerToken === NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId] ? NULL_ADDRESS : takerToken;
// Supports the PLP interface
return this.getLiquidityProviderBuyQuotes(
clipperPoolAddress,
adjustedMakerToken,
adjustedTakerToken,
makerFillAmounts,
// tslint:disable-next-line: custom-no-magic-numbers
0, // Not used for Clipper
ERC20BridgeSource.Clipper,
);
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -69,7 +69,6 @@ export enum ERC20BridgeSource {
CurveV2 = 'Curve_V2',
Lido = 'Lido',
ShibaSwap = 'ShibaSwap',
Clipper = 'Clipper',
// BSC only
PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2',
@@ -91,6 +90,9 @@ export enum ERC20BridgeSource {
FirebirdOneSwap = 'FirebirdOneSwap',
JetSwap = 'JetSwap',
IronSwap = 'IronSwap',
// Avalanche
Pangolin = 'Pangolin',
TraderJoe = 'TraderJoe',
}
export type SourcesWithPoolsCache = ERC20BridgeSource.Balancer | ERC20BridgeSource.BalancerV2 | ERC20BridgeSource.Cream;
@@ -452,6 +454,11 @@ export interface GetMarketOrdersOpts {
* hopping to. E.g DAI->USDC via an adjacent token WETH
*/
tokenAdjacencyGraph: TokenAdjacencyGraph;
/**
* Gas price to use for quote
*/
gasPrice: BigNumber;
}
/**
@@ -531,10 +538,11 @@ export interface GenerateOptimizedOrdersOpts {
bridgeSlippage?: number;
maxFallbackSlippage?: number;
excludedSources?: ERC20BridgeSource[];
feeSchedule?: FeeSchedule;
feeSchedule: FeeSchedule;
exchangeProxyOverhead?: ExchangeProxyOverhead;
allowFallback?: boolean;
shouldBatchBridgeOrders?: boolean;
gasPrice: BigNumber;
}
export interface ComparisonPrice {

View File

@@ -14,6 +14,7 @@ import { constants } from '../constants';
import {
AltQuoteModel,
AltRfqMakerAssetOfferings,
Integrator,
LogFunction,
MarketOperation,
RfqMakerAssetOfferings,
@@ -61,6 +62,31 @@ export interface MetricsProxy {
* @param expirationTimeSeconds the expiration time in seconds
*/
incrementFillRatioWarningCounter(isLastLook: boolean, maker: string): void;
/**
* Logs the outcome of a network (HTTP) interaction with a market maker.
*
* @param interaction.isLastLook true if the request is RFQM
* @param interaction.integrator the integrator that is requesting the RFQ quote
* @param interaction.url the URL of the market maker
* @param interaction.quoteType indicative or firm quote
* @param interaction.statusCode the statusCode returned by a market maker
* @param interaction.latencyMs the latency of the HTTP request (in ms)
* @param interaction.included if a firm quote that was returned got included in the next step of processing.
* NOTE: this does not mean that the request returned a valid fillable order. It just
* means that the network response was successful.
*/
logRfqMakerNetworkInteraction(interaction: {
isLastLook: boolean;
integrator: Integrator;
url: string;
quoteType: 'firm' | 'indicative';
statusCode: number | undefined;
latencyMs: number;
included: boolean;
sellTokenAddress: string;
buyTokenAddress: string;
}): void;
}
/**
@@ -178,6 +204,42 @@ export class QuoteRequestor {
}
}
/**
* Gets both standard RFQ makers and "alternative" RFQ makers and combines them together
* in a single configuration map. If an integration key whitelist is present, it will be used
* to filter a specific makers.
*
* @param options the RfqmRequestOptions passed in
* @param assetOfferings the RFQM or RFQT maker offerings
* @returns a list of TypedMakerUrl instances
*/
public static getTypedMakerUrlsAndWhitelist(
options: Pick<RfqmRequestOptions, 'integrator' | 'altRfqAssetOfferings'>,
assetOfferings: RfqMakerAssetOfferings,
): TypedMakerUrl[] {
const standardUrls = Object.keys(assetOfferings).map(
(mm: string): TypedMakerUrl => {
return { pairType: RfqPairType.Standard, url: mm };
},
);
const altUrls = options.altRfqAssetOfferings
? Object.keys(options.altRfqAssetOfferings).map(
(mm: string): TypedMakerUrl => {
return { pairType: RfqPairType.Alt, url: mm };
},
)
: [];
let typedMakerUrls = standardUrls.concat(altUrls);
// If there is a whitelist, only allow approved maker URLs
if (options.integrator.whitelistIntegratorUrls !== undefined) {
const whitelist = new Set(options.integrator.whitelistIntegratorUrls.map(key => key.toLowerCase()));
typedMakerUrls = typedMakerUrls.filter(makerUrl => whitelist.has(makerUrl.url.toLowerCase()));
}
return typedMakerUrls;
}
public static getDurationUntilExpirationMs(expirationTimeSeconds: BigNumber): BigNumber {
const expirationTimeMs = expirationTimeSeconds.times(constants.ONE_SECOND_MS);
const currentTimeMs = new BigNumber(Date.now());
@@ -401,21 +463,6 @@ export class QuoteRequestor {
}
})();
const standardUrls = Object.keys(assetOfferings).map(
(mm: string): TypedMakerUrl => {
return { pairType: RfqPairType.Standard, url: mm };
},
);
const altUrls = options.altRfqAssetOfferings
? Object.keys(options.altRfqAssetOfferings).map(
(mm: string): TypedMakerUrl => {
return { pairType: RfqPairType.Alt, url: mm };
},
)
: [];
const typedMakerUrls = standardUrls.concat(altUrls);
const timeoutMs =
options.makerEndpointMaxResponseTimeMs ||
constants.DEFAULT_RFQT_REQUEST_OPTS.makerEndpointMaxResponseTimeMs!;
@@ -427,11 +474,25 @@ export class QuoteRequestor {
cancelTokenSource.cancel('timeout via cancel token');
}, timeoutMs + bufferMs);
const typedMakerUrls = QuoteRequestor.getTypedMakerUrlsAndWhitelist(options, assetOfferings);
const quotePromises = typedMakerUrls.map(async typedMakerUrl => {
// filter out requests to skip
const isBlacklisted = rfqMakerBlacklist.isMakerBlacklisted(typedMakerUrl.url);
const partialLogEntry = { url: typedMakerUrl.url, quoteType, requestParams, isBlacklisted };
const { isLastLook, integrator } = options;
const { sellTokenAddress, buyTokenAddress } = requestParams;
if (isBlacklisted) {
this._metrics?.logRfqMakerNetworkInteraction({
isLastLook: false,
url: typedMakerUrl.url,
quoteType,
statusCode: undefined,
sellTokenAddress,
buyTokenAddress,
latencyMs: 0,
included: false,
integrator,
});
this._infoLogger({ rfqtMakerInteraction: { ...partialLogEntry } });
return;
} else if (
@@ -450,18 +511,32 @@ export class QuoteRequestor {
try {
if (typedMakerUrl.pairType === RfqPairType.Standard) {
const response = await this._quoteRequestorHttpClient.get(`${typedMakerUrl.url}/${quotePath}`, {
headers: { '0x-api-key': options.apiKey },
headers: {
'0x-api-key': options.integrator.integratorId,
'0x-integrator-id': options.integrator.integratorId,
},
params: requestParams,
timeout: timeoutMs,
cancelToken: cancelTokenSource.token,
});
const latencyMs = Date.now() - timeBeforeAwait;
this._metrics?.logRfqMakerNetworkInteraction({
isLastLook: isLastLook || false,
url: typedMakerUrl.url,
quoteType,
statusCode: response.status,
sellTokenAddress,
buyTokenAddress,
latencyMs,
included: true,
integrator,
});
this._infoLogger({
rfqtMakerInteraction: {
...partialLogEntry,
response: {
included: true,
apiKey: options.apiKey,
apiKey: options.integrator.integratorId,
takerAddress: requestParams.takerAddress,
txOrigin: requestParams.txOrigin,
statusCode: response.status,
@@ -479,7 +554,7 @@ export class QuoteRequestor {
typedMakerUrl.url,
this._altRfqCreds.altRfqApiKey,
this._altRfqCreds.altRfqProfile,
options.apiKey,
options.integrator.integratorId,
quoteType === 'firm' ? AltQuoteModel.Firm : AltQuoteModel.Indicative,
makerToken,
takerToken,
@@ -492,12 +567,23 @@ export class QuoteRequestor {
);
const latencyMs = Date.now() - timeBeforeAwait;
this._metrics?.logRfqMakerNetworkInteraction({
isLastLook: isLastLook || false,
url: typedMakerUrl.url,
quoteType,
statusCode: quote.status,
sellTokenAddress,
buyTokenAddress,
latencyMs,
included: true,
integrator,
});
this._infoLogger({
rfqtMakerInteraction: {
...partialLogEntry,
response: {
included: true,
apiKey: options.apiKey,
apiKey: options.integrator.integratorId,
takerAddress: requestParams.takerAddress,
txOrigin: requestParams.txOrigin,
statusCode: quote.status,
@@ -511,12 +597,23 @@ export class QuoteRequestor {
} catch (err) {
// log error if any
const latencyMs = Date.now() - timeBeforeAwait;
this._metrics?.logRfqMakerNetworkInteraction({
isLastLook: isLastLook || false,
url: typedMakerUrl.url,
quoteType,
statusCode: err.response?.status,
sellTokenAddress,
buyTokenAddress,
latencyMs,
included: false,
integrator,
});
this._infoLogger({
rfqtMakerInteraction: {
...partialLogEntry,
response: {
included: false,
apiKey: options.apiKey,
apiKey: options.integrator.integratorId,
takerAddress: requestParams.takerAddress,
txOrigin: requestParams.txOrigin,
statusCode: err.response ? err.response.status : undefined,
@@ -527,7 +624,7 @@ export class QuoteRequestor {
rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs);
this._warningLogger(
convertIfAxiosError(err),
`Failed to get RFQ-T ${quoteType} quote from market maker endpoint ${typedMakerUrl.url} for API key ${options.apiKey} for taker address ${options.takerAddress} and tx origin ${options.txOrigin}`,
`Failed to get RFQ-T ${quoteType} quote from market maker endpoint ${typedMakerUrl.url} for integrator ${options.integrator.integratorId} (${options.integrator.label}) for taker address ${options.takerAddress} and tx origin ${options.txOrigin}`,
);
return;
}

View File

@@ -28,7 +28,11 @@ export const rfqtMocker = {
// Mock out RFQT responses
for (const mockedResponse of mockedResponses) {
const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse;
const requestHeaders = { Accept: 'application/json, text/plain, */*', '0x-api-key': requestApiKey };
const requestHeaders = {
Accept: 'application/json, text/plain, */*',
'0x-api-key': requestApiKey,
'0x-integrator-id': requestApiKey,
};
mockedAxios
.onGet(`${endpoint}/${quoteType}`, { params: requestParams }, requestHeaders)
.replyOnce(responseCode, responseData);

View File

@@ -18,7 +18,9 @@ import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '
// tslint:disable: custom-no-magic-numbers
const { NULL_ADDRESS } = constants;
blockchainTests('erc20-bridge-sampler', env => {
// HACK(dorothy-zbornak): Disabled because these tests are flakey and all this logic is moving to
// the sampler service anyway.
blockchainTests.skip('erc20-bridge-sampler', env => {
let testContract: TestERC20BridgeSamplerContract;
const RATE_DENOMINATOR = constants.ONE_ETHER;
const MIN_RATE = new BigNumber('0.01');

View File

@@ -186,7 +186,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
);
}
const transformERC20Encoder = AbiEncoder.createMethod('transformERC20', [
const transformERC20Encoder = AbiEncoder.createMethod('transformERC20Staging', [
{ type: 'address', name: 'inputToken' },
{ type: 'address', name: 'outputToken' },
{ type: 'uint256', name: 'inputTokenAmount' },
@@ -261,7 +261,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, MAKER_TOKEN, ETH_TOKEN_ADDRESS]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]);
});
it('can produce a buy quote', async () => {
@@ -292,7 +292,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, MAKER_TOKEN, ETH_TOKEN_ADDRESS]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]);
});
it('ERC20 -> ERC20 does not have a WETH transformer', async () => {
@@ -437,12 +437,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
expect(secondHopFillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[2].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([
TAKER_TOKEN,
MAKER_TOKEN,
ETH_TOKEN_ADDRESS,
INTERMEDIATE_TOKEN,
]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, INTERMEDIATE_TOKEN, ETH_TOKEN_ADDRESS]);
});
// it.skip('Uses the `LiquidityProviderFeature` if given a single LiquidityProvider order', async () => {
// const quote = {
@@ -504,7 +499,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
expect(fillQuoteTransformerData.buyToken).to.eq(MAKER_TOKEN);
const payTakerTransformerData = decodePayTakerTransformerData(callArgs.transformations[1].data);
expect(payTakerTransformerData.amounts).to.deep.eq([]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, MAKER_TOKEN, ETH_TOKEN_ADDRESS]);
expect(payTakerTransformerData.tokens).to.deep.eq([TAKER_TOKEN, ETH_TOKEN_ADDRESS]);
});
});
});

View File

@@ -16,7 +16,7 @@ import * as _ from 'lodash';
import * as TypeMoq from 'typemoq';
import { MarketOperation, QuoteRequestor, RfqRequestOpts, SignedNativeOrder } from '../src';
import { NativeOrderWithFillableAmounts } from '../src/types';
import { Integrator, NativeOrderWithFillableAmounts } from '../src/types';
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
import {
BUY_SOURCE_FILTER_BY_CHAIN_ID,
@@ -62,6 +62,10 @@ const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign };
const FOO_INTEGRATOR: Integrator = {
integratorId: 'foo',
label: 'foo',
};
/**
* gets the orders required for a market sell operation by (potentially) merging native orders with
@@ -75,7 +79,7 @@ async function getMarketSellOrdersAsync(
utils: MarketOperationUtils,
nativeOrders: SignedNativeOrder[],
takerAmount: BigNumber,
opts?: Partial<GetMarketOrdersOpts>,
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
): Promise<OptimizerResultWithReport> {
return utils.getOptimizerResultAsync(nativeOrders, takerAmount, MarketOperation.Sell, opts);
}
@@ -92,7 +96,7 @@ async function getMarketBuyOrdersAsync(
utils: MarketOperationUtils,
nativeOrders: SignedNativeOrder[],
makerAmount: BigNumber,
opts?: Partial<GetMarketOrdersOpts>,
opts: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber },
): Promise<OptimizerResultWithReport> {
return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts);
}
@@ -455,7 +459,7 @@ describe('MarketOperationUtils tests', () => {
FILL_AMOUNT,
_.times(NUM_SAMPLES, i => DEFAULT_RATES[ERC20BridgeSource.Native][i]),
);
const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> = {
const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber } = {
numSamples: NUM_SAMPLES,
sampleDistributionBase: 1,
bridgeSlippage: 0,
@@ -464,6 +468,7 @@ describe('MarketOperationUtils tests', () => {
allowFallback: false,
gasSchedule: {},
feeSchedule: {},
gasPrice: new BigNumber(30e9),
};
beforeEach(() => {
@@ -745,7 +750,7 @@ describe('MarketOperationUtils tests', () => {
feeSchedule,
rfqt: {
isIndicative: false,
apiKey: 'foo',
integrator: FOO_INTEGRATOR,
takerAddress: randomAddress(),
txOrigin: randomAddress(),
intentOnFilling: true,
@@ -790,7 +795,7 @@ describe('MarketOperationUtils tests', () => {
...DEFAULT_OPTS,
rfqt: {
isIndicative: false,
apiKey: 'foo',
integrator: FOO_INTEGRATOR,
takerAddress: randomAddress(),
intentOnFilling: true,
txOrigin: randomAddress(),
@@ -837,7 +842,7 @@ describe('MarketOperationUtils tests', () => {
...DEFAULT_OPTS,
rfqt: {
isIndicative: true,
apiKey: 'foo',
integrator: FOO_INTEGRATOR,
takerAddress: randomAddress(),
txOrigin: randomAddress(),
intentOnFilling: true,
@@ -896,7 +901,10 @@ describe('MarketOperationUtils tests', () => {
...DEFAULT_OPTS,
rfqt: {
isIndicative: false,
apiKey: 'foo',
integrator: {
integratorId: 'foo',
label: 'foo',
},
takerAddress: randomAddress(),
intentOnFilling: true,
txOrigin: randomAddress(),
@@ -954,7 +962,7 @@ describe('MarketOperationUtils tests', () => {
...DEFAULT_OPTS,
rfqt: {
isIndicative: false,
apiKey: 'foo',
integrator: FOO_INTEGRATOR,
takerAddress: randomAddress(),
txOrigin: randomAddress(),
intentOnFilling: true,
@@ -1222,6 +1230,7 @@ describe('MarketOperationUtils tests', () => {
excludedSources: [],
numSamples: 4,
bridgeSlippage: 0,
gasPrice: new BigNumber(30e9),
},
);
const result = ordersAndReport.optimizedOrders;
@@ -1291,7 +1300,8 @@ describe('MarketOperationUtils tests', () => {
FILL_AMOUNT,
_.times(NUM_SAMPLES, () => DEFAULT_RATES[ERC20BridgeSource.Native][0]),
);
const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> = {
const GAS_PRICE = new BigNumber(100e9); // 100 gwei
const DEFAULT_OPTS: Partial<GetMarketOrdersOpts> & { gasPrice: BigNumber } = {
numSamples: NUM_SAMPLES,
sampleDistributionBase: 1,
bridgeSlippage: 0,
@@ -1300,6 +1310,7 @@ describe('MarketOperationUtils tests', () => {
allowFallback: false,
gasSchedule: {},
feeSchedule: {},
gasPrice: GAS_PRICE,
};
beforeEach(() => {
@@ -1619,11 +1630,10 @@ describe('MarketOperationUtils tests', () => {
getMedianSellRate: createGetMedianSellRate(ETH_TO_TAKER_RATE),
});
const optimizer = new MarketOperationUtils(MOCK_SAMPLER, contractAddresses, ORDER_DOMAIN);
const gasPrice = 100e9; // 100 gwei
const exchangeProxyOverhead = (sourceFlags: bigint) =>
sourceFlags === SOURCE_FLAGS.LiquidityProvider
? constants.ZERO_AMOUNT
: new BigNumber(1.3e5).times(gasPrice);
: new BigNumber(1.3e5).times(GAS_PRICE);
const improvedOrdersResponse = await optimizer.getOptimizerResultAsync(
createOrdersFromSellRates(FILL_AMOUNT, rates[ERC20BridgeSource.Native]),
FILL_AMOUNT,

View File

@@ -240,7 +240,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Sell,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -435,7 +438,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Sell,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -551,7 +557,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Sell,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -675,7 +684,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Sell,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -762,7 +774,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Sell,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -823,7 +838,10 @@ describe('QuoteRequestor', async () => {
MarketOperation.Buy,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin: takerAddress,
intentOnFilling: true,
@@ -834,6 +852,43 @@ describe('QuoteRequestor', async () => {
quoteRequestorHttpClient,
);
});
it('should be able to handle and filter RFQ offerings', () => {
const tests: Array<[string[] | undefined, string[]]> = [
[['https://top.maker'], []],
[undefined, ['https://foo.bar/', 'https://lorem.ipsum/']],
[['https://lorem.ipsum/'], ['https://lorem.ipsum/']],
];
for (const test of tests) {
const [apiKeyWhitelist, results] = test;
const response = QuoteRequestor.getTypedMakerUrlsAndWhitelist(
{
integrator: {
integratorId: 'foo',
label: 'bar',
whitelistIntegratorUrls: apiKeyWhitelist,
},
altRfqAssetOfferings: {},
},
{
'https://foo.bar/': [
[
'0xA6cD4cb8c62aCDe44739E3Ed0F1d13E0e31f2d94',
'0xF45107c0200a04A8aB9C600cc52A3C89AE5D0489',
],
],
'https://lorem.ipsum/': [
[
'0xA6cD4cb8c62aCDe44739E3Ed0F1d13E0e31f2d94',
'0xF45107c0200a04A8aB9C600cc52A3C89AE5D0489',
],
],
},
);
const typedUrls = response.map(typed => typed.url);
expect(typedUrls).to.eql(results);
}
});
it('should return successful alt indicative quotes', async () => {
const takerAddress = '0xd209925defc99488e3afff1174e48b4fa628302a';
const txOrigin = '0xf209925defc99488e3afff1174e48b4fa628302a';
@@ -1055,7 +1110,10 @@ describe('QuoteRequestor', async () => {
altScenario.requestedOperation,
undefined,
{
apiKey,
integrator: {
integratorId: apiKey,
label: 'foo',
},
takerAddress,
txOrigin,
intentOnFilling: true,

View File

@@ -744,7 +744,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: fillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
if (side === MarketOperation.Sell) {
expect(result.totalMakerAssetAmount).to.be.bignumber.eq(fillableOutput);
@@ -769,10 +769,10 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: fillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.eq(countCollapsedFills(orders));
expect(result.protocolFeeAmount).to.bignumber.gt(orders.length);
expect(result.protocolFeeAmount).to.bignumber.eq(orders.length);
expect(result.takerFeeTakerAssetAmount).to.bignumber.eq(0);
expect(result.takerFeeMakerAssetAmount).to.bignumber.eq(0);
expect(result.makerAssetAmount).to.bignumber.eq(result.totalMakerAssetAmount);
@@ -801,7 +801,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: inputFillAmount,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.gt(0);
expect(result.protocolFeeAmount).to.bignumber.gt(0);
@@ -835,7 +835,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: totalFillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
assertRoughlyEquals(result.takerAssetAmount, fillableInput);
@@ -865,7 +865,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: inputFillAmount,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.gt(0);
expect(result.protocolFeeAmount).to.bignumber.gt(0);
@@ -893,10 +893,10 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: fillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.eq(countCollapsedFills(orders));
expect(result.protocolFeeAmount).to.bignumber.gt(orders.length);
expect(result.protocolFeeAmount).to.bignumber.eq(orders.length);
assertRoughlyEquals(result.makerAssetAmount, fillableInput);
assertRoughlyEquals(result.totalMakerAssetAmount, fillableInput);
@@ -923,7 +923,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: inputFillAmount,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
expect(result.gas).to.gt(0);
expect(result.protocolFeeAmount).to.bignumber.gt(0);
@@ -951,7 +951,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: fillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE, slippage },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE, slippage },
});
if (side === MarketOperation.Sell) {
const slippedOutput = fillableOutput.times(1 - slippage).integerValue();
@@ -982,7 +982,7 @@ describe('quote_simulation tests', async () => {
side,
fillAmount: fillableInput,
gasPrice: ONE,
opts: { gasSchedule: GAS_SCHEDULE },
opts: { gasSchedule: GAS_SCHEDULE, protocolFeeMultiplier: ONE },
});
const worstCase = simulateWorstCaseFill({
orders,

View File

@@ -48,7 +48,11 @@ export const testHelpers = {
// Mock out Standard RFQ-T/M responses
for (const mockedResponse of standardMockedResponses) {
const { endpoint, requestApiKey, requestParams, responseData, responseCode } = mockedResponse;
const requestHeaders = { Accept: 'application/json, text/plain, */*', '0x-api-key': requestApiKey };
const requestHeaders = {
Accept: 'application/json, text/plain, */*',
'0x-api-key': requestApiKey,
'0x-integrator-id': requestApiKey,
};
if (mockedResponse.callback !== undefined) {
mockedAxios
.onGet(`${endpoint}/${quoteType}`, { params: requestParams }, requestHeaders)

View File

@@ -1,4 +1,14 @@
[
{
"version": "6.7.0",
"changes": [
{
"note": "Avalanche deployment",
"pr": 312
}
],
"timestamp": 1630459879
},
{
"timestamp": 1629353596,
"version": "6.6.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.7.0 - _September 1, 2021_
* Avalanche deployment (#312)
## v6.6.1 - _August 19, 2021_
* Dependencies updated

View File

@@ -334,5 +334,47 @@
"fillQuoteTransformer": "0x750cb81ee6d64e29e1e358ba155925000bf044d4",
"positiveSlippageFeeTransformer": "0x30aebc4c68effa70e21612b39b94299a8778d0cb"
}
},
"43114": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0xca7bab1b2d1ec7d81710b7f9e2ab4e6788930588",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyTransformerDeployer": "0xa60b57833dce6260f4f2411c811755dd980bc0a7",
"exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498",
"exchangeProxyLiquidityProviderSandbox": "0x8953c63d0858d286cc407cd6f8e26b9cbd02a511",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
"payTakerTransformer": "0x898c6fde239d646c73f0a57e3570b6f86a3d62a3",
"affiliateFeeTransformer": "0x34617b855411e52fbc05899435f44cbd0503022c",
"fillQuoteTransformer": "0x8a5417dd7ffde61ec61e11b45797e16686e1d6b9",
"positiveSlippageFeeTransformer": "0x470ba89da18a6db6e8a0567b3c9214b960861857"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-addresses",
"version": "6.6.1",
"version": "6.7.0",
"engines": {
"node": ">=6.12"
},

View File

@@ -52,6 +52,7 @@ export enum ChainId {
BSC = 56,
Polygon = 137,
PolygonMumbai = 80001,
Avalanche = 43114,
}
/**

View File

@@ -1,4 +1,23 @@
[
{
"version": "3.16.0",
"changes": [
{
"note": "Update IZeroEx and ITransformERC20 artifacts",
"pr": 282
}
],
"timestamp": 1632957537
},
{
"timestamp": 1631710679,
"version": "3.15.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "3.15.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.16.0 - _September 29, 2021_
* Update IZeroEx and ITransformERC20 artifacts (#282)
## v3.15.1 - _September 15, 2021_
* Dependencies updated
## v3.15.0 - _June 2, 2021_
* Update artifacts (#237)

View File

@@ -1,6 +1,6 @@
{
"schemaVersion": "2.0.0",
"contractName": "ITransformERC20",
"contractName": "ITransformERC20Feature",
"compilerOutput": {
"abi": [
{
@@ -43,14 +43,14 @@
{ "internalType": "uint32", "name": "deploymentNonce", "type": "uint32" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct ITransformERC20.Transformation[]",
"internalType": "struct ITransformERC20Feature.Transformation[]",
"name": "transformations",
"type": "tuple[]"
},
{ "internalType": "bytes32", "name": "callDataHash", "type": "bytes32" },
{ "internalType": "bytes", "name": "callDataSignature", "type": "bytes" }
{ "internalType": "bool", "name": "useSelfBalance", "type": "bool" },
{ "internalType": "address payable", "name": "recipient", "type": "address" }
],
"internalType": "struct ITransformERC20.TransformERC20Args",
"internalType": "struct ITransformERC20Feature.TransformERC20Args",
"name": "args",
"type": "tuple"
}
@@ -113,7 +113,7 @@
{ "internalType": "uint32", "name": "deploymentNonce", "type": "uint32" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct ITransformERC20.Transformation[]",
"internalType": "struct ITransformERC20Feature.Transformation[]",
"name": "transformations",
"type": "tuple[]"
}
@@ -148,7 +148,7 @@
},
"kind": "dev",
"methods": {
"_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[],bytes32,bytes))": {
"_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[],bool,address))": {
"details": "Internal version of `transformERC20()`. Only callable from within.",
"params": { "args": "A `TransformERC20Args` struct." },
"returns": { "outputTokenAmount": "The amount of `outputToken` received by the taker." }
@@ -212,7 +212,8 @@
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap"
"evm.deployedBytecode.sourceMap",
"evm.methodIdentifiers"
]
}
},

View File

@@ -3,16 +3,6 @@
"contractName": "IZeroEx",
"compilerOutput": {
"abi": [
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "bytes32", "name": "orderHash", "type": "bytes32" },
{ "indexed": false, "internalType": "address", "name": "maker", "type": "address" },
{ "indexed": false, "internalType": "uint64", "name": "expiry", "type": "uint64" }
],
"name": "ExpiredRfqOrder",
"type": "event"
},
{
"anonymous": false,
"inputs": [
@@ -49,11 +39,26 @@
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "address", "name": "inputToken", "type": "address" },
{ "indexed": false, "internalType": "address", "name": "outputToken", "type": "address" },
{
"indexed": false,
"internalType": "contract IERC20TokenV06",
"name": "inputToken",
"type": "address"
},
{
"indexed": false,
"internalType": "contract IERC20TokenV06",
"name": "outputToken",
"type": "address"
},
{ "indexed": false, "internalType": "uint256", "name": "inputTokenAmount", "type": "uint256" },
{ "indexed": false, "internalType": "uint256", "name": "outputTokenAmount", "type": "uint256" },
{ "indexed": false, "internalType": "address", "name": "provider", "type": "address" },
{
"indexed": false,
"internalType": "contract ILiquidityProvider",
"name": "provider",
"type": "address"
},
{ "indexed": false, "internalType": "address", "name": "recipient", "type": "address" }
],
"name": "LiquidityProviderSwap",
@@ -110,10 +115,10 @@
{
"indexed": false,
"internalType": "uint128",
"name": "takerTokenFilledAmount",
"name": "makerTokenFilledAmount",
"type": "uint128"
},
{ "indexed": false, "internalType": "uint128", "name": "makerTokenFilledAmount", "type": "uint128" }
{ "indexed": false, "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" }
],
"name": "OtcOrderFilled",
"type": "event"
@@ -268,6 +273,51 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "uint256", "name": "expiryAndNonce", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.OtcOrder",
"name": "order",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "makerSignature",
"type": "tuple"
},
{ "internalType": "uint128", "name": "takerTokenFillAmount", "type": "uint128" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "bool", "name": "useSelfBalance", "type": "bool" },
{ "internalType": "address", "name": "recipient", "type": "address" }
],
"name": "_fillOtcOrder",
"outputs": [
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "makerTokenFilledAmount", "type": "uint128" }
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
@@ -303,7 +353,9 @@
"type": "tuple"
},
{ "internalType": "uint128", "name": "takerTokenFillAmount", "type": "uint128" },
{ "internalType": "address", "name": "taker", "type": "address" }
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "bool", "name": "useSelfBalance", "type": "bool" },
{ "internalType": "address", "name": "recipient", "type": "address" }
],
"name": "_fillRfqOrder",
"outputs": [
@@ -313,6 +365,18 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "bytes", "name": "encodedPath", "type": "bytes" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" },
{ "internalType": "address", "name": "recipient", "type": "address" }
],
"name": "_sellHeldTokenForTokenToUniswapV3",
"outputs": [{ "internalType": "uint256", "name": "buyAmount", "type": "uint256" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
@@ -330,7 +394,9 @@
"internalType": "struct ITransformERC20Feature.Transformation[]",
"name": "transformations",
"type": "tuple[]"
}
},
{ "internalType": "bool", "name": "useSelfBalance", "type": "bool" },
{ "internalType": "address payable", "name": "recipient", "type": "address" }
],
"internalType": "struct ITransformERC20Feature.TransformERC20Args",
"name": "args",
@@ -480,35 +546,6 @@
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "inputToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "outputToken", "type": "address" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{
"components": [
{ "internalType": "bytes4", "name": "selector", "type": "bytes4" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.WrappedBatchCall[]",
"name": "calls",
"type": "tuple[]"
}
],
"internalType": "struct IMultiplexFeature.BatchFillData",
"name": "fillData",
"type": "tuple"
},
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "batchFill",
"outputs": [{ "internalType": "uint256", "name": "outputTokenAmount", "type": "uint256" }],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
@@ -601,6 +638,60 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "uint256", "name": "expiryAndNonce", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.OtcOrder[]",
"name": "orders",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature[]",
"name": "makerSignatures",
"type": "tuple[]"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature[]",
"name": "takerSignatures",
"type": "tuple[]"
},
{ "internalType": "bool[]", "name": "unwrapWeth", "type": "bool[]" }
],
"name": "batchFillTakerSignedOtcOrders",
"outputs": [{ "internalType": "bool[]", "name": "successes", "type": "bool[]" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
@@ -1026,8 +1117,7 @@
"name": "makerSignature",
"type": "tuple"
},
{ "internalType": "uint128", "name": "takerTokenFillAmount", "type": "uint128" },
{ "internalType": "bool", "name": "unwrapWeth", "type": "bool" }
{ "internalType": "uint128", "name": "takerTokenFillAmount", "type": "uint128" }
],
"name": "fillOtcOrder",
"outputs": [
@@ -1037,6 +1127,48 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "uint256", "name": "expiryAndNonce", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.OtcOrder",
"name": "order",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "makerSignature",
"type": "tuple"
},
{ "internalType": "uint128", "name": "takerTokenFillAmount", "type": "uint128" }
],
"name": "fillOtcOrderForEth",
"outputs": [
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "makerTokenFilledAmount", "type": "uint128" }
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
@@ -1168,14 +1300,63 @@
"internalType": "struct LibSignature.Signature",
"name": "takerSignature",
"type": "tuple"
},
{ "internalType": "bool", "name": "unwrapWeth", "type": "bool" }
}
],
"name": "fillTakerSignedOtcOrder",
"outputs": [
{ "internalType": "uint128", "name": "takerTokenFilledAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "makerTokenFilledAmount", "type": "uint128" }
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{ "internalType": "contract IERC20TokenV06", "name": "makerToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "takerToken", "type": "address" },
{ "internalType": "uint128", "name": "makerAmount", "type": "uint128" },
{ "internalType": "uint128", "name": "takerAmount", "type": "uint128" },
{ "internalType": "address", "name": "maker", "type": "address" },
{ "internalType": "address", "name": "taker", "type": "address" },
{ "internalType": "address", "name": "txOrigin", "type": "address" },
{ "internalType": "uint256", "name": "expiryAndNonce", "type": "uint256" }
],
"internalType": "struct LibNativeOrder.OtcOrder",
"name": "order",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "makerSignature",
"type": "tuple"
},
{
"components": [
{
"internalType": "enum LibSignature.SignatureType",
"name": "signatureType",
"type": "uint8"
},
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"internalType": "struct LibSignature.Signature",
"name": "takerSignature",
"type": "tuple"
}
],
"name": "fillTakerSignedOtcOrderForEth",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
@@ -1604,31 +1785,150 @@
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06", "name": "outputToken", "type": "address" },
{
"components": [
{ "internalType": "address[]", "name": "tokens", "type": "address[]" },
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{
"components": [
{ "internalType": "bytes4", "name": "selector", "type": "bytes4" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.WrappedMultiHopCall[]",
"name": "calls",
"type": "tuple[]"
}
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.MultiHopFillData",
"name": "fillData",
"type": "tuple"
"internalType": "struct IMultiplexFeature.BatchSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiHopFill",
"outputs": [{ "internalType": "uint256", "name": "outputTokenAmount", "type": "uint256" }],
"name": "multiplexBatchSellEthForToken",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06", "name": "inputToken", "type": "address" },
{
"components": [
{
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.BatchSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiplexBatchSellTokenForEth",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "contract IERC20TokenV06", "name": "inputToken", "type": "address" },
{ "internalType": "contract IERC20TokenV06", "name": "outputToken", "type": "address" },
{
"components": [
{
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.BatchSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiplexBatchSellTokenForToken",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address[]", "name": "tokens", "type": "address[]" },
{
"components": [
{
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.MultiHopSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiplexMultiHopSellEthForToken",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address[]", "name": "tokens", "type": "address[]" },
{
"components": [
{
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.MultiHopSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiplexMultiHopSellTokenForEth",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address[]", "name": "tokens", "type": "address[]" },
{
"components": [
{
"internalType": "enum IMultiplexFeature.MultiplexSubcall",
"name": "id",
"type": "uint8"
},
{ "internalType": "bytes", "name": "data", "type": "bytes" }
],
"internalType": "struct IMultiplexFeature.MultiHopSellSubcall[]",
"name": "calls",
"type": "tuple[]"
},
{ "internalType": "uint256", "name": "sellAmount", "type": "uint256" },
{ "internalType": "uint256", "name": "minBuyAmount", "type": "uint256" }
],
"name": "multiplexMultiHopSellTokenForToken",
"outputs": [{ "internalType": "uint256", "name": "boughtAmount", "type": "uint256" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
@@ -1819,20 +2119,47 @@
"takerTokenFilledAmount": "How much maker token was filled."
}
},
"_fillRfqOrder((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32),uint128,address)": {
"_fillOtcOrder((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),uint128,address,bool,address)": {
"details": "Fill an OTC order for up to `takerTokenFillAmount` taker tokens. Internal variant.",
"params": {
"makerSignature": "The order signature from the maker.",
"order": "The OTC order.",
"recipient": "The recipient of the bought maker tokens.",
"taker": "The address to fill the order in the context of.",
"takerTokenFillAmount": "Maximum taker token amount to fill this order with.",
"useSelfBalance": "Whether to use the Exchange Proxy's balance of input tokens."
},
"returns": {
"makerTokenFilledAmount": "How much maker token was filled.",
"takerTokenFilledAmount": "How much taker token was filled."
}
},
"_fillRfqOrder((address,address,uint128,uint128,address,address,address,bytes32,uint64,uint256),(uint8,uint8,bytes32,bytes32),uint128,address,bool,address)": {
"details": "Fill an RFQ order. Internal variant.",
"params": {
"order": "The RFQ order.",
"recipient": "The recipient of the maker tokens.",
"signature": "The order signature.",
"taker": "The order taker.",
"takerTokenFillAmount": "Maximum taker token to fill this order with."
"takerTokenFillAmount": "Maximum taker token to fill this order with.",
"useSelfBalance": "Whether to use the ExchangeProxy's transient balance of taker tokens to fill the order."
},
"returns": {
"makerTokenFilledAmount": "How much maker token was filled.",
"takerTokenFilledAmount": "How much maker token was filled."
}
},
"_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[]))": {
"_sellHeldTokenForTokenToUniswapV3(bytes,uint256,uint256,address)": {
"details": "Sell a token for another token directly against uniswap v3. Private variant, uses tokens held by `address(this)`.",
"params": {
"encodedPath": "Uniswap-encoded path.",
"minBuyAmount": "Minimum amount of the last token in the path to buy.",
"recipient": "The recipient of the bought tokens. Can be zero for sender.",
"sellAmount": "amount of the first token in the path to sell."
},
"returns": { "buyAmount": "Amount of the last token in the path bought." }
},
"_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[],bool,address))": {
"details": "Internal version of `transformERC20()`. Only callable from within.",
"params": { "args": "A `TransformERC20Args` struct." },
"returns": { "outputTokenAmount": "The amount of `outputToken` received by the taker." }
@@ -1887,14 +2214,6 @@
},
"returns": { "returnResults": "The ABI-encoded results of the underlying calls." }
},
"batchFill((address,address,uint256,(bytes4,uint256,bytes)[]),uint256)": {
"details": "Executes a batch of fills selling `fillData.inputToken` for `fillData.outputToken` in sequence. Refer to the internal variant `_batchFill` for the allowed nested operations.",
"params": {
"fillData": "Encodes the input/output tokens, the sell amount, and the nested operations for this batch fill.",
"minBuyAmount": "The minimum amount of `fillData.outputToken` to buy. Reverts if this amount is not met."
},
"returns": { "outputTokenAmount": "The amount of the output token bought." }
},
"batchFillLimitOrders((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[],uint128[],bool)": {
"details": "Fills multiple limit orders.",
"params": {
@@ -1921,6 +2240,18 @@
"takerTokenFilledAmounts": "Array of amounts filled, in taker token."
}
},
"batchFillTakerSignedOtcOrders((address,address,uint128,uint128,address,address,address,uint256)[],(uint8,uint8,bytes32,bytes32)[],(uint8,uint8,bytes32,bytes32)[],bool[])": {
"details": "Fills multiple taker-signed OTC orders.",
"params": {
"makerSignatures": "Array of maker signatures for each order.",
"orders": "Array of OTC orders.",
"takerSignatures": "Array of taker signatures for each order.",
"unwrapWeth": "Array of booleans representing whether or not to unwrap bought WETH into ETH for each order. Should be set to false if the maker token is not WETH."
},
"returns": {
"successes": "Array of booleans representing whether or not each order in `orders` was filled successfully."
}
},
"batchGetLimitOrderRelevantStates((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256)[],(uint8,uint8,bytes32,bytes32)[])": {
"details": "Batch version of `getLimitOrderRelevantState()`, without reverting. Orders that would normally cause `getLimitOrderRelevantState()` to revert will have empty results.",
"params": { "orders": "The limit orders.", "signatures": "The order signatures." },
@@ -2027,13 +2358,24 @@
},
"returns": { "makerTokenFilledAmount": "How much maker token was filled." }
},
"fillOtcOrder((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),uint128,bool)": {
"fillOtcOrder((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),uint128)": {
"details": "Fill an OTC order for up to `takerTokenFillAmount` taker tokens.",
"params": {
"makerSignature": "The order signature from the maker.",
"order": "The OTC order.",
"takerTokenFillAmount": "Maximum taker token amount to fill this order with.",
"unwrapWeth": "Whether or not to unwrap bought WETH into ETH before transferring it to the taker. Should be set to false"
"takerTokenFillAmount": "Maximum taker token amount to fill this order with."
},
"returns": {
"makerTokenFilledAmount": "How much maker token was filled.",
"takerTokenFilledAmount": "How much taker token was filled."
}
},
"fillOtcOrderForEth((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),uint128)": {
"details": "Fill an OTC order for up to `takerTokenFillAmount` taker tokens. Unwraps bought WETH into ETH before sending it to the taker.",
"params": {
"makerSignature": "The order signature from the maker.",
"order": "The OTC order.",
"takerTokenFillAmount": "Maximum taker token amount to fill this order with."
},
"returns": {
"makerTokenFilledAmount": "How much maker token was filled.",
@@ -2060,17 +2402,20 @@
"takerTokenFilledAmount": "How much maker token was filled."
}
},
"fillTakerSignedOtcOrder((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),(uint8,uint8,bytes32,bytes32),bool)": {
"fillTakerSignedOtcOrder((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),(uint8,uint8,bytes32,bytes32))": {
"details": "Fully fill an OTC order. \"Meta-transaction\" variant, requires order to be signed by both maker and taker.",
"params": {
"makerSignature": "The order signature from the maker.",
"order": "The OTC order.",
"takerSignature": "The order signature from the taker.",
"unwrapWeth": "Whether or not to unwrap bought WETH into ETH before transferring it to the taker. Should be set to false if the maker token is not WETH."
},
"returns": {
"makerTokenFilledAmount": "How much maker token was filled.",
"takerTokenFilledAmount": "How much taker token was filled."
"takerSignature": "The order signature from the taker."
}
},
"fillTakerSignedOtcOrderForEth((address,address,uint128,uint128,address,address,address,uint256),(uint8,uint8,bytes32,bytes32),(uint8,uint8,bytes32,bytes32))": {
"details": "Fully fill an OTC order. \"Meta-transaction\" variant, requires order to be signed by both maker and taker. Unwraps bought WETH into ETH before sending it to the taker.",
"params": {
"makerSignature": "The order signature from the maker.",
"order": "The OTC order.",
"takerSignature": "The order signature from the taker."
}
},
"getLimitOrderHash((address,address,uint128,uint128,uint128,address,address,address,address,bytes32,uint64,uint256))": {
@@ -2184,13 +2529,62 @@
"target": "The migrator contract address."
}
},
"multiHopFill((address[],uint256,(bytes4,bytes)[]),uint256)": {
"details": "Executes a sequence of fills \"hopping\" through the path of tokens given by `fillData.tokens`. Refer to the internal variant `_multiHopFill` for the allowed nested operations.",
"multiplexBatchSellEthForToken(address,(uint8,uint256,bytes)[],uint256)": {
"details": "Sells attached ETH for `outputToken` using the provided calls.",
"params": {
"fillData": "Encodes the path of tokens, the sell amount, and the nested operations for this multi-hop fill.",
"minBuyAmount": "The minimum amount of the output token to buy. Reverts if this amount is not met."
"calls": "The calls to use to sell the attached ETH.",
"minBuyAmount": "The minimum amount of `outputToken` that must be bought for this function to not revert.",
"outputToken": "The token to buy."
},
"returns": { "outputTokenAmount": "The amount of the output token bought." }
"returns": { "boughtAmount": "The amount of `outputToken` bought." }
},
"multiplexBatchSellTokenForEth(address,(uint8,uint256,bytes)[],uint256,uint256)": {
"details": "Sells `sellAmount` of the given `inputToken` for ETH using the provided calls.",
"params": {
"calls": "The calls to use to sell the input tokens.",
"inputToken": "The token to sell.",
"minBuyAmount": "The minimum amount of ETH that must be bought for this function to not revert.",
"sellAmount": "The amount of `inputToken` to sell."
},
"returns": { "boughtAmount": "The amount of ETH bought." }
},
"multiplexBatchSellTokenForToken(address,address,(uint8,uint256,bytes)[],uint256,uint256)": {
"details": "Sells `sellAmount` of the given `inputToken` for `outputToken` using the provided calls.",
"params": {
"calls": "The calls to use to sell the input tokens.",
"inputToken": "The token to sell.",
"minBuyAmount": "The minimum amount of `outputToken` that must be bought for this function to not revert.",
"outputToken": "The token to buy.",
"sellAmount": "The amount of `inputToken` to sell."
},
"returns": { "boughtAmount": "The amount of `outputToken` bought." }
},
"multiplexMultiHopSellEthForToken(address[],(uint8,bytes)[],uint256)": {
"details": "Sells attached ETH via the given sequence of tokens and calls. `tokens[0]` must be WETH. The last token in `tokens` is the output token that will ultimately be sent to `msg.sender`",
"params": {
"calls": "The sequence of calls to use for the sell.",
"minBuyAmount": "The minimum amount of output tokens that must be bought for this function to not revert.",
"tokens": "The sequence of tokens to use for the sell, i.e. `tokens[i]` will be sold for `tokens[i+1]` via `calls[i]`."
},
"returns": { "boughtAmount": "The amount of output tokens bought." }
},
"multiplexMultiHopSellTokenForEth(address[],(uint8,bytes)[],uint256,uint256)": {
"details": "Sells `sellAmount` of the input token (`tokens[0]`) for ETH via the given sequence of tokens and calls. The last token in `tokens` must be WETH.",
"params": {
"calls": "The sequence of calls to use for the sell.",
"minBuyAmount": "The minimum amount of ETH that must be bought for this function to not revert.",
"tokens": "The sequence of tokens to use for the sell, i.e. `tokens[i]` will be sold for `tokens[i+1]` via `calls[i]`."
},
"returns": { "boughtAmount": "The amount of ETH bought." }
},
"multiplexMultiHopSellTokenForToken(address[],(uint8,bytes)[],uint256,uint256)": {
"details": "Sells `sellAmount` of the input token (`tokens[0]`) via the given sequence of tokens and calls. The last token in `tokens` is the output token that will ultimately be sent to `msg.sender`",
"params": {
"calls": "The sequence of calls to use for the sell.",
"minBuyAmount": "The minimum amount of output tokens that must be bought for this function to not revert.",
"tokens": "The sequence of tokens to use for the sell, i.e. `tokens[i]` will be sold for `tokens[i+1]` via `calls[i]`."
},
"returns": { "boughtAmount": "The amount of output tokens bought." }
},
"owner()": {
"details": "The owner of this contract.",

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-artifacts",
"version": "3.15.0",
"version": "3.16.0",
"engines": {
"node": ">=6.12"
},
@@ -30,7 +30,7 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/packages/contract-artifacts",
"devDependencies": {
"@0x/utils": "^6.4.3",
"@0x/utils": "^6.4.4",
"@types/mocha": "^5.2.7",
"chai": "^4.0.1",
"lodash": "^4.17.11",

View File

@@ -1,4 +1,32 @@
[
{
"version": "13.18.0",
"changes": [
{
"note": "Update IZeroEx and ITransformERC20 wrappers",
"pr": 282
}
],
"timestamp": 1632957537
},
{
"timestamp": 1631710679,
"version": "13.17.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1630459879,
"version": "13.17.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629353596,
"version": "13.17.5",

View File

@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v13.18.0 - _September 29, 2021_
* Update IZeroEx and ITransformERC20 wrappers (#282)
## v13.17.7 - _September 15, 2021_
* Dependencies updated
## v13.17.6 - _September 1, 2021_
* Dependencies updated
## v13.17.5 - _August 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers",
"version": "13.17.5",
"version": "13.18.0",
"engines": {
"node": ">=6.12"
},
@@ -55,14 +55,14 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/json-schemas": "^6.1.3",
"@0x/types": "^3.3.3",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/json-schemas": "^6.3.0",
"@0x/types": "^3.3.4",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"ethereum-types": "^3.6.0",
"ethers": "~4.0.4"
},
"publishConfig": {

View File

@@ -269,12 +269,12 @@ export class ITransformERC20Contract extends BaseContract {
],
},
{
name: 'callDataHash',
type: 'bytes32',
name: 'useSelfBalance',
type: 'bool',
},
{
name: 'callDataSignature',
type: 'bytes',
name: 'recipient',
type: 'address',
},
],
},
@@ -497,13 +497,13 @@ export class ITransformERC20Contract extends BaseContract {
inputTokenAmount: BigNumber;
minOutputTokenAmount: BigNumber;
transformations: Array<{ deploymentNonce: number | BigNumber; data: string }>;
callDataHash: string;
callDataSignature: string;
useSelfBalance: boolean;
recipient: string;
}): ContractTxFunctionObj<BigNumber> {
const self = (this as any) as ITransformERC20Contract;
const functionSignature =
'_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[],bytes32,bytes))';
'_transformERC20((address,address,address,uint256,uint256,(uint32,bytes)[],bool,address))';
return {
async sendTransactionAsync(

File diff suppressed because it is too large Load Diff

View File

@@ -126,7 +126,6 @@ export {
IZeroExContract,
IZeroExEventArgs,
IZeroExEvents,
IZeroExExpiredRfqOrderEventArgs,
IZeroExLiquidityProviderSwapEventArgs,
IZeroExMetaTransactionExecutedEventArgs,
IZeroExMigratedEventArgs,

View File

@@ -1,4 +1,49 @@
[
{
"timestamp": 1633374058,
"version": "8.1.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1632957537,
"version": "8.1.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631710679,
"version": "8.1.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1631120757,
"version": "8.1.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1630459879,
"version": "8.1.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629414734,
"version": "8.1.3",

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v8.1.8 - _October 4, 2021_
* Dependencies updated
## v8.1.7 - _September 29, 2021_
* Dependencies updated
## v8.1.6 - _September 15, 2021_
* Dependencies updated
## v8.1.5 - _September 8, 2021_
* Dependencies updated
## v8.1.4 - _September 1, 2021_
* Dependencies updated
## v8.1.3 - _August 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
"version": "8.1.3",
"version": "8.1.8",
"engines": {
"node": ">=6.12"
},
@@ -48,10 +48,10 @@
"registry": "git@github.com:0xProject/gitpkg-registry.git"
},
"devDependencies": {
"@0x/dev-utils": "^4.2.7",
"@0x/dev-utils": "^4.2.9",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/types": "^3.3.4",
"@types/yargs": "^11.0.0",
"chai": "^4.0.1",
"dirty-chai": "^2.0.1",
@@ -67,29 +67,29 @@
"yargs": "^10.0.3"
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.6.1",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.7.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-coordinator": "^3.1.38",
"@0x/contracts-dev-utils": "^1.3.36",
"@0x/contracts-erc1155": "^2.1.37",
"@0x/contracts-erc20": "^3.3.17",
"@0x/contracts-erc20": "^3.3.20",
"@0x/contracts-erc721": "^3.1.37",
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-forwarder": "^4.2.38",
"@0x/contracts-extensions": "^6.2.32",
"@0x/contracts-multisig": "^4.1.38",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-utils": "^4.7.17",
"@0x/contracts-zero-ex": "^0.28.2",
"@0x/sol-compiler": "^4.7.3",
"@0x/subproviders": "^6.5.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/contracts-utils": "^4.8.1",
"@0x/contracts-zero-ex": "^0.29.1",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"@ledgerhq/hw-app-eth": "^4.3.0",
"@types/web3-provider-engine": "^14.0.0",
"ethereum-types": "^3.5.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.1.0",
"ethers": "~4.0.4",
"lodash": "^4.17.11"

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1633374058,
"version": "1.9.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1632957537,
"version": "1.9.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.9.0",
"changes": [
{
"note": "Add 'TreasuryVote' class"
}
],
"timestamp": 1631710679
},
{
"timestamp": 1630459879,
"version": "1.8.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1629353596,
"version": "1.8.3",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.9.2 - _October 4, 2021_
* Dependencies updated
## v1.9.1 - _September 29, 2021_
* Dependencies updated
## v1.9.0 - _September 15, 2021_
* Add 'TreasuryVote' class
## v1.8.4 - _September 1, 2021_
* Dependencies updated
## v1.8.3 - _August 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "1.8.3",
"version": "1.9.2",
"engines": {
"node": ">=6.12"
},
@@ -41,17 +41,17 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/packages/protocol-utils",
"devDependencies": {
"@0x/dev-utils": "^4.2.7",
"@0x/dev-utils": "^4.2.9",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
"@types/web3-provider-engine": "^14.0.0",
"ethereum-types": "^3.5.0",
"ethereum-types": "^3.6.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -62,13 +62,13 @@
"web3-provider-engine": "14.0.6"
},
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/contract-addresses": "^6.6.1",
"@0x/contract-wrappers": "^13.17.5",
"@0x/json-schemas": "^6.1.3",
"@0x/subproviders": "^6.5.3",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@0x/assert": "^3.0.29",
"@0x/contract-addresses": "^6.7.0",
"@0x/contract-wrappers": "^13.18.0",
"@0x/json-schemas": "^6.3.0",
"@0x/subproviders": "^6.6.0",
"@0x/utils": "^6.4.4",
"@0x/web3-wrapper": "^7.6.0",
"chai": "^4.0.1",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4",

View File

@@ -9,3 +9,4 @@ export * from './signature_utils';
export * from './transformer_utils';
export * from './constants';
export * from './vip_utils';
export * from './treasury_votes';

View File

@@ -131,7 +131,7 @@ export enum BridgeProtocol {
KyberDmm,
CurveV2,
Lido,
Clipper,
Clipper, // Not used: Clipper is now using PLP interface
}
// tslint:enable: enum-naming

View File

@@ -0,0 +1,85 @@
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import * as ethUtil from 'ethereumjs-util';
import { ZERO } from './constants';
import { EIP712_DOMAIN_PARAMETERS, getTypeHash } from './eip712_utils';
import { eip712SignHashWithKey, Signature } from './signature_utils';
const VOTE_DEFAULT_VALUES = {
proposalId: ZERO,
support: false,
operatedPoolIds: [] as string[],
chainId: 1,
version: '1.0.0',
verifyingContract: NULL_ADDRESS,
};
export type TreasuryVoteFields = typeof VOTE_DEFAULT_VALUES;
export class TreasuryVote {
public static readonly CONTRACT_NAME = 'Zrx Treasury';
public static readonly MESSAGE_STRUCT_NAME = 'TreasuryVote';
public static readonly MESSAGE_STRUCT_ABI = [
{ type: 'uint256', name: 'proposalId' },
{ type: 'bool', name: 'support' },
{ type: 'bytes32[]', name: 'operatedPoolIds' },
];
public static readonly MESSAGE_TYPE_HASH = getTypeHash(
TreasuryVote.MESSAGE_STRUCT_NAME,
TreasuryVote.MESSAGE_STRUCT_ABI,
);
public static readonly DOMAIN_STRUCT_NAME = 'EIP712Domain';
public static readonly DOMAIN_TYPE_HASH = getTypeHash(TreasuryVote.DOMAIN_STRUCT_NAME, EIP712_DOMAIN_PARAMETERS);
public proposalId: BigNumber;
public support: boolean;
public operatedPoolIds: string[];
public chainId: number;
public version: string;
public verifyingContract: string;
constructor(fields: Partial<TreasuryVoteFields> = {}) {
const _fields = { ...VOTE_DEFAULT_VALUES, ...fields };
this.proposalId = _fields.proposalId;
this.support = _fields.support;
this.operatedPoolIds = _fields.operatedPoolIds;
this.chainId = _fields.chainId;
this.version = _fields.version;
this.verifyingContract = _fields.verifyingContract;
}
public getDomainHash(): string {
return hexUtils.hash(
hexUtils.concat(
hexUtils.leftPad(TreasuryVote.DOMAIN_TYPE_HASH),
hexUtils.hash(hexUtils.toHex(Buffer.from(TreasuryVote.CONTRACT_NAME))),
hexUtils.leftPad(this.chainId),
hexUtils.hash(hexUtils.toHex(Buffer.from(this.version))),
hexUtils.leftPad(this.verifyingContract),
),
);
}
public getStructHash(): string {
return hexUtils.hash(
hexUtils.concat(
hexUtils.leftPad(TreasuryVote.MESSAGE_TYPE_HASH),
hexUtils.leftPad(this.proposalId),
hexUtils.leftPad(this.support ? 1 : 0),
hexUtils.hash(
ethUtil.toBuffer(hexUtils.concat(...this.operatedPoolIds.map(id => hexUtils.leftPad(id)))),
),
),
);
}
public getEIP712Hash(): string {
return hexUtils.hash(hexUtils.toHex(hexUtils.concat('0x1901', this.getDomainHash(), this.getStructHash())));
}
public getSignatureWithKey(privateKey: string): Signature {
return eip712SignHashWithKey(this.getEIP712Hash(), privateKey);
}
}

310
yarn.lock
View File

@@ -643,19 +643,20 @@
npmlog "^4.1.2"
write-file-atomic "^2.3.0"
"@0x/abi-gen@^5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@0x/abi-gen/-/abi-gen-5.6.0.tgz#2b022efe77a35f5589624633d880d794d0a16bde"
"@0x/abi-gen@^5.6.2":
version "5.6.2"
resolved "https://registry.yarnpkg.com/@0x/abi-gen/-/abi-gen-5.6.2.tgz#a31b26b93b061ceb8513424d622119c78b3ed0e9"
integrity sha512-G+B9/VEfsOc8IOgUgkIdFiC+0RNg1DTR921WOxOxcvLsls9m9lBudl7hFrU03N1QdbFFP08e+HDj1kS2Q+SzEQ==
dependencies:
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@types/node" "12.12.54"
"@types/toposort" "^2.0.1"
chalk "^2.3.0"
change-case "^3.0.2"
cli-format "^3.0.9"
ethereum-types "^3.5.0"
ethereum-types "^3.6.0"
glob "^7.1.2"
handlebars "^4.1.2"
lodash "^4.17.11"
@@ -676,6 +677,18 @@
lodash "^4.17.11"
valid-url "^1.0.9"
"@0x/assert@^3.0.29":
version "3.0.29"
resolved "https://registry.yarnpkg.com/@0x/assert/-/assert-3.0.29.tgz#604e415b943140a1577c9389b04766909a60b589"
integrity sha512-EiF8TwCtM17KFcLy2FYVzu3+7K51VyUNs9ZQ/Wf8tWDeTWrIh1ixKGsNtRnL9sR5SPqOLwKR3lbGn+zPlxw+Yw==
dependencies:
"@0x/json-schemas" "^6.3.0"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@types/node" "12.12.54"
lodash "^4.17.11"
valid-url "^1.0.9"
"@0x/assert@^3.0.6":
version "3.0.21"
resolved "https://registry.yarnpkg.com/@0x/assert/-/assert-3.0.21.tgz#b385868d1833625912fd9173a2477be5a4090aed"
@@ -704,6 +717,24 @@
js-sha3 "^0.7.0"
uuid "^3.3.2"
"@0x/base-contract@^6.4.2":
version "6.4.2"
resolved "https://registry.yarnpkg.com/@0x/base-contract/-/base-contract-6.4.2.tgz#d87cb5416613d29d2ec71c60d4a7c6cdd5c48694"
integrity sha512-lcmsXGJ2ImiO1tJoWefYiQ8/WRSdQ4BPA8XulYqVQ4su6PYjLa1XvU91zM779QrIPeRo8fL7FUvGE7CkyG/gwA==
dependencies:
"@0x/assert" "^3.0.29"
"@0x/json-schemas" "^6.3.0"
"@0x/utils" "^6.4.4"
"@0x/web3-wrapper" "^7.6.0"
"@types/node" "12.12.54"
ethereumjs-account "^3.0.0"
ethereumjs-blockstream "^7.0.0"
ethereumjs-util "^7.1.0"
ethereumjs-vm "^4.2.0"
ethers "~4.0.4"
js-sha3 "^0.7.0"
uuid "^3.3.2"
"@0x/contracts-asset-proxy@^3.7.19":
version "3.7.19"
resolved "https://registry.yarnpkg.com/@0x/contracts-asset-proxy/-/contracts-asset-proxy-3.7.19.tgz#ee621a233f4d77b439c74c5b8d70db2e1ed001c4"
@@ -804,17 +835,18 @@
"@0x/typescript-typings" "^5.2.0"
ethereum-types "^3.5.0"
"@0x/contracts-gen@^2.0.38":
version "2.0.38"
resolved "https://registry.yarnpkg.com/@0x/contracts-gen/-/contracts-gen-2.0.38.tgz#6f2977e2bcb299b5e8a32f45d7eca73d19e34c50"
"@0x/contracts-gen@^2.0.40":
version "2.0.40"
resolved "https://registry.yarnpkg.com/@0x/contracts-gen/-/contracts-gen-2.0.40.tgz#526c25991125b5a4deb745e470b3c64cd8673095"
integrity sha512-Luj6R4DtPI7KHr3tUSdarudNiySd6GY1mvfhu8566K76oq4aK1no1hf0pyvy9tQLJDViNePP8Ad5KcEmC89sAg==
dependencies:
"@0x/sol-compiler" "^4.7.3"
"@0x/sol-resolver" "^3.1.8"
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/sol-compiler" "^4.7.5"
"@0x/sol-resolver" "^3.1.9"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@types/node" "12.12.54"
ethereum-types "^3.5.0"
ethereum-types "^3.6.0"
lodash "^4.17.11"
mkdirp "^0.5.1"
prettier "^1.16.3"
@@ -839,36 +871,23 @@
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
"@0x/contracts-zero-ex@^0.27.0":
version "0.27.1"
resolved "https://registry.yarnpkg.com/@0x/contracts-zero-ex/-/contracts-zero-ex-0.27.1.tgz#968fe9d8134972cb464f7c4a33c4e4089ba9218e"
"@0x/dev-utils@^4.2.9":
version "4.2.9"
resolved "https://registry.yarnpkg.com/@0x/dev-utils/-/dev-utils-4.2.9.tgz#b048b139b0055ef3702682c42ccc2a3788a49f5d"
integrity sha512-juIjVvky0umt7Tmzhz2PF7e7pQEe1hbrV2XyB5tocRQVAsTD+TuwTG9VVKULQUptX+B/mF1mjb3WwEQV6y/yTQ==
dependencies:
"@0x/base-contract" "^6.4.0"
"@0x/protocol-utils" "^1.8.1"
"@0x/subproviders" "^6.5.3"
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/web3-wrapper" "^7.5.3"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
"@0x/dev-utils@^4.2.7":
version "4.2.7"
resolved "https://registry.yarnpkg.com/@0x/dev-utils/-/dev-utils-4.2.7.tgz#9c85a134cace5a423a75221241fd687df81f3bbc"
dependencies:
"@0x/subproviders" "^6.5.3"
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/web3-wrapper" "^7.5.3"
"@0x/subproviders" "^6.6.0"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@0x/web3-wrapper" "^7.6.0"
"@types/node" "12.12.54"
"@types/web3-provider-engine" "^14.0.0"
chai "^4.0.1"
chai-as-promised "^7.1.0"
chai-bignumber "^3.0.0"
dirty-chai "^2.0.1"
ethereum-types "^3.5.0"
ethereum-types "^3.6.0"
lodash "^4.17.11"
web3-provider-engine "14.0.6"
@@ -890,6 +909,16 @@
ajv "^6.12.5"
lodash.values "^4.3.0"
"@0x/json-schemas@^6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@0x/json-schemas/-/json-schemas-6.3.0.tgz#b7addf8167af492f6667561caa9a62b3ec567696"
integrity sha512-cygnTxvJhLYcDeI05Olp4CN0BKQRs2rC3L0gRCU+mV/IMDNYlkKqNluHmzoHLBleN55uLZZZybSLOsbE5HfIlQ==
dependencies:
"@0x/typescript-typings" "^5.2.1"
"@types/node" "12.12.54"
ajv "^6.12.5"
lodash.values "^4.3.0"
"@0x/mesh-rpc-client@^9.4.2":
version "9.4.2"
resolved "https://registry.yarnpkg.com/@0x/mesh-rpc-client/-/mesh-rpc-client-9.4.2.tgz#6f9690fb1cb37fb0c2fd3907241af0e543c78451"
@@ -930,6 +959,13 @@
typedoc "~0.16.11"
yargs "^10.0.3"
"@0x/neon-router@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.1.3.tgz#70da4c17ca4b59dfe8b5e539673e364a70e62ebd"
integrity sha512-EfdrG829NalYjAK5/nMTD6YyJQgUzgssL2Hvyphu1ugWxWlZ3QMM9qpZsKt82hUiyZT/64I4JJ3hkerMhTaHeg==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.5"
"@0x/order-utils@^10.2.4", "@0x/order-utils@^10.4.28":
version "10.4.28"
resolved "https://registry.yarnpkg.com/@0x/order-utils/-/order-utils-10.4.28.tgz#c7b2f7d87a7f9834f9aa6186fbac68f32a05a81d"
@@ -944,9 +980,10 @@
ethers "~4.0.4"
lodash "^4.17.11"
"@0x/quote-server@^6.0.2":
version "6.0.2"
resolved "https://registry.yarnpkg.com/@0x/quote-server/-/quote-server-6.0.2.tgz#cb99e00c737e0f97a2a32bc7e7be6db65243c3af"
"@0x/quote-server@^6.0.6":
version "6.0.6"
resolved "https://registry.yarnpkg.com/@0x/quote-server/-/quote-server-6.0.6.tgz#0f0bf50647efc4bff039a491689974af7e8c5776"
integrity sha512-ubugDwCFDhOv8R8LWO4Z9BmWfm/KjbB92bg1nEHw2HzosOk1rLkQWnPCJGqbMzxHlt3EtLxXPrrZE2IxWBKgwQ==
dependencies:
"@0x/json-schemas" "^6.0.1"
"@0x/order-utils" "^10.2.4"
@@ -957,23 +994,24 @@
express-async-handler "^1.1.4"
http-status-codes "^1.4.0"
"@0x/sol-compiler@^4.7.3":
version "4.7.3"
resolved "https://registry.yarnpkg.com/@0x/sol-compiler/-/sol-compiler-4.7.3.tgz#d994661bc9c06a0a63b0e2f77ee6511d3cef488e"
"@0x/sol-compiler@^4.7.5":
version "4.7.5"
resolved "https://registry.yarnpkg.com/@0x/sol-compiler/-/sol-compiler-4.7.5.tgz#f744f836786f44747cfd23eb167067ce6f348136"
integrity sha512-vVsMNFLsR7ORuriZXCWloEhDZh3loaTkiFgFI3zTne7wOCwyMntZkgA7uij/iyOGrZW0XEbxAA+QjAYenroRfQ==
dependencies:
"@0x/assert" "^3.0.27"
"@0x/json-schemas" "^6.1.3"
"@0x/sol-resolver" "^3.1.8"
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/web3-wrapper" "^7.5.3"
"@0x/assert" "^3.0.29"
"@0x/json-schemas" "^6.3.0"
"@0x/sol-resolver" "^3.1.9"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@0x/web3-wrapper" "^7.6.0"
"@types/node" "12.12.54"
"@types/yargs" "^11.0.0"
chalk "^2.3.0"
chokidar "^3.0.2"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
lodash "^4.17.11"
mkdirp "^0.5.1"
pluralize "^7.0.0"
@@ -985,74 +1023,79 @@
web3-eth-abi "^1.0.0-beta.24"
yargs "^10.0.3"
"@0x/sol-coverage@^4.0.37":
version "4.0.37"
resolved "https://registry.yarnpkg.com/@0x/sol-coverage/-/sol-coverage-4.0.37.tgz#951363f1497cc65edf9bc76f37ac7824667e2c2b"
"@0x/sol-coverage@^4.0.39":
version "4.0.39"
resolved "https://registry.yarnpkg.com/@0x/sol-coverage/-/sol-coverage-4.0.39.tgz#27db909a3f35c625bbf271fa7ecd693eb880ed53"
integrity sha512-mARTgkNX4xkY8UVeYO1oQ0+iY9OJyeRHZbuiWizdeCRuoZth7qk8F1xbywZTCPyo6Pa4zmusi7poKRfHIFTFHA==
dependencies:
"@0x/sol-tracing-utils" "^7.2.3"
"@0x/subproviders" "^6.5.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/sol-tracing-utils" "^7.2.5"
"@0x/subproviders" "^6.6.0"
"@0x/typescript-typings" "^5.2.1"
"@types/minimatch" "^3.0.3"
"@types/node" "12.12.54"
ethereum-types "^3.5.0"
ethereum-types "^3.6.0"
lodash "^4.17.11"
minimatch "^3.0.4"
web3-provider-engine "14.0.6"
"@0x/sol-profiler@^4.1.27":
version "4.1.27"
resolved "https://registry.yarnpkg.com/@0x/sol-profiler/-/sol-profiler-4.1.27.tgz#2bd14882dd204a7465b494149877daa16d86208d"
"@0x/sol-profiler@^4.1.29":
version "4.1.29"
resolved "https://registry.yarnpkg.com/@0x/sol-profiler/-/sol-profiler-4.1.29.tgz#e77e0ae50541e8acaf7cecb5378a60efd4282f11"
integrity sha512-4CbrNan9xF3auv0ZwjsoajgpqLO23eqcq1u9seSVriNs5IGxEOq4U5xxofvFIMaS0NQXnHJobuUT2qRayBbgkw==
dependencies:
"@0x/sol-tracing-utils" "^7.2.3"
"@0x/subproviders" "^6.5.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/sol-tracing-utils" "^7.2.5"
"@0x/subproviders" "^6.6.0"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@types/node" "12.12.54"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
lodash "^4.17.11"
web3-provider-engine "14.0.6"
"@0x/sol-resolver@^3.1.8":
version "3.1.8"
resolved "https://registry.yarnpkg.com/@0x/sol-resolver/-/sol-resolver-3.1.8.tgz#eaaaf17052e88213e55daf2c6e39a585cc16fa0a"
"@0x/sol-resolver@^3.1.9":
version "3.1.9"
resolved "https://registry.yarnpkg.com/@0x/sol-resolver/-/sol-resolver-3.1.9.tgz#525c545c4ff4d0ff2ff99e433b2405778abe0693"
integrity sha512-N+GxAqtHzEgVsnj9k4yeE7xRqE2ymR+yo98j0s2VC8icjecVqm6LtqQpEpdPULEg20vA0aPdU/XY2q0xiCDpLg==
dependencies:
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@types/node" "12.12.54"
lodash "^4.17.11"
"@0x/sol-trace@^3.0.37":
version "3.0.37"
resolved "https://registry.yarnpkg.com/@0x/sol-trace/-/sol-trace-3.0.37.tgz#915a1c7c4869f9a95994fdb24878997d5bc39450"
"@0x/sol-trace@^3.0.39":
version "3.0.39"
resolved "https://registry.yarnpkg.com/@0x/sol-trace/-/sol-trace-3.0.39.tgz#caca4fbf049eda25185c09ab00c23cf37d44d9a2"
integrity sha512-Dg+jPjCnSmWL4t/tq/kQY8NOnAWy/g4HjFQYyL6uz8ioJ4gvCCV+2UADATb2OA7bqrvtbADJrw7icJ+/laqXuA==
dependencies:
"@0x/sol-tracing-utils" "^7.2.3"
"@0x/subproviders" "^6.5.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/sol-tracing-utils" "^7.2.5"
"@0x/subproviders" "^6.6.0"
"@0x/typescript-typings" "^5.2.1"
"@types/node" "12.12.54"
chalk "^2.3.0"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
lodash "^4.17.11"
loglevel "^1.6.1"
web3-provider-engine "14.0.6"
"@0x/sol-tracing-utils@^7.2.3":
version "7.2.3"
resolved "https://registry.yarnpkg.com/@0x/sol-tracing-utils/-/sol-tracing-utils-7.2.3.tgz#2a24969943315af4f86ceab12ad8bc34433069e5"
"@0x/sol-tracing-utils@^7.2.5":
version "7.2.5"
resolved "https://registry.yarnpkg.com/@0x/sol-tracing-utils/-/sol-tracing-utils-7.2.5.tgz#19ced9ecf6811dab4133ea9acf4dfdd0987b14fd"
integrity sha512-ptffYU/KigOipFGwxWHqToQ/pbkbCyODBcxVTKeEW4MFlHeRMDRHypDM13VFAyAxqQwzvOfk22xeuLVUsKwPVQ==
dependencies:
"@0x/dev-utils" "^4.2.7"
"@0x/sol-compiler" "^4.7.3"
"@0x/sol-resolver" "^3.1.8"
"@0x/subproviders" "^6.5.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/web3-wrapper" "^7.5.3"
"@0x/dev-utils" "^4.2.9"
"@0x/sol-compiler" "^4.7.5"
"@0x/sol-resolver" "^3.1.9"
"@0x/subproviders" "^6.6.0"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@0x/web3-wrapper" "^7.6.0"
"@types/node" "12.12.54"
"@types/solidity-parser-antlr" "^0.2.3"
chalk "^2.3.0"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
ethers "~4.0.4"
glob "^7.1.2"
istanbul "^0.4.5"
@@ -1064,17 +1107,18 @@
solc "^0.5.5"
solidity-parser-antlr "^0.4.2"
"@0x/subproviders@^6.5.3":
version "6.5.3"
resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-6.5.3.tgz#aec86903527c8f972beec1bc2fbda5fdba361235"
"@0x/subproviders@^6.6.0":
version "6.6.0"
resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-6.6.0.tgz#1743d44ae5e2be9ec48caddbf0f1a580f1672d32"
integrity sha512-fQ4efPH/io+TAYEsZuYj9YpoAy0fGWx8QZaWeNstxFT2Miph2aq4Bh+GGrQcXSjNx+prgdBDAGePJvtGP8Qs3Q==
dependencies:
"@0x/assert" "^3.0.27"
"@0x/types" "^3.3.3"
"@0x/typescript-typings" "^5.2.0"
"@0x/utils" "^6.4.3"
"@0x/web3-wrapper" "^7.5.3"
"@ethereumjs/common" "^2.2.0"
"@ethereumjs/tx" "^3.1.3"
"@0x/assert" "^3.0.29"
"@0x/types" "^3.3.4"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@0x/web3-wrapper" "^7.6.0"
"@ethereumjs/common" "^2.4.0"
"@ethereumjs/tx" "^3.3.0"
"@ledgerhq/hw-app-eth" "^4.3.0"
"@ledgerhq/hw-transport-u2f" "4.24.0"
"@types/hdkey" "^0.7.0"
@@ -1082,8 +1126,8 @@
"@types/web3-provider-engine" "^14.0.0"
bip39 "^2.5.0"
bn.js "^4.11.8"
ethereum-types "^3.5.0"
ethereumjs-util "^7.0.10"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
ganache-core "^2.13.2"
hdkey "^0.7.1"
json-rpc-error "2.0.0"
@@ -1284,6 +1328,21 @@
ethers "~4.0.4"
lodash "^4.17.11"
"@0x/web3-wrapper@^7.6.0":
version "7.6.0"
resolved "https://registry.yarnpkg.com/@0x/web3-wrapper/-/web3-wrapper-7.6.0.tgz#34ae5e32affc02ed425c7bb3402a26fd7880c999"
integrity sha512-yxvTT/w5PFfnbKZ9Xvt412fyhVfiNQ0ugFbJYr+X+Xye+Q9vZzzbfc2a3bJSO7w/HkZx7vND071F/jtqU1JsEg==
dependencies:
"@0x/assert" "^3.0.29"
"@0x/json-schemas" "^6.3.0"
"@0x/typescript-typings" "^5.2.1"
"@0x/utils" "^6.4.4"
"@types/node" "12.12.54"
ethereum-types "^3.6.0"
ethereumjs-util "^7.1.0"
ethers "~4.0.4"
lodash "^4.17.11"
"@0xproject/npm-cli-login@^0.0.11":
version "0.0.11"
resolved "https://registry.yarnpkg.com/@0xproject/npm-cli-login/-/npm-cli-login-0.0.11.tgz#3f1ec06112ce62aad300ff0575358f68aeecde2e"
@@ -1346,19 +1405,21 @@
web3 "1.2.1"
web3-typescript-typings "^0.10.2"
"@ethereumjs/common@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.2.0.tgz#850a3e3e594ee707ad8d44a11e8152fb62450535"
"@ethereumjs/common@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.4.0.tgz#2d67f6e6ba22246c5c89104e6b9a119fb3039766"
integrity sha512-UdkhFWzWcJCZVsj1O/H8/oqj/0RVYjLc1OhPjBrQdALAkQHpCp8xXI4WLnuGTADqTdJZww0NtgwG+TRPkXt27w==
dependencies:
crc-32 "^1.2.0"
ethereumjs-util "^7.0.9"
ethereumjs-util "^7.1.0"
"@ethereumjs/tx@^3.1.3":
version "3.1.4"
resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.1.4.tgz#04cf9e9406da5f04a1a26c458744641f4b4b8dd0"
"@ethereumjs/tx@^3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.0.tgz#14ed1b7fa0f28e1cd61e3ecbdab824205f6a4378"
integrity sha512-yTwEj2lVzSMgE6Hjw9Oa1DZks/nKTWM8Wn4ykDNapBPua2f4nXO3qKnni86O6lgDj5fVNRqbDsD0yy7/XNGDEA==
dependencies:
"@ethereumjs/common" "^2.2.0"
ethereumjs-util "^7.0.10"
"@ethereumjs/common" "^2.4.0"
ethereumjs-util "^7.1.0"
"@ethersproject/abi@5.0.0-beta.153":
version "5.0.0-beta.153"
@@ -2419,6 +2480,21 @@
semver "^7.3.4"
tar "^6.1.0"
"@mapbox/node-pre-gyp@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.5.tgz#2a0b32fcb416fb3f2250fd24cb2a81421a4f5950"
integrity sha512-4srsKPXWlIxp5Vbqz5uLfBN+du2fJChBoYn/f2h991WLdk7jUvcSk/McVLSv/X+xQIPI8eGD5GjrnygdyHnhPA==
dependencies:
detect-libc "^1.0.3"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.1"
nopt "^5.0.0"
npmlog "^4.1.2"
rimraf "^3.0.2"
semver "^7.3.4"
tar "^6.1.0"
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -5996,7 +6072,7 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum
rlp "^2.0.0"
safe-buffer "^5.1.1"
ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.9:
ethereumjs-util@^7.0.10:
version "7.0.10"
resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f"
dependencies: