Compare commits
56 Commits
feat/Proto
...
curve-auto
Author | SHA1 | Date | |
---|---|---|---|
|
2d16f83e37 | ||
|
4057bdab91 | ||
|
1cd10f0ac9 | ||
|
68f87b2432 | ||
|
69bafc3bcd | ||
|
2c44b06b7b | ||
|
0233f00b4e | ||
|
fedb53187d | ||
|
6774d2f588 | ||
|
cf740b74f5 | ||
|
177c00463a | ||
|
49b0e32129 | ||
|
938fc94756 | ||
|
1561d91c2b | ||
|
9a28e51f51 | ||
|
f55eaa867b | ||
|
6b2856424a | ||
|
da757c4700 | ||
|
75e6654884 | ||
|
87308e7693 | ||
|
d5eef93a76 | ||
|
a7f23a982e | ||
|
9eadc5fc28 | ||
|
92ad1a612e | ||
|
09413c0e12 | ||
|
23788b41d5 | ||
|
ccf999a495 | ||
|
aa1016ee5f | ||
|
423ef57344 | ||
|
c18149e82f | ||
|
d14aebf724 | ||
|
ba719a9631 | ||
|
d36034d958 | ||
|
7750c57620 | ||
|
4d027e11d1 | ||
|
470e9a4697 | ||
|
7c51412e2f | ||
|
b3d1f3cd10 | ||
|
389bb77439 | ||
|
4327885a00 | ||
|
0aef0afbbb | ||
|
fa4c3a4f5f | ||
|
1d7c527c5c | ||
|
cbe3135e4b | ||
|
955ad49711 | ||
|
8d6f6e76e0 | ||
|
9337115650 | ||
|
fa45a44fe4 | ||
|
c9c7ac8559 | ||
|
c881723578 | ||
|
c9c30d3a76 | ||
|
73dfdb5b69 | ||
|
e638268f94 | ||
|
0bfd765481 | ||
|
1f12893735 | ||
|
dd3d9337c4 |
@@ -110,7 +110,7 @@ jobs:
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
environment:
|
||||
RUST_ROUTER: 'true'
|
||||
RUST_ROUTER: "true"
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
@@ -118,6 +118,7 @@ jobs:
|
||||
- run: yarn wsrun -p @0x/contracts-test-utils -m --serial -c test:circleci
|
||||
- run: yarn wsrun -p @0x/contract-artifacts -m --serial -c test:circleci
|
||||
- run: yarn wsrun -p @0x/contract-wrappers-test -m --serial -c test:circleci
|
||||
- run: yarn wsrun -p @0x/migrations -m --serial -c test:circleci
|
||||
- run: yarn wsrun -p @0x/order-utils -m --serial -c test:circleci
|
||||
- run: yarn wsrun -p @0x/asset-swapper -m --serial -c test:circleci
|
||||
- save_cache:
|
||||
|
1
.github/autolabeler.yml
vendored
1
.github/autolabeler.yml
vendored
@@ -1,6 +1,7 @@
|
||||
python: ['python-packages']
|
||||
contracts: ['contracts']
|
||||
@0x/contract-addresses: ['packages/contract-addresses']
|
||||
@0x/migrations: ['packages/migrations']
|
||||
@0x/order-utils: ['packages/order-utils']
|
||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||
|
@@ -38,6 +38,7 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
||||
| [`@0x/protocol-utils`](/packages/protocol-utils) | [](https://www.npmjs.com/package/@0x/protocol-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
|
||||
## Usage
|
||||
|
@@ -1,22 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1655244958,
|
||||
"version": "3.3.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1654284040,
|
||||
"version": "3.3.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1652919697,
|
||||
"version": "3.3.30",
|
||||
|
@@ -5,14 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.3.32 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.31 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.30 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "3.3.32",
|
||||
"version": "3.3.30",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -53,8 +53,8 @@
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.23",
|
||||
"@0x/contracts-utils": "^4.8.13",
|
||||
"@0x/contracts-test-utils": "^5.4.21",
|
||||
"@0x/contracts-utils": "^4.8.11",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
|
@@ -1,22 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1655244958,
|
||||
"version": "5.4.23",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1654284040,
|
||||
"version": "5.4.22",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1652919697,
|
||||
"version": "5.4.21",
|
||||
|
@@ -5,14 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.23 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.22 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.21 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.23",
|
||||
"version": "5.4.21",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -44,7 +44,7 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.34",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/contract-addresses": "^6.16.0",
|
||||
"@0x/contract-addresses": "^6.14.0",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
|
@@ -1,22 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1655244958,
|
||||
"version": "1.4.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1654284040,
|
||||
"version": "1.4.14",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1652919697,
|
||||
"version": "1.4.13",
|
||||
|
@@ -5,14 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.15 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.14 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.13 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.15",
|
||||
"version": "1.4.13",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -47,12 +47,12 @@
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contract-addresses": "^6.16.0",
|
||||
"@0x/contract-addresses": "^6.14.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.32",
|
||||
"@0x/contracts-erc20": "^3.3.30",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.23",
|
||||
"@0x/contracts-test-utils": "^5.4.21",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
@@ -73,7 +73,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/protocol-utils": "^11.15.0",
|
||||
"@0x/protocol-utils": "^11.13.0",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
|
@@ -1,22 +1,4 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1655244958,
|
||||
"version": "4.8.13",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1654284040,
|
||||
"version": "4.8.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1652919697,
|
||||
"version": "4.8.11",
|
||||
|
@@ -5,14 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.8.13 - _June 14, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.12 - _June 3, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.11 - _May 19, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.8.13",
|
||||
"version": "4.8.11",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -52,7 +52,7 @@
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.23",
|
||||
"@0x/contracts-test-utils": "^5.4.21",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
|
@@ -1,32 +1,4 @@
|
||||
[
|
||||
{
|
||||
"version": "0.35.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Adds support for Velodrome OptimismBridgeAdapter",
|
||||
"pr": 494
|
||||
}
|
||||
],
|
||||
"timestamp": 1655244958
|
||||
},
|
||||
{
|
||||
"version": "0.34.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Splits BridgeAdapter up by chain",
|
||||
"pr": 487
|
||||
},
|
||||
{
|
||||
"note": "Add stETH wrap/unwrap support",
|
||||
"pr": 476
|
||||
},
|
||||
{
|
||||
"note": "Adds support for BancorV3 to EthereumBridgeAdapter",
|
||||
"pr": 492
|
||||
}
|
||||
],
|
||||
"timestamp": 1654284040
|
||||
},
|
||||
{
|
||||
"version": "0.33.0",
|
||||
"changes": [
|
||||
|
@@ -5,16 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.35.0 - _June 14, 2022_
|
||||
|
||||
* Adds support for Velodrome OptimismBridgeAdapter (#494)
|
||||
|
||||
## v0.34.0 - _June 3, 2022_
|
||||
|
||||
* Splits BridgeAdapter up by chain (#487)
|
||||
* Add stETH wrap/unwrap support (#476)
|
||||
* Adds support for BancorV3 to EthereumBridgeAdapter (#492)
|
||||
|
||||
## v0.33.0 - _May 19, 2022_
|
||||
|
||||
* Add support for GMX and Platypus to bridge adapter (#478)
|
||||
|
@@ -1,88 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./IBridgeAdapter.sol";
|
||||
|
||||
abstract contract AbstractBridgeAdapter is IBridgeAdapter {
|
||||
|
||||
constructor(
|
||||
uint256 expectedChainId,
|
||||
string memory expectedChainName
|
||||
)
|
||||
public
|
||||
{
|
||||
uint256 chainId;
|
||||
assembly { chainId := chainid() }
|
||||
// Allow testing on Ganache
|
||||
if (chainId != expectedChainId && chainId != 1337) {
|
||||
revert(string(abi.encodePacked(expectedChainName, "BridgeAdapter.constructor: wrong chain ID")));
|
||||
}
|
||||
}
|
||||
|
||||
function isSupportedSource(bytes32 source)
|
||||
external
|
||||
override
|
||||
returns (bool isSupported)
|
||||
{
|
||||
BridgeOrder memory placeholderOrder;
|
||||
placeholderOrder.source = source;
|
||||
IERC20TokenV06 placeholderToken = IERC20TokenV06(address(0));
|
||||
|
||||
(, isSupported) = _trade(
|
||||
placeholderOrder,
|
||||
placeholderToken,
|
||||
placeholderToken,
|
||||
0,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
function trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
(boughtAmount, ) = _trade(
|
||||
order,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
virtual
|
||||
returns (uint256 boughtAmount, bool supportedSource);
|
||||
}
|
@@ -1,141 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinGMX.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinPlatypus.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract AvalancheBridgeAdapter is
|
||||
AbstractBridgeAdapter(43114, "Avalanche"),
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinGMX,
|
||||
MixinKyberDmm,
|
||||
MixinAaveV2,
|
||||
MixinNerve,
|
||||
MixinPlatypus,
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinCurve(weth)
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeKyberDmm(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeAaveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.GMX) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeGMX(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.PLATYPUS) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradePlatypus(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,132 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinMooniswap.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract BSCBridgeAdapter is
|
||||
AbstractBridgeAdapter(56, "BSC"),
|
||||
MixinCurve,
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinKyberDmm,
|
||||
MixinMooniswap,
|
||||
MixinNerve,
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinCurve(weth)
|
||||
MixinMooniswap(weth)
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.MOONISWAP) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeMooniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODO) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodo(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodoV2(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeKyberDmm(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
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.
|
||||
@@ -20,52 +20,54 @@
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./IBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBalancerV2.sol";
|
||||
import "./mixins/MixinBalancerV2Batch.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinBancorV3.sol";
|
||||
import "./mixins/MixinCompound.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinCryptoCom.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinGMX.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinLido.sol";
|
||||
import "./mixins/MixinMakerPSM.sol";
|
||||
import "./mixins/MixinMooniswap.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinPlatypus.sol";
|
||||
import "./mixins/MixinShell.sol";
|
||||
import "./mixins/MixinUniswap.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinUniswapV3.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract EthereumBridgeAdapter is
|
||||
AbstractBridgeAdapter(1, "Ethereum"),
|
||||
contract BridgeAdapter is
|
||||
IBridgeAdapter,
|
||||
MixinAaveV2,
|
||||
MixinBalancer,
|
||||
MixinBalancerV2,
|
||||
MixinBalancerV2Batch,
|
||||
MixinBancor,
|
||||
MixinBancorV3,
|
||||
MixinCompound,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinCryptoCom,
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinGMX,
|
||||
MixinKyberDmm,
|
||||
MixinLido,
|
||||
MixinMakerPSM,
|
||||
MixinMooniswap,
|
||||
MixinMStable,
|
||||
MixinNerve,
|
||||
MixinPlatypus,
|
||||
MixinShell,
|
||||
MixinUniswap,
|
||||
MixinUniswapV2,
|
||||
@@ -74,29 +76,42 @@ contract EthereumBridgeAdapter is
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinAaveV2()
|
||||
MixinBalancer()
|
||||
MixinBalancerV2()
|
||||
MixinBancor(weth)
|
||||
MixinBancorV3(weth)
|
||||
MixinCompound(weth)
|
||||
MixinCurve(weth)
|
||||
MixinCurveV2()
|
||||
MixinCryptoCom()
|
||||
MixinDodo()
|
||||
MixinDodoV2()
|
||||
MixinGMX()
|
||||
MixinLido(weth)
|
||||
MixinMakerPSM()
|
||||
MixinMooniswap(weth)
|
||||
MixinMStable()
|
||||
MixinNerve()
|
||||
MixinPlatypus()
|
||||
MixinShell()
|
||||
MixinUniswap(weth)
|
||||
MixinUniswapV2()
|
||||
MixinUniswapV3()
|
||||
MixinZeroExBridge()
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
function trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -104,7 +119,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -112,21 +126,18 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV3(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAP) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -134,7 +145,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCER) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancer(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -142,7 +152,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancerV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -150,13 +159,11 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancerV2Batch(
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.MAKERPSM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
}else if (protocolId == BridgeProtocols.MAKERPSM) {
|
||||
boughtAmount = _tradeMakerPsm(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -164,7 +171,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.MOONISWAP) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeMooniswap(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -172,7 +178,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeMStable(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -180,7 +185,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.SHELL) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeShell(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -188,49 +192,42 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODO) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodo(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodoV2(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CRYPTOCOM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCryptoCom(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BANCOR) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBancor(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeKyberDmm(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.LIDO) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeLido(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -238,7 +235,6 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeAaveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
@@ -246,22 +242,25 @@ contract EthereumBridgeAdapter is
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.COMPOUND) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCompound(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BANCORV3) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBancorV3(
|
||||
} else if (protocolId == BridgeProtocols.GMX) {
|
||||
boughtAmount = _tradeGMX(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
} else if (protocolId == BridgeProtocols.PLATYPUS) {
|
||||
boughtAmount = _tradePlatypus(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else {
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
@@ -55,6 +55,4 @@ library BridgeProtocols {
|
||||
uint128 internal constant BALANCERV2BATCH = 25;
|
||||
uint128 internal constant GMX = 26;
|
||||
uint128 internal constant PLATYPUS = 27;
|
||||
uint128 internal constant BANCORV3 = 28;
|
||||
uint128 internal constant VELODROME = 29;
|
||||
}
|
||||
|
@@ -1,84 +0,0 @@
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract CeloBridgeAdapter is
|
||||
AbstractBridgeAdapter(42220, "Celo"),
|
||||
MixinNerve,
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(address _weth)
|
||||
public
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,124 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinBalancerV2.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract FantomBridgeAdapter is
|
||||
AbstractBridgeAdapter(250, "Fantom"),
|
||||
MixinAaveV2,
|
||||
MixinBalancerV2,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinNerve,
|
||||
MixinUniswapV2,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinCurve(weth)
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancerV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeAaveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -50,10 +50,6 @@ interface IBridgeAdapter {
|
||||
uint256 outputTokenAmount
|
||||
);
|
||||
|
||||
function isSupportedSource(bytes32 source)
|
||||
external
|
||||
returns (bool isSupported);
|
||||
|
||||
function trade(
|
||||
BridgeOrder calldata order,
|
||||
IERC20TokenV06 sellToken,
|
||||
|
@@ -1,114 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV3.sol";
|
||||
import "./mixins/MixinVelodrome.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract OptimismBridgeAdapter is
|
||||
AbstractBridgeAdapter(10, "Optimism"),
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinNerve,
|
||||
MixinUniswapV3,
|
||||
MixinVelodrome,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinCurve(weth)
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV3(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.VELODROME) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeVelodrome(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,178 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./AbstractBridgeAdapter.sol";
|
||||
import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinBalancerV2.sol";
|
||||
import "./mixins/MixinBalancerV2Batch.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV2.sol";
|
||||
import "./mixins/MixinUniswapV3.sol";
|
||||
import "./mixins/MixinZeroExBridge.sol";
|
||||
|
||||
contract PolygonBridgeAdapter is
|
||||
AbstractBridgeAdapter(137, "Polygon"),
|
||||
MixinAaveV2,
|
||||
MixinBalancerV2,
|
||||
MixinBalancerV2Batch,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinKyberDmm,
|
||||
MixinMStable,
|
||||
MixinNerve,
|
||||
MixinUniswapV2,
|
||||
MixinUniswapV3,
|
||||
MixinZeroExBridge
|
||||
{
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
MixinCurve(weth)
|
||||
{}
|
||||
|
||||
function _trade(
|
||||
BridgeOrder memory order,
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bool dryRun
|
||||
)
|
||||
internal
|
||||
override
|
||||
returns (uint256 boughtAmount, bool supportedSource)
|
||||
{
|
||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||
if (protocolId == BridgeProtocols.CURVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurve(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV3(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeUniswapV2(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancerV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeBalancerV2Batch(
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeMStable(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODO) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodo(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeDodoV2(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeNerve(
|
||||
sellToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeKyberDmm(
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeAaveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||
if (dryRun) { return (0, true); }
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
}
|
||||
|
||||
emit BridgeFill(
|
||||
order.source,
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
boughtAmount
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,128 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
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";
|
||||
|
||||
|
||||
/*
|
||||
BancorV3
|
||||
*/
|
||||
interface IBancorV3 {
|
||||
/**
|
||||
* @dev performs a trade by providing the source amount and returns the target amount and the associated fee
|
||||
*
|
||||
* requirements:
|
||||
*
|
||||
* - the caller must be the network contract
|
||||
*/
|
||||
function tradeBySourceAmount(
|
||||
address sourceToken,
|
||||
address targetToken,
|
||||
uint256 sourceAmount,
|
||||
uint256 minReturnAmount,
|
||||
uint256 deadline,
|
||||
address beneficiary
|
||||
) external payable returns (uint256 amount);
|
||||
}
|
||||
|
||||
contract MixinBancorV3 {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
IERC20TokenV06 constant public BANCORV3_ETH_ADDRESS =
|
||||
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeBancorV3(
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 amountOut)
|
||||
|
||||
{
|
||||
IBancorV3 router;
|
||||
IERC20TokenV06[] memory path;
|
||||
address[] memory _path;
|
||||
uint256 payableAmount = 0;
|
||||
|
||||
{
|
||||
(router, _path) = abi.decode(bridgeData, (IBancorV3, address[]));
|
||||
// To get around `abi.decode()` not supporting interface array types.
|
||||
assembly { path := _path }
|
||||
}
|
||||
|
||||
require(path.length >= 2, "MixinBancorV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||
require(
|
||||
path[path.length - 1] == buyToken,
|
||||
"MixinBancorV3/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||
);
|
||||
|
||||
//swap WETH->ETH as Bancor only deals in ETH
|
||||
if(_path[0] == address(WETH)) {
|
||||
//withdraw the sell amount of WETH for ETH
|
||||
WETH.withdraw(sellAmount);
|
||||
payableAmount = sellAmount;
|
||||
// set _path[0] to the ETH address if WETH is our buy token
|
||||
_path[0] = address(BANCORV3_ETH_ADDRESS);
|
||||
} else {
|
||||
// Grant the BancorV3 router an allowance to sell the first token.
|
||||
path[0].approveIfBelow(address(router), sellAmount);
|
||||
}
|
||||
|
||||
// if we are buying WETH we need to swap to ETH and deposit into WETH after the swap
|
||||
if(_path[1] == address(WETH)){
|
||||
_path[1] = address(BANCORV3_ETH_ADDRESS);
|
||||
}
|
||||
|
||||
|
||||
uint256 amountOut = router.tradeBySourceAmount{value: payableAmount}(
|
||||
_path[0],
|
||||
_path[1],
|
||||
// Sell all tokens we hold.
|
||||
sellAmount,
|
||||
// Minimum buy amount.
|
||||
1,
|
||||
//deadline
|
||||
block.timestamp + 1,
|
||||
// address of the mixin
|
||||
address(this)
|
||||
);
|
||||
|
||||
// if we want to return WETH deposit the ETH amount we sold
|
||||
if(buyToken == WETH){
|
||||
WETH.deposit{value: amountOut}();
|
||||
}
|
||||
|
||||
return amountOut;
|
||||
}
|
||||
}
|
@@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
|
||||
|
||||
/// @dev Minimal interface for minting StETH
|
||||
interface IStETH {
|
||||
interface ILido {
|
||||
/// @dev Adds eth to the pool
|
||||
/// @param _referral optional address for referrals
|
||||
/// @return StETH Amount of shares generated
|
||||
@@ -37,33 +37,6 @@ interface IStETH {
|
||||
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
|
||||
}
|
||||
|
||||
/// @dev Minimal interface for wrapping/unwrapping stETH.
|
||||
interface IWstETH {
|
||||
|
||||
/**
|
||||
* @notice Exchanges stETH to wstETH
|
||||
* @param _stETHAmount amount of stETH to wrap in exchange for wstETH
|
||||
* @dev Requirements:
|
||||
* - `_stETHAmount` must be non-zero
|
||||
* - msg.sender must approve at least `_stETHAmount` stETH to this
|
||||
* contract.
|
||||
* - msg.sender must have at least `_stETHAmount` of stETH.
|
||||
* User should first approve _stETHAmount to the WstETH contract
|
||||
* @return Amount of wstETH user receives after wrap
|
||||
*/
|
||||
function wrap(uint256 _stETHAmount) external returns (uint256);
|
||||
|
||||
/**
|
||||
* @notice Exchanges wstETH to stETH
|
||||
* @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
|
||||
* @dev Requirements:
|
||||
* - `_wstETHAmount` must be non-zero
|
||||
* - msg.sender must have at least `_wstETHAmount` wstETH.
|
||||
* @return Amount of stETH user receives after unwrap
|
||||
*/
|
||||
function unwrap(uint256 _wstETHAmount) external returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract MixinLido {
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
@@ -86,43 +59,12 @@ contract MixinLido {
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
if (address(sellToken) == address(WETH)) {
|
||||
return _tradeStETH(buyToken, sellAmount, bridgeData);
|
||||
}
|
||||
|
||||
return _tradeWstETH(sellToken, buyToken, sellAmount, bridgeData);
|
||||
}
|
||||
|
||||
function _tradeStETH(
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
) private returns (uint256 boughtAmount) {
|
||||
(IStETH stETH) = abi.decode(bridgeData, (IStETH));
|
||||
if (address(buyToken) == address(stETH)) {
|
||||
(ILido lido) = abi.decode(bridgeData, (ILido));
|
||||
if (address(sellToken) == address(WETH) && address(buyToken) == address(lido)) {
|
||||
WETH.withdraw(sellAmount);
|
||||
return stETH.getPooledEthByShares(stETH.submit{ value: sellAmount}(address(0)));
|
||||
boughtAmount = lido.getPooledEthByShares(lido.submit{ value: sellAmount}(address(0)));
|
||||
} else {
|
||||
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||
}
|
||||
|
||||
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||
}
|
||||
|
||||
function _tradeWstETH(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
|
||||
) private returns(uint256 boughtAmount){
|
||||
(IEtherTokenV06 stETH, IWstETH wstETH) = abi.decode(bridgeData, (IEtherTokenV06, IWstETH));
|
||||
if (address(sellToken) == address(stETH) && address(buyToken) == address(wstETH) ) {
|
||||
sellToken.approveIfBelow(address(wstETH), sellAmount);
|
||||
return wstETH.wrap(sellAmount);
|
||||
}
|
||||
if (address(sellToken) == address(wstETH) && address(buyToken) == address(stETH) ) {
|
||||
return wstETH.unwrap(sellAmount);
|
||||
}
|
||||
|
||||
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||
}
|
||||
}
|
||||
|
@@ -1,22 +1,3 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 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;
|
||||
|
||||
@@ -77,7 +58,7 @@ contract MixinPlatypus {
|
||||
//keep track of the previous balance to confirm amount out
|
||||
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||
|
||||
router.swapTokensForTokens(
|
||||
(uint256 amountOut, uint256 haircut) = router.swapTokensForTokens(
|
||||
// Convert to `buyToken` along this path.
|
||||
_path,
|
||||
// pool to swap on
|
||||
|
@@ -1,64 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 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";
|
||||
|
||||
interface IVelodromeRouter {
|
||||
function swapExactTokensForTokensSimple(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address tokenFrom,
|
||||
address tokenTo,
|
||||
bool stable,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
}
|
||||
|
||||
contract MixinVelodrome {
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
function _tradeVelodrome(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
|
||||
(IVelodromeRouter router, bool stable) = abi.decode(bridgeData, (IVelodromeRouter, bool));
|
||||
sellToken.approveIfBelow(address(router), sellAmount);
|
||||
|
||||
boughtAmount = router.swapExactTokensForTokensSimple(
|
||||
sellAmount,
|
||||
0,
|
||||
address(sellToken),
|
||||
address(buyToken),
|
||||
stable,
|
||||
address(this),
|
||||
block.timestamp + 1
|
||||
)[1];
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.35.0",
|
||||
"version": "0.33.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -41,9 +41,9 @@
|
||||
"rollback": "node ./lib/scripts/rollback.js"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,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/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinVelodrome|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -56,10 +56,10 @@
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contract-addresses": "^6.16.0",
|
||||
"@0x/contracts-erc20": "^3.3.32",
|
||||
"@0x/contract-addresses": "^6.14.0",
|
||||
"@0x/contracts-erc20": "^3.3.30",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.23",
|
||||
"@0x/contracts-test-utils": "^5.4.21",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
@@ -83,7 +83,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/protocol-utils": "^11.15.0",
|
||||
"@0x/protocol-utils": "^11.13.0",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
|
@@ -6,13 +6,9 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as AffiliateFeeTransformer from '../generated-artifacts/AffiliateFeeTransformer.json';
|
||||
import * as AvalancheBridgeAdapter from '../generated-artifacts/AvalancheBridgeAdapter.json';
|
||||
import * as BatchFillNativeOrdersFeature from '../generated-artifacts/BatchFillNativeOrdersFeature.json';
|
||||
import * as BSCBridgeAdapter from '../generated-artifacts/BSCBridgeAdapter.json';
|
||||
import * as CeloBridgeAdapter from '../generated-artifacts/CeloBridgeAdapter.json';
|
||||
import * as BridgeAdapter from '../generated-artifacts/BridgeAdapter.json';
|
||||
import * as CurveLiquidityProvider from '../generated-artifacts/CurveLiquidityProvider.json';
|
||||
import * as EthereumBridgeAdapter from '../generated-artifacts/EthereumBridgeAdapter.json';
|
||||
import * as FantomBridgeAdapter from '../generated-artifacts/FantomBridgeAdapter.json';
|
||||
import * as FeeCollector from '../generated-artifacts/FeeCollector.json';
|
||||
import * as FeeCollectorController from '../generated-artifacts/FeeCollectorController.json';
|
||||
import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json';
|
||||
@@ -34,11 +30,9 @@ import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTrans
|
||||
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
|
||||
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.json';
|
||||
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
|
||||
import * as OptimismBridgeAdapter from '../generated-artifacts/OptimismBridgeAdapter.json';
|
||||
import * as OtcOrdersFeature from '../generated-artifacts/OtcOrdersFeature.json';
|
||||
import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
|
||||
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
||||
import * as PolygonBridgeAdapter from '../generated-artifacts/PolygonBridgeAdapter.json';
|
||||
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
|
||||
@@ -64,6 +58,7 @@ export const artifacts = {
|
||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
|
||||
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
|
||||
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
||||
@@ -77,11 +72,4 @@ export const artifacts = {
|
||||
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
|
||||
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
|
||||
IOtcOrdersFeature: IOtcOrdersFeature as ContractArtifact,
|
||||
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
|
||||
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
|
||||
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
|
||||
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
|
||||
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
|
||||
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
|
||||
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
|
||||
};
|
||||
|
@@ -35,11 +35,7 @@ export * from './bloom_filter_utils';
|
||||
export { GREEDY_TOKENS } from './constants';
|
||||
export {
|
||||
AffiliateFeeTransformerContract,
|
||||
AvalancheBridgeAdapterContract,
|
||||
BSCBridgeAdapterContract,
|
||||
CeloBridgeAdapterContract,
|
||||
EthereumBridgeAdapterContract,
|
||||
FantomBridgeAdapterContract,
|
||||
BridgeAdapterContract,
|
||||
FillQuoteTransformerContract,
|
||||
IOwnableFeatureContract,
|
||||
IOwnableFeatureEvents,
|
||||
@@ -49,9 +45,7 @@ export {
|
||||
IZeroExContract,
|
||||
LogMetadataTransformerContract,
|
||||
MultiplexFeatureContract,
|
||||
OptimismBridgeAdapterContract,
|
||||
PayTakerTransformerContract,
|
||||
PolygonBridgeAdapterContract,
|
||||
PositiveSlippageFeeTransformerContract,
|
||||
TransformERC20FeatureContract,
|
||||
WethTransformerContract,
|
||||
|
@@ -4,13 +4,9 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/affiliate_fee_transformer';
|
||||
export * from '../generated-wrappers/avalanche_bridge_adapter';
|
||||
export * from '../generated-wrappers/b_s_c_bridge_adapter';
|
||||
export * from '../generated-wrappers/batch_fill_native_orders_feature';
|
||||
export * from '../generated-wrappers/celo_bridge_adapter';
|
||||
export * from '../generated-wrappers/bridge_adapter';
|
||||
export * from '../generated-wrappers/curve_liquidity_provider';
|
||||
export * from '../generated-wrappers/ethereum_bridge_adapter';
|
||||
export * from '../generated-wrappers/fantom_bridge_adapter';
|
||||
export * from '../generated-wrappers/fee_collector';
|
||||
export * from '../generated-wrappers/fee_collector_controller';
|
||||
export * from '../generated-wrappers/fill_quote_transformer';
|
||||
@@ -32,11 +28,9 @@ export * from '../generated-wrappers/log_metadata_transformer';
|
||||
export * from '../generated-wrappers/meta_transactions_feature';
|
||||
export * from '../generated-wrappers/multiplex_feature';
|
||||
export * from '../generated-wrappers/native_orders_feature';
|
||||
export * from '../generated-wrappers/optimism_bridge_adapter';
|
||||
export * from '../generated-wrappers/otc_orders_feature';
|
||||
export * from '../generated-wrappers/ownable_feature';
|
||||
export * from '../generated-wrappers/pay_taker_transformer';
|
||||
export * from '../generated-wrappers/polygon_bridge_adapter';
|
||||
export * from '../generated-wrappers/positive_slippage_fee_transformer';
|
||||
export * from '../generated-wrappers/simple_function_registry_feature';
|
||||
export * from '../generated-wrappers/transform_erc20_feature';
|
||||
|
@@ -5,20 +5,15 @@
|
||||
*/
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as AbstractBridgeAdapter from '../test/generated-artifacts/AbstractBridgeAdapter.json';
|
||||
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
||||
import * as AvalancheBridgeAdapter from '../test/generated-artifacts/AvalancheBridgeAdapter.json';
|
||||
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json';
|
||||
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
||||
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
||||
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
|
||||
import * as BSCBridgeAdapter from '../test/generated-artifacts/BSCBridgeAdapter.json';
|
||||
import * as CeloBridgeAdapter from '../test/generated-artifacts/CeloBridgeAdapter.json';
|
||||
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
|
||||
import * as ERC1155OrdersFeature from '../test/generated-artifacts/ERC1155OrdersFeature.json';
|
||||
import * as ERC165Feature from '../test/generated-artifacts/ERC165Feature.json';
|
||||
import * as ERC721OrdersFeature from '../test/generated-artifacts/ERC721OrdersFeature.json';
|
||||
import * as EthereumBridgeAdapter from '../test/generated-artifacts/EthereumBridgeAdapter.json';
|
||||
import * as FantomBridgeAdapter from '../test/generated-artifacts/FantomBridgeAdapter.json';
|
||||
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
|
||||
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
|
||||
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
||||
@@ -108,7 +103,6 @@ import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
|
||||
import * as MixinBalancerV2Batch from '../test/generated-artifacts/MixinBalancerV2Batch.json';
|
||||
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
|
||||
import * as MixinBancorV3 from '../test/generated-artifacts/MixinBancorV3.json';
|
||||
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
|
||||
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
|
||||
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
||||
@@ -127,7 +121,6 @@ import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
|
||||
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
||||
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
||||
import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json';
|
||||
import * as MixinVelodrome from '../test/generated-artifacts/MixinVelodrome.json';
|
||||
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
||||
import * as MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json';
|
||||
import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json';
|
||||
@@ -143,13 +136,11 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.
|
||||
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
|
||||
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json';
|
||||
import * as NFTOrders from '../test/generated-artifacts/NFTOrders.json';
|
||||
import * as OptimismBridgeAdapter from '../test/generated-artifacts/OptimismBridgeAdapter.json';
|
||||
import * as OtcOrdersFeature from '../test/generated-artifacts/OtcOrdersFeature.json';
|
||||
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
|
||||
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
|
||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
|
||||
import * as PolygonBridgeAdapter from '../test/generated-artifacts/PolygonBridgeAdapter.json';
|
||||
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
|
||||
@@ -316,22 +307,14 @@ export const artifacts = {
|
||||
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
|
||||
Transformer: Transformer as ContractArtifact,
|
||||
WethTransformer: WethTransformer as ContractArtifact,
|
||||
AbstractBridgeAdapter: AbstractBridgeAdapter as ContractArtifact,
|
||||
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
|
||||
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
|
||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
||||
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
||||
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
|
||||
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
|
||||
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
|
||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
|
||||
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
|
||||
MixinAaveV2: MixinAaveV2 as ContractArtifact,
|
||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
||||
MixinBalancerV2Batch: MixinBalancerV2Batch as ContractArtifact,
|
||||
MixinBancor: MixinBancor as ContractArtifact,
|
||||
MixinBancorV3: MixinBancorV3 as ContractArtifact,
|
||||
MixinCompound: MixinCompound as ContractArtifact,
|
||||
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
||||
MixinCurve: MixinCurve as ContractArtifact,
|
||||
@@ -350,7 +333,6 @@ export const artifacts = {
|
||||
MixinUniswap: MixinUniswap as ContractArtifact,
|
||||
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
||||
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
|
||||
MixinVelodrome: MixinVelodrome as ContractArtifact,
|
||||
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
||||
IERC1155Token: IERC1155Token as ContractArtifact,
|
||||
IERC721Token: IERC721Token as ContractArtifact,
|
||||
|
@@ -28,7 +28,7 @@ import { artifacts } from '../artifacts';
|
||||
import { TestFillQuoteTransformerBridgeContract } from '../generated-wrappers/test_fill_quote_transformer_bridge';
|
||||
import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
|
||||
import {
|
||||
EthereumBridgeAdapterContract,
|
||||
BridgeAdapterContract,
|
||||
FillQuoteTransformerContract,
|
||||
TestFillQuoteTransformerExchangeContract,
|
||||
TestFillQuoteTransformerHostContract,
|
||||
@@ -52,8 +52,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
let singleProtocolFee: BigNumber;
|
||||
|
||||
const GAS_PRICE = 1337;
|
||||
// Left half is 0, corresponding to BridgeProtocol.Unknown
|
||||
const TEST_BRIDGE_SOURCE = hexUtils.leftPad(hexUtils.random(16), 32);
|
||||
const TEST_BRIDGE_SOURCE = hexUtils.random(32);
|
||||
const HIGH_BIT = new BigNumber(2).pow(255);
|
||||
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');
|
||||
|
||||
@@ -65,8 +64,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
);
|
||||
const bridgeAdapter = await EthereumBridgeAdapterContract.deployFrom0xArtifactAsync(
|
||||
artifacts.EthereumBridgeAdapter,
|
||||
const bridgeAdapter = await BridgeAdapterContract.deployFrom0xArtifactAsync(
|
||||
artifacts.BridgeAdapter,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
|
@@ -3,20 +3,15 @@
|
||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/abstract_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
||||
export * from '../test/generated-wrappers/avalanche_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/b_s_c_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/batch_fill_native_orders_feature';
|
||||
export * from '../test/generated-wrappers/bootstrap_feature';
|
||||
export * from '../test/generated-wrappers/bridge_adapter';
|
||||
export * from '../test/generated-wrappers/bridge_protocols';
|
||||
export * from '../test/generated-wrappers/celo_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/curve_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/erc1155_orders_feature';
|
||||
export * from '../test/generated-wrappers/erc165_feature';
|
||||
export * from '../test/generated-wrappers/erc721_orders_feature';
|
||||
export * from '../test/generated-wrappers/ethereum_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/fantom_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/fee_collector';
|
||||
export * from '../test/generated-wrappers/fee_collector_controller';
|
||||
export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||
@@ -106,7 +101,6 @@ export * from '../test/generated-wrappers/mixin_balancer';
|
||||
export * from '../test/generated-wrappers/mixin_balancer_v2';
|
||||
export * from '../test/generated-wrappers/mixin_balancer_v2_batch';
|
||||
export * from '../test/generated-wrappers/mixin_bancor';
|
||||
export * from '../test/generated-wrappers/mixin_bancor_v3';
|
||||
export * from '../test/generated-wrappers/mixin_compound';
|
||||
export * from '../test/generated-wrappers/mixin_crypto_com';
|
||||
export * from '../test/generated-wrappers/mixin_curve';
|
||||
@@ -125,7 +119,6 @@ export * from '../test/generated-wrappers/mixin_shell';
|
||||
export * from '../test/generated-wrappers/mixin_uniswap';
|
||||
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
||||
export * from '../test/generated-wrappers/mixin_uniswap_v3';
|
||||
export * from '../test/generated-wrappers/mixin_velodrome';
|
||||
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
||||
export * from '../test/generated-wrappers/mooniswap_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/multiplex_feature';
|
||||
@@ -141,13 +134,11 @@ export * from '../test/generated-wrappers/native_orders_feature';
|
||||
export * from '../test/generated-wrappers/native_orders_info';
|
||||
export * from '../test/generated-wrappers/native_orders_protocol_fees';
|
||||
export * from '../test/generated-wrappers/native_orders_settlement';
|
||||
export * from '../test/generated-wrappers/optimism_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/otc_orders_feature';
|
||||
export * from '../test/generated-wrappers/ownable_feature';
|
||||
export * from '../test/generated-wrappers/pancake_swap_feature';
|
||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
|
||||
export * from '../test/generated-wrappers/polygon_bridge_adapter';
|
||||
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
|
||||
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
||||
export * from '../test/generated-wrappers/test_bridge';
|
||||
|
@@ -4,13 +4,9 @@
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*", "./scripts/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/AffiliateFeeTransformer.json",
|
||||
"generated-artifacts/AvalancheBridgeAdapter.json",
|
||||
"generated-artifacts/BSCBridgeAdapter.json",
|
||||
"generated-artifacts/BatchFillNativeOrdersFeature.json",
|
||||
"generated-artifacts/CeloBridgeAdapter.json",
|
||||
"generated-artifacts/BridgeAdapter.json",
|
||||
"generated-artifacts/CurveLiquidityProvider.json",
|
||||
"generated-artifacts/EthereumBridgeAdapter.json",
|
||||
"generated-artifacts/FantomBridgeAdapter.json",
|
||||
"generated-artifacts/FeeCollector.json",
|
||||
"generated-artifacts/FeeCollectorController.json",
|
||||
"generated-artifacts/FillQuoteTransformer.json",
|
||||
@@ -32,30 +28,23 @@
|
||||
"generated-artifacts/MetaTransactionsFeature.json",
|
||||
"generated-artifacts/MultiplexFeature.json",
|
||||
"generated-artifacts/NativeOrdersFeature.json",
|
||||
"generated-artifacts/OptimismBridgeAdapter.json",
|
||||
"generated-artifacts/OtcOrdersFeature.json",
|
||||
"generated-artifacts/OwnableFeature.json",
|
||||
"generated-artifacts/PayTakerTransformer.json",
|
||||
"generated-artifacts/PolygonBridgeAdapter.json",
|
||||
"generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||
"generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||
"generated-artifacts/TransformERC20Feature.json",
|
||||
"generated-artifacts/WethTransformer.json",
|
||||
"generated-artifacts/ZeroEx.json",
|
||||
"test/generated-artifacts/AbstractBridgeAdapter.json",
|
||||
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
||||
"test/generated-artifacts/AvalancheBridgeAdapter.json",
|
||||
"test/generated-artifacts/BSCBridgeAdapter.json",
|
||||
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
|
||||
"test/generated-artifacts/BootstrapFeature.json",
|
||||
"test/generated-artifacts/BridgeAdapter.json",
|
||||
"test/generated-artifacts/BridgeProtocols.json",
|
||||
"test/generated-artifacts/CeloBridgeAdapter.json",
|
||||
"test/generated-artifacts/CurveLiquidityProvider.json",
|
||||
"test/generated-artifacts/ERC1155OrdersFeature.json",
|
||||
"test/generated-artifacts/ERC165Feature.json",
|
||||
"test/generated-artifacts/ERC721OrdersFeature.json",
|
||||
"test/generated-artifacts/EthereumBridgeAdapter.json",
|
||||
"test/generated-artifacts/FantomBridgeAdapter.json",
|
||||
"test/generated-artifacts/FeeCollector.json",
|
||||
"test/generated-artifacts/FeeCollectorController.json",
|
||||
"test/generated-artifacts/FillQuoteTransformer.json",
|
||||
@@ -145,7 +134,6 @@
|
||||
"test/generated-artifacts/MixinBalancerV2.json",
|
||||
"test/generated-artifacts/MixinBalancerV2Batch.json",
|
||||
"test/generated-artifacts/MixinBancor.json",
|
||||
"test/generated-artifacts/MixinBancorV3.json",
|
||||
"test/generated-artifacts/MixinCompound.json",
|
||||
"test/generated-artifacts/MixinCryptoCom.json",
|
||||
"test/generated-artifacts/MixinCurve.json",
|
||||
@@ -164,7 +152,6 @@
|
||||
"test/generated-artifacts/MixinUniswap.json",
|
||||
"test/generated-artifacts/MixinUniswapV2.json",
|
||||
"test/generated-artifacts/MixinUniswapV3.json",
|
||||
"test/generated-artifacts/MixinVelodrome.json",
|
||||
"test/generated-artifacts/MixinZeroExBridge.json",
|
||||
"test/generated-artifacts/MooniswapLiquidityProvider.json",
|
||||
"test/generated-artifacts/MultiplexFeature.json",
|
||||
@@ -180,13 +167,11 @@
|
||||
"test/generated-artifacts/NativeOrdersInfo.json",
|
||||
"test/generated-artifacts/NativeOrdersProtocolFees.json",
|
||||
"test/generated-artifacts/NativeOrdersSettlement.json",
|
||||
"test/generated-artifacts/OptimismBridgeAdapter.json",
|
||||
"test/generated-artifacts/OtcOrdersFeature.json",
|
||||
"test/generated-artifacts/OwnableFeature.json",
|
||||
"test/generated-artifacts/PancakeSwapFeature.json",
|
||||
"test/generated-artifacts/PayTakerTransformer.json",
|
||||
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
|
||||
"test/generated-artifacts/PolygonBridgeAdapter.json",
|
||||
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||
"test/generated-artifacts/TestBridge.json",
|
||||
|
@@ -52,10 +52,10 @@
|
||||
},
|
||||
"config": {
|
||||
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
|
||||
"nonContractPackages": "@0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
|
||||
"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",
|
||||
"packagesWithDocPages": "@0x/contract-wrappers",
|
||||
"packagesWithDocPages": "@0x/contract-wrappers @0x/migrations",
|
||||
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
||||
"ignoreDependencyVersionsForPackage": "contract-wrappers"
|
||||
},
|
||||
|
@@ -1,61 +1,12 @@
|
||||
[
|
||||
{
|
||||
"version": "16.62.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Remove nUSD from intermediate liquidity to save on sampler gas",
|
||||
"pr": 505
|
||||
}
|
||||
],
|
||||
"timestamp": 1655253622
|
||||
},
|
||||
{
|
||||
"version": "16.62.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add MDEX on BSC",
|
||||
"pr": 496
|
||||
},
|
||||
{
|
||||
"note": "Add KnightSwap on BSC",
|
||||
"pr": 498
|
||||
},
|
||||
{
|
||||
"note": "Add Velodrome support on Optimism",
|
||||
"pr": 494
|
||||
},
|
||||
{
|
||||
"note": "Do not send empty entries on Quote Report",
|
||||
"pr": 501
|
||||
},
|
||||
{
|
||||
"note": "KnightSwap/Mdex cosmetic change",
|
||||
"pr": 502
|
||||
},
|
||||
{
|
||||
"note": "Offboard JetSwap, CafeSwap, JulSwap, and PolyDex",
|
||||
"pr": 503
|
||||
}
|
||||
],
|
||||
"timestamp": 1655244958
|
||||
},
|
||||
{
|
||||
"version": "16.61.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add stETH wrap/unwrap support",
|
||||
"pr": 476
|
||||
},
|
||||
{
|
||||
"note": "Offboard/clean up Oasis, CoFix, and legacy Kyber",
|
||||
"pr": 482
|
||||
},
|
||||
{
|
||||
"note": "Add MeshSwap on Polygon",
|
||||
"pr": 491
|
||||
}
|
||||
],
|
||||
"timestamp": 1654284040
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "16.60.1",
|
||||
|
@@ -5,25 +5,6 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v16.62.1 - _June 15, 2022_
|
||||
|
||||
* Remove nUSD from intermediate liquidity to save on sampler gas (#505)
|
||||
|
||||
## v16.62.0 - _June 14, 2022_
|
||||
|
||||
* Add MDEX on BSC (#496)
|
||||
* Add KnightSwap on BSC (#498)
|
||||
* Add Velodrome support on Optimism (#494)
|
||||
* Do not send empty entries on Quote Report (#501)
|
||||
* KnightSwap/Mdex cosmetic change (#502)
|
||||
* Offboard JetSwap, CafeSwap, JulSwap, and PolyDex (#503)
|
||||
|
||||
## v16.61.0 - _June 3, 2022_
|
||||
|
||||
* Add stETH wrap/unwrap support (#476)
|
||||
* Offboard/clean up Oasis, CoFix, and legacy Kyber (#482)
|
||||
* Add MeshSwap on Polygon (#491)
|
||||
|
||||
## v16.60.1 - _May 19, 2022_
|
||||
|
||||
* Alias Balancer sor to the old version (#481)
|
||||
|
148
packages/asset-swapper/contracts/src/ApproximateBuys.sol
Normal file
148
packages/asset-swapper/contracts/src/ApproximateBuys.sol
Normal file
@@ -0,0 +1,148 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
|
||||
|
||||
contract ApproximateBuys {
|
||||
|
||||
/// @dev Information computing buy quotes for sources that do not have native
|
||||
/// buy quote support.
|
||||
struct ApproximateBuyQuoteOpts {
|
||||
// Arbitrary maker token data to pass to `getSellQuoteCallback`.
|
||||
bytes makerTokenData;
|
||||
// Arbitrary taker token data to pass to `getSellQuoteCallback`.
|
||||
bytes takerTokenData;
|
||||
// Callback to retrieve a sell quote.
|
||||
function (bytes memory, bytes memory, uint256)
|
||||
internal
|
||||
view
|
||||
returns (uint256) getSellQuoteCallback;
|
||||
}
|
||||
|
||||
uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
|
||||
/// @dev Maximum approximate (positive) error rate when approximating a buy quote.
|
||||
uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
|
||||
/// @dev Maximum iterations to perform when approximating a buy quote.
|
||||
uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 5;
|
||||
|
||||
function _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts memory opts,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||
if (makerTokenAmounts.length == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
uint256 sellAmount = opts.getSellQuoteCallback(
|
||||
opts.makerTokenData,
|
||||
opts.takerTokenData,
|
||||
makerTokenAmounts[0]
|
||||
);
|
||||
if (sellAmount == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
uint256 buyAmount = opts.getSellQuoteCallback(
|
||||
opts.takerTokenData,
|
||||
opts.makerTokenData,
|
||||
sellAmount
|
||||
);
|
||||
if (buyAmount == 0) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
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(
|
||||
makerTokenAmounts[i],
|
||||
buyAmount,
|
||||
sellAmount
|
||||
);
|
||||
if (sellAmount == 0) {
|
||||
break;
|
||||
}
|
||||
sellAmount = _safeGetPartialAmountCeil(
|
||||
(ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
|
||||
ONE_HUNDED_PERCENT_BPS,
|
||||
sellAmount
|
||||
);
|
||||
if (sellAmount == 0) {
|
||||
break;
|
||||
}
|
||||
uint256 _buyAmount = opts.getSellQuoteCallback(
|
||||
opts.takerTokenData,
|
||||
opts.makerTokenData,
|
||||
sellAmount
|
||||
);
|
||||
if (_buyAmount == 0) {
|
||||
break;
|
||||
}
|
||||
// We re-use buyAmount next iteration, only assign if it is
|
||||
// non zero
|
||||
buyAmount = _buyAmount;
|
||||
// If we've reached our goal, exit early
|
||||
if (buyAmount >= makerTokenAmounts[i]) {
|
||||
eps =
|
||||
(buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
|
||||
makerTokenAmounts[i];
|
||||
if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
takerTokenAmounts[i] = _safeGetPartialAmountCeil(
|
||||
makerTokenAmounts[i],
|
||||
buyAmount,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _safeGetPartialAmountCeil(
|
||||
uint256 numerator,
|
||||
uint256 denominator,
|
||||
uint256 target
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256 partialAmount)
|
||||
{
|
||||
if (numerator == 0 || target == 0 || denominator == 0) return 0;
|
||||
uint256 c = numerator * target;
|
||||
if (c / numerator != target) return 0;
|
||||
return (c + (denominator - 1)) / denominator;
|
||||
}
|
||||
}
|
197
packages/asset-swapper/contracts/src/BalancerSampler.sol
Normal file
197
packages/asset-swapper/contracts/src/BalancerSampler.sol
Normal file
@@ -0,0 +1,197 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBalancer.sol";
|
||||
|
||||
|
||||
contract BalancerSampler {
|
||||
|
||||
/// @dev Base gas limit for Balancer calls.
|
||||
uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k
|
||||
|
||||
// Balancer math constants
|
||||
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
|
||||
uint256 constant private BONE = 10 ** 18;
|
||||
uint256 constant private MAX_IN_RATIO = BONE / 2;
|
||||
uint256 constant private MAX_OUT_RATIO = (BONE / 3) + 1 wei;
|
||||
|
||||
struct BalancerState {
|
||||
uint256 takerTokenBalance;
|
||||
uint256 makerTokenBalance;
|
||||
uint256 takerTokenWeight;
|
||||
uint256 makerTokenWeight;
|
||||
uint256 swapFee;
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from Balancer.
|
||||
/// @param poolAddress Address of the Balancer pool to query.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromBalancer(
|
||||
address poolAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
IBalancer pool = IBalancer(poolAddress);
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
BalancerState memory poolState;
|
||||
poolState.takerTokenBalance = pool.getBalance(takerToken);
|
||||
poolState.makerTokenBalance = pool.getBalance(makerToken);
|
||||
poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
|
||||
poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
|
||||
poolState.swapFee = pool.getSwapFee();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
// Handles this revert scenario:
|
||||
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443
|
||||
if (takerTokenAmounts[i] > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) {
|
||||
break;
|
||||
}
|
||||
try
|
||||
pool.calcOutGivenIn
|
||||
{gas: BALANCER_CALL_GAS}
|
||||
(
|
||||
poolState.takerTokenBalance,
|
||||
poolState.takerTokenWeight,
|
||||
poolState.makerTokenBalance,
|
||||
poolState.makerTokenWeight,
|
||||
takerTokenAmounts[i],
|
||||
poolState.swapFee
|
||||
)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Balancer.
|
||||
/// @param poolAddress Address of the Balancer pool to query.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromBalancer(
|
||||
address poolAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
IBalancer pool = IBalancer(poolAddress);
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
BalancerState memory poolState;
|
||||
poolState.takerTokenBalance = pool.getBalance(takerToken);
|
||||
poolState.makerTokenBalance = pool.getBalance(makerToken);
|
||||
poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
|
||||
poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
|
||||
poolState.swapFee = pool.getSwapFee();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
// Handles this revert scenario:
|
||||
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L505
|
||||
if (makerTokenAmounts[i] > _bmul(poolState.makerTokenBalance, MAX_OUT_RATIO)) {
|
||||
break;
|
||||
}
|
||||
try
|
||||
pool.calcInGivenOut
|
||||
{gas: BALANCER_CALL_GAS}
|
||||
(
|
||||
poolState.takerTokenBalance,
|
||||
poolState.takerTokenWeight,
|
||||
poolState.makerTokenBalance,
|
||||
poolState.makerTokenWeight,
|
||||
makerTokenAmounts[i],
|
||||
poolState.swapFee
|
||||
)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
// Handles this revert scenario:
|
||||
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443
|
||||
if (amount > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) {
|
||||
break;
|
||||
}
|
||||
|
||||
takerTokenAmounts[i] = amount;
|
||||
// Break early if there are 0 amounts
|
||||
if (takerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Hacked version of Balancer's `bmul` function, returning 0 instead
|
||||
/// of reverting.
|
||||
/// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L63-L73
|
||||
/// @param a The first operand.
|
||||
/// @param b The second operand.
|
||||
/// @param c The result of the multiplication, or 0 if `bmul` would've reverted.
|
||||
function _bmul(uint256 a, uint256 b)
|
||||
private
|
||||
pure
|
||||
returns (uint256 c)
|
||||
{
|
||||
uint c0 = a * b;
|
||||
if (a != 0 && c0 / a != b) {
|
||||
return 0;
|
||||
}
|
||||
uint c1 = c0 + (BONE / 2);
|
||||
if (c1 < c0) {
|
||||
return 0;
|
||||
}
|
||||
uint c2 = c1 / BONE;
|
||||
return c2;
|
||||
}
|
||||
}
|
142
packages/asset-swapper/contracts/src/BalancerV2Sampler.sol
Normal file
142
packages/asset-swapper/contracts/src/BalancerV2Sampler.sol
Normal file
@@ -0,0 +1,142 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./SamplerUtils.sol";
|
||||
import "./interfaces/IBalancerV2Vault.sol";
|
||||
import "./BalancerV2Common.sol";
|
||||
|
||||
|
||||
contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
|
||||
|
||||
/// @dev Sample sell quotes from Balancer V2.
|
||||
/// @param poolInfo Struct with pool related data
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromBalancerV2(
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||
address[] memory swapAssets = new address[](2);
|
||||
swapAssets[0] = takerToken;
|
||||
swapAssets[1] = makerToken;
|
||||
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
IBalancerV2Vault.FundManagement memory swapFunds =
|
||||
_createSwapFunds();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
||||
_createSwapSteps(poolInfo, takerTokenAmounts[i]);
|
||||
|
||||
try
|
||||
// For sells we specify the takerToken which is what the vault will receive from the trade
|
||||
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds)
|
||||
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
||||
returns (int256[] memory amounts) {
|
||||
// Outgoing balance is negative so we need to flip the sign
|
||||
int256 amountOutFromPool = amounts[amounts.length - 1] * -1;
|
||||
if (amountOutFromPool <= 0) {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = uint256(amountOutFromPool);
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Balancer V2.
|
||||
/// @param poolInfo Struct with pool related data
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromBalancerV2(
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||
address[] memory swapAssets = new address[](2);
|
||||
swapAssets[0] = takerToken;
|
||||
swapAssets[1] = makerToken;
|
||||
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
IBalancerV2Vault.FundManagement memory swapFunds =
|
||||
_createSwapFunds();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
||||
_createSwapSteps(poolInfo, makerTokenAmounts[i]);
|
||||
|
||||
try
|
||||
// For buys we specify the makerToken which is what taker will receive from the trade
|
||||
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds)
|
||||
returns (int256[] memory amounts) {
|
||||
int256 amountIntoPool = amounts[0];
|
||||
if (amountIntoPool <= 0) {
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = uint256(amountIntoPool);
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _createSwapSteps(
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
uint256 amount
|
||||
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
||||
new IBalancerV2Vault.BatchSwapStep[](1);
|
||||
swapSteps[0] = IBalancerV2Vault.BatchSwapStep({
|
||||
poolId: poolInfo.poolId,
|
||||
assetInIndex: 0,
|
||||
assetOutIndex: 1,
|
||||
amount: amount,
|
||||
userData: ""
|
||||
});
|
||||
|
||||
return swapSteps;
|
||||
}
|
||||
}
|
141
packages/asset-swapper/contracts/src/BancorSampler.sol
Normal file
141
packages/asset-swapper/contracts/src/BancorSampler.sol
Normal file
@@ -0,0 +1,141 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBancor.sol";
|
||||
|
||||
|
||||
contract BancorSampler {
|
||||
|
||||
/// @dev Base gas limit for Bancor calls.
|
||||
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
|
||||
|
||||
struct BancorSamplerOpts {
|
||||
IBancorRegistry registry;
|
||||
address[][] paths;
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from Bancor.
|
||||
/// @param opts BancorSamplerOpts The Bancor registry contract address and paths
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return bancorNetwork the Bancor Network address
|
||||
/// @return path the selected conversion path from bancor
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromBancor(
|
||||
BancorSamplerOpts memory opts,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
if (opts.paths.length == 0) {
|
||||
return (bancorNetwork, path, makerTokenAmounts);
|
||||
}
|
||||
(bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts);
|
||||
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
|
||||
|
||||
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
|
||||
try
|
||||
IBancorNetwork(bancorNetwork)
|
||||
.rateByPath
|
||||
{gas: BANCOR_CALL_GAS}
|
||||
(path, takerTokenAmounts[i])
|
||||
returns (uint256 amount)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (bancorNetwork, path, makerTokenAmounts);
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Bancor. Unimplemented
|
||||
/// @param opts BancorSamplerOpts The Bancor registry contract address and paths
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return bancorNetwork the Bancor Network address
|
||||
/// @return path the selected conversion path from bancor
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromBancor(
|
||||
BancorSamplerOpts memory opts,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (address bancorNetwork, address[] memory path, uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
}
|
||||
|
||||
function _findBestPath(
|
||||
BancorSamplerOpts memory opts,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (address bancorNetwork, address[] memory path)
|
||||
{
|
||||
bancorNetwork = opts.registry.getAddress(opts.registry.BANCOR_NETWORK());
|
||||
if (opts.paths.length == 0) {
|
||||
return (bancorNetwork, path);
|
||||
}
|
||||
uint256 maxBoughtAmount = 0;
|
||||
// Find the best path by selling the largest taker amount
|
||||
for (uint256 i = 0; i < opts.paths.length; i++) {
|
||||
if (opts.paths[i].length < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
IBancorNetwork(bancorNetwork)
|
||||
.rateByPath
|
||||
{gas: BANCOR_CALL_GAS}
|
||||
(opts.paths[i], takerTokenAmounts[takerTokenAmounts.length-1])
|
||||
returns (uint256 amount)
|
||||
{
|
||||
if (amount > maxBoughtAmount) {
|
||||
maxBoughtAmount = amount;
|
||||
path = opts.paths[i];
|
||||
}
|
||||
} catch {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
packages/asset-swapper/contracts/src/CompoundSampler.sol
Normal file
96
packages/asset-swapper/contracts/src/CompoundSampler.sol
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./SamplerUtils.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
// Minimal CToken interface
|
||||
interface ICToken {
|
||||
function mint(uint mintAmount) external returns (uint);
|
||||
function redeem(uint redeemTokens) external returns (uint);
|
||||
function redeemUnderlying(uint redeemAmount) external returns (uint);
|
||||
function exchangeRateStored() external view returns (uint);
|
||||
function decimals() external view returns (uint8);
|
||||
}
|
||||
|
||||
contract CompoundSampler is SamplerUtils {
|
||||
uint256 constant private EXCHANGE_RATE_SCALE = 1e10;
|
||||
|
||||
function sampleSellsFromCompound(
|
||||
ICToken cToken,
|
||||
IERC20TokenV06 takerToken,
|
||||
IERC20TokenV06 makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
|
||||
uint256 exchangeRate = cToken.exchangeRateStored();
|
||||
uint256 cTokenDecimals = uint256(cToken.decimals());
|
||||
|
||||
if (address(makerToken) == address(cToken)) {
|
||||
// mint
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
makerTokenAmounts[i] = (takerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals) / exchangeRate;
|
||||
}
|
||||
|
||||
} else if (address(takerToken) == address(cToken)) {
|
||||
// redeem
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
makerTokenAmounts[i] = (takerTokenAmounts[i] * exchangeRate) / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sampleBuysFromCompound(
|
||||
ICToken cToken,
|
||||
IERC20TokenV06 takerToken,
|
||||
IERC20TokenV06 makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
|
||||
uint256 exchangeRate = cToken.exchangeRateStored();
|
||||
uint256 cTokenDecimals = uint256(cToken.decimals());
|
||||
|
||||
if (address(makerToken) == address(cToken)) {
|
||||
// mint
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
takerTokenAmounts[i] = makerTokenAmounts[i] * exchangeRate / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
|
||||
}
|
||||
} else if (address(takerToken) == address(cToken)) {
|
||||
// redeem
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
takerTokenAmounts[i] = (makerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals)/exchangeRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
161
packages/asset-swapper/contracts/src/CurveSampler.sol
Normal file
161
packages/asset-swapper/contracts/src/CurveSampler.sol
Normal file
@@ -0,0 +1,161 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/ICurve.sol";
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
contract CurveSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
/// @dev Information for sampling from curve sources.
|
||||
struct CurveInfo {
|
||||
address poolAddress;
|
||||
bytes4 sellQuoteFunctionSelector;
|
||||
bytes4 buyQuoteFunctionSelector;
|
||||
}
|
||||
|
||||
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
|
||||
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
|
||||
uint256 constant private CURVE_CALL_GAS = 2000e3; // Was 600k for Curve but SnowSwap is using 1500k+
|
||||
|
||||
/// @dev Sample sell quotes from Curve.
|
||||
/// @param curveInfo Curve information specific to this token pair.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromCurve(
|
||||
CurveInfo memory curveInfo,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
curveInfo.sellQuoteFunctionSelector,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Curve.
|
||||
/// @param curveInfo Curve information specific to this token pair.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromCurve(
|
||||
CurveInfo memory curveInfo,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
if (curveInfo.buyQuoteFunctionSelector == bytes4(0)) {
|
||||
// Buys not supported on this curve, so approximate it.
|
||||
return _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(toTokenIdx, curveInfo),
|
||||
takerTokenData: abi.encode(fromTokenIdx, curveInfo),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromCurve
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
curveInfo.buyQuoteFunctionSelector,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
makerTokenAmounts[i]
|
||||
));
|
||||
uint256 sellAmount = 0;
|
||||
if (didSucceed) {
|
||||
sellAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
takerTokenAmounts[i] = sellAmount;
|
||||
// Break early if there are 0 amounts
|
||||
if (takerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromCurve(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(int128 takerTokenIdx, CurveInfo memory curveInfo) =
|
||||
abi.decode(takerTokenData, (int128, CurveInfo));
|
||||
(int128 makerTokenIdx) =
|
||||
abi.decode(makerTokenData, (int128));
|
||||
(bool success, bytes memory resultData) =
|
||||
address(this).staticcall(abi.encodeWithSelector(
|
||||
this.sampleSellsFromCurve.selector,
|
||||
curveInfo,
|
||||
takerTokenIdx,
|
||||
makerTokenIdx,
|
||||
_toSingleValueArray(sellAmount)
|
||||
));
|
||||
if (!success) {
|
||||
return 0;
|
||||
}
|
||||
// solhint-disable-next-line indent
|
||||
return abi.decode(resultData, (uint256[]))[0];
|
||||
}
|
||||
}
|
211
packages/asset-swapper/contracts/src/DODOSampler.sol
Normal file
211
packages/asset-swapper/contracts/src/DODOSampler.sol
Normal file
@@ -0,0 +1,211 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
interface IDODOZoo {
|
||||
function getDODO(address baseToken, address quoteToken) external view returns (address);
|
||||
}
|
||||
|
||||
interface IDODOHelper {
|
||||
function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
|
||||
}
|
||||
|
||||
interface IDODO {
|
||||
function querySellBaseToken(uint256 amount) external view returns (uint256);
|
||||
function _TRADE_ALLOWED_() external view returns (bool);
|
||||
}
|
||||
|
||||
contract DODOSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
|
||||
/// @dev Gas limit for DODO calls.
|
||||
uint256 constant private DODO_CALL_GAS = 300e3; // 300k
|
||||
struct DODOSamplerOpts {
|
||||
address registry;
|
||||
address helper;
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from DODO.
|
||||
/// @param opts DODOSamplerOpts DODO Registry and helper addresses
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return sellBase whether the bridge needs to sell the base token
|
||||
/// @return pool the DODO pool address
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromDODO(
|
||||
DODOSamplerOpts memory opts,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
|
||||
address baseToken;
|
||||
// If pool exists we have the correct order of Base/Quote
|
||||
if (pool != address(0)) {
|
||||
baseToken = takerToken;
|
||||
sellBase = true;
|
||||
} else {
|
||||
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
|
||||
// No pool either direction
|
||||
if (address(pool) == address(0)) {
|
||||
return (sellBase, pool, makerTokenAmounts);
|
||||
}
|
||||
baseToken = makerToken;
|
||||
sellBase = false;
|
||||
}
|
||||
|
||||
// DODO Pool has been disabled
|
||||
if (!IDODO(pool)._TRADE_ALLOWED_()) {
|
||||
return (sellBase, pool, makerTokenAmounts);
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
uint256 buyAmount = _sampleSellForApproximateBuyFromDODO(
|
||||
abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data
|
||||
abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from DODO.
|
||||
/// @param opts DODOSamplerOpts DODO Registry and helper addresses
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||
/// @return sellBase whether the bridge needs to sell the base token
|
||||
/// @return pool the DODO pool address
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromDODO(
|
||||
DODOSamplerOpts memory opts,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// Pool is BASE/QUOTE
|
||||
// Look up the pool from the taker/maker combination
|
||||
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
|
||||
address baseToken;
|
||||
// If pool exists we have the correct order of Base/Quote
|
||||
if (pool != address(0)) {
|
||||
baseToken = takerToken;
|
||||
sellBase = true;
|
||||
} else {
|
||||
// Look up the pool from the maker/taker combination
|
||||
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
|
||||
// No pool either direction
|
||||
if (address(pool) == address(0)) {
|
||||
return (sellBase, pool, takerTokenAmounts);
|
||||
}
|
||||
baseToken = makerToken;
|
||||
sellBase = false;
|
||||
}
|
||||
|
||||
// DODO Pool has been disabled
|
||||
if (!IDODO(pool)._TRADE_ALLOWED_()) {
|
||||
return (sellBase, pool, takerTokenAmounts);
|
||||
}
|
||||
|
||||
takerTokenAmounts = _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper),
|
||||
takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromDODO(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory /* makerTokenData */,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
(address takerToken, address pool, address baseToken, address helper) = abi.decode(
|
||||
takerTokenData,
|
||||
(address, address, address, address)
|
||||
);
|
||||
|
||||
// We will get called to sell both the taker token and also to sell the maker token
|
||||
if (takerToken == baseToken) {
|
||||
// If base token then use the original query on the pool
|
||||
try
|
||||
IDODO(pool).querySellBaseToken
|
||||
{gas: DODO_CALL_GAS}
|
||||
(sellAmount)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
return amount;
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// If quote token then use helper, this is less accurate
|
||||
try
|
||||
IDODOHelper(helper).querySellQuoteToken
|
||||
{gas: DODO_CALL_GAS}
|
||||
(pool, sellAmount)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
return amount;
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
204
packages/asset-swapper/contracts/src/DODOV2Sampler.sol
Normal file
204
packages/asset-swapper/contracts/src/DODOV2Sampler.sol
Normal file
@@ -0,0 +1,204 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
interface IDODOV2Registry {
|
||||
function getDODOPool(address baseToken, address quoteToken)
|
||||
external
|
||||
view
|
||||
returns (address[] memory machines);
|
||||
}
|
||||
|
||||
interface IDODOV2Pool {
|
||||
function querySellBase(address trader, uint256 payBaseAmount)
|
||||
external
|
||||
view
|
||||
returns (uint256 receiveQuoteAmount, uint256 mtFee);
|
||||
|
||||
function querySellQuote(address trader, uint256 payQuoteAmount)
|
||||
external
|
||||
view
|
||||
returns (uint256 receiveBaseAmount, uint256 mtFee);
|
||||
}
|
||||
|
||||
contract DODOV2Sampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
|
||||
/// @dev Gas limit for DODO V2 calls.
|
||||
uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k
|
||||
|
||||
/// @dev Sample sell quotes from DODO V2.
|
||||
/// @param registry Address of the registry to look up.
|
||||
/// @param offset offset index for the pool in the registry.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return sellBase whether the bridge needs to sell the base token
|
||||
/// @return pool the DODO pool address
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromDODOV2(
|
||||
address registry,
|
||||
uint256 offset,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
|
||||
if (pool == address(0)) {
|
||||
return (sellBase, pool, makerTokenAmounts);
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2(
|
||||
abi.encode(takerToken, pool, sellBase), // taker token data
|
||||
abi.encode(makerToken, pool, sellBase), // maker token data
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from DODO.
|
||||
/// @param registry Address of the registry to look up.
|
||||
/// @param offset offset index for the pool in the registry.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||
/// @return sellBase whether the bridge needs to sell the base token
|
||||
/// @return pool the DODO pool address
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromDODOV2(
|
||||
address registry,
|
||||
uint256 offset,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
|
||||
if (pool == address(0)) {
|
||||
return (sellBase, pool, takerTokenAmounts);
|
||||
}
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
takerTokenAmounts = _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(makerToken, pool, !sellBase),
|
||||
takerTokenData: abi.encode(takerToken, pool, sellBase),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromDODOV2(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory /* makerTokenData */,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
(address takerToken, address pool, bool sellBase) = abi.decode(
|
||||
takerTokenData,
|
||||
(address, address, bool)
|
||||
);
|
||||
|
||||
// We will get called to sell both the taker token and also to sell the maker token
|
||||
// since we use approximate buy for sell and buy functions
|
||||
if (sellBase) {
|
||||
try
|
||||
IDODOV2Pool(pool).querySellBase
|
||||
{ gas: DODO_V2_CALL_GAS }
|
||||
(address(0), sellAmount)
|
||||
returns (uint256 amount, uint256)
|
||||
{
|
||||
return amount;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
try
|
||||
IDODOV2Pool(pool).querySellQuote
|
||||
{ gas: DODO_V2_CALL_GAS }
|
||||
(address(0), sellAmount)
|
||||
returns (uint256 amount, uint256)
|
||||
{
|
||||
return amount;
|
||||
} catch {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getNextDODOV2Pool(
|
||||
address registry,
|
||||
uint256 offset,
|
||||
address takerToken,
|
||||
address makerToken
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (address machine, bool sellBase)
|
||||
{
|
||||
// Query in base -> quote direction, if a pool is found then we are selling the base
|
||||
address[] memory machines = IDODOV2Registry(registry).getDODOPool(takerToken, makerToken);
|
||||
sellBase = true;
|
||||
if (machines.length == 0) {
|
||||
// Query in quote -> base direction, if a pool is found then we are selling the quote
|
||||
machines = IDODOV2Registry(registry).getDODOPool(makerToken, takerToken);
|
||||
sellBase = false;
|
||||
}
|
||||
|
||||
if (offset >= machines.length) {
|
||||
return (address(0), false);
|
||||
}
|
||||
|
||||
machine = machines[offset];
|
||||
}
|
||||
|
||||
}
|
@@ -24,7 +24,6 @@ import "./BalancerSampler.sol";
|
||||
import "./BalancerV2Sampler.sol";
|
||||
import "./BalancerV2BatchSampler.sol";
|
||||
import "./BancorSampler.sol";
|
||||
import "./BancorV3Sampler.sol";
|
||||
import "./CompoundSampler.sol";
|
||||
import "./CurveSampler.sol";
|
||||
import "./DODOSampler.sol";
|
||||
@@ -44,7 +43,6 @@ import "./TwoHopSampler.sol";
|
||||
import "./UniswapSampler.sol";
|
||||
import "./UniswapV2Sampler.sol";
|
||||
import "./UniswapV3Sampler.sol";
|
||||
import "./VelodromeSampler.sol";
|
||||
import "./UtilitySampler.sol";
|
||||
|
||||
|
||||
@@ -53,7 +51,6 @@ contract ERC20BridgeSampler is
|
||||
BalancerV2Sampler,
|
||||
BalancerV2BatchSampler,
|
||||
BancorSampler,
|
||||
BancorV3Sampler,
|
||||
CompoundSampler,
|
||||
CurveSampler,
|
||||
DODOSampler,
|
||||
@@ -73,7 +70,6 @@ contract ERC20BridgeSampler is
|
||||
UniswapSampler,
|
||||
UniswapV2Sampler,
|
||||
UniswapV3Sampler,
|
||||
VelodromeSampler,
|
||||
UtilitySampler
|
||||
{
|
||||
|
||||
|
179
packages/asset-swapper/contracts/src/KyberDmmSampler.sol
Normal file
179
packages/asset-swapper/contracts/src/KyberDmmSampler.sol
Normal file
@@ -0,0 +1,179 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IKyberDmmPool {
|
||||
|
||||
function totalSupply()
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
}
|
||||
|
||||
interface IKyberDmmFactory {
|
||||
|
||||
function getPools(address token0, address token1)
|
||||
external
|
||||
view
|
||||
returns (address[] memory _tokenPools);
|
||||
}
|
||||
|
||||
interface IKyberDmmRouter {
|
||||
|
||||
function factory() external view returns (address);
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata pools, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata pools, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
}
|
||||
|
||||
|
||||
|
||||
contract KyberDmmSampler
|
||||
{
|
||||
/// @dev Gas limit for KyberDmm calls.
|
||||
uint256 constant private KYBER_DMM_CALL_GAS = 150e3; // 150k
|
||||
|
||||
/// @dev Sample sell quotes from KyberDmm.
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return pools The pool addresses involved in the multi path trade
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromKyberDmm(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (address[] memory pools, uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
pools = _getKyberDmmPools(router, path);
|
||||
if (pools.length == 0) {
|
||||
return (pools, makerTokenAmounts);
|
||||
}
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IKyberDmmRouter(router).getAmountsOut
|
||||
{gas: KYBER_DMM_CALL_GAS}
|
||||
(takerTokenAmounts[i], pools, path)
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
makerTokenAmounts[i] = amounts[path.length - 1];
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from KyberDmm.
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return pools The pool addresses involved in the multi path trade
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromKyberDmm(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (address[] memory pools, uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
pools = _getKyberDmmPools(router, path);
|
||||
if (pools.length == 0) {
|
||||
return (pools, takerTokenAmounts);
|
||||
}
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IKyberDmmRouter(router).getAmountsIn
|
||||
{gas: KYBER_DMM_CALL_GAS}
|
||||
(makerTokenAmounts[i], pools, path)
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
takerTokenAmounts[i] = amounts[0];
|
||||
// Break early if there are 0 amounts
|
||||
if (takerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getKyberDmmPools(
|
||||
address router,
|
||||
address[] memory path
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (address[] memory pools)
|
||||
{
|
||||
IKyberDmmFactory factory = IKyberDmmFactory(IKyberDmmRouter(router).factory());
|
||||
pools = new address[](path.length - 1);
|
||||
for (uint256 i = 0; i < pools.length; i++) {
|
||||
// find the best pool
|
||||
address[] memory allPools;
|
||||
try
|
||||
factory.getPools
|
||||
{gas: KYBER_DMM_CALL_GAS}
|
||||
(path[i], path[i + 1])
|
||||
returns (address[] memory allPools)
|
||||
{
|
||||
if (allPools.length == 0) {
|
||||
return new address[](0);
|
||||
}
|
||||
|
||||
uint256 maxSupply = 0;
|
||||
for (uint256 j = 0; j < allPools.length; j++) {
|
||||
uint256 totalSupply = IKyberDmmPool(allPools[j]).totalSupply();
|
||||
if (totalSupply > maxSupply) {
|
||||
maxSupply = totalSupply;
|
||||
pools[i] = allPools[j];
|
||||
}
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
return new address[](0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
91
packages/asset-swapper/contracts/src/LidoSampler.sol
Normal file
91
packages/asset-swapper/contracts/src/LidoSampler.sol
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
contract LidoSampler is SamplerUtils {
|
||||
struct LidoInfo {
|
||||
address stEthToken;
|
||||
address wethToken;
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from Lido
|
||||
/// @param lidoInfo Info regarding a specific Lido deployment
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromLido(
|
||||
LidoInfo memory lidoInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
|
||||
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
|
||||
// Return 0 values if not selling WETH for stETH
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
uint256[] memory makerTokenAmounts = new uint256[](numSamples);
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
// Minting stETH is always 1:1 therefore we can just return the same amounts back
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Lido.
|
||||
/// @param lidoInfo Info regarding a specific Lido deployment
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromLido(
|
||||
LidoInfo memory lidoInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
pure
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
|
||||
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
|
||||
// Return 0 values if not buying stETH for WETH
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
uint256[] memory takerTokenAmounts = new uint256[](numSamples);
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
// Minting stETH is always 1:1 therefore we can just return the same amounts back
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,132 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-zero-ex/contracts/src/vendor/ILiquidityProvider.sol";
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
contract LiquidityProviderSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
/// @dev Default gas limit for liquidity provider calls.
|
||||
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
|
||||
|
||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param providerAddress Address of the liquidity provider.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromLiquidityProvider(
|
||||
address providerAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
ILiquidityProvider(providerAddress).getSellQuote
|
||||
{gas: DEFAULT_CALL_GAS}
|
||||
(
|
||||
IERC20TokenV06(takerToken),
|
||||
IERC20TokenV06(makerToken),
|
||||
takerTokenAmounts[i]
|
||||
)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||
/// @param providerAddress Address of the liquidity provider.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromLiquidityProvider(
|
||||
address providerAddress,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
takerTokenAmounts = _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(makerToken, providerAddress),
|
||||
takerTokenData: abi.encode(takerToken, providerAddress),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProvider
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromLiquidityProvider(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(address takerToken, address providerAddress) =
|
||||
abi.decode(takerTokenData, (address, address));
|
||||
(address makerToken) =
|
||||
abi.decode(makerTokenData, (address));
|
||||
try
|
||||
this.sampleSellsFromLiquidityProvider
|
||||
{gas: DEFAULT_CALL_GAS}
|
||||
(providerAddress, takerToken, makerToken, _toSingleValueArray(sellAmount))
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
return amounts[0];
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
127
packages/asset-swapper/contracts/src/MStableSampler.sol
Normal file
127
packages/asset-swapper/contracts/src/MStableSampler.sol
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IMStable.sol";
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
contract MStableSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
/// @dev Default gas limit for mStable calls.
|
||||
uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k
|
||||
|
||||
/// @dev Sample sell quotes from the mStable contract
|
||||
/// @param router Address of the mStable contract
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromMStable(
|
||||
address router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IMStable(router).getSwapOutput
|
||||
{gas: DEFAULT_CALL_GAS}
|
||||
(takerToken, makerToken, takerTokenAmounts[i])
|
||||
returns (uint256 amount)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from MStable contract
|
||||
/// @param router Address of the mStable contract
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromMStable(
|
||||
address router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
return _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(makerToken, router),
|
||||
takerTokenData: abi.encode(takerToken, router),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromMStable(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(address takerToken, address router) =
|
||||
abi.decode(takerTokenData, (address, address));
|
||||
(address makerToken) =
|
||||
abi.decode(makerTokenData, (address));
|
||||
try
|
||||
this.sampleSellsFromMStable
|
||||
(router, takerToken, makerToken, _toSingleValueArray(sellAmount))
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
return amounts[0];
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
267
packages/asset-swapper/contracts/src/MakerPSMSampler.sol
Normal file
267
packages/asset-swapper/contracts/src/MakerPSMSampler.sol
Normal file
@@ -0,0 +1,267 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./SamplerUtils.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
|
||||
interface IPSM {
|
||||
// @dev Get the fee for selling USDC to DAI in PSM
|
||||
// @return tin toll in [wad]
|
||||
function tin() external view returns (uint256);
|
||||
// @dev Get the fee for selling DAI to USDC in PSM
|
||||
// @return tout toll out [wad]
|
||||
function tout() external view returns (uint256);
|
||||
|
||||
// @dev Get the address of the PSM state Vat
|
||||
// @return address of the Vat
|
||||
function vat() external view returns (address);
|
||||
|
||||
// @dev Get the address of the underlying vault powering PSM
|
||||
// @return address of gemJoin contract
|
||||
function gemJoin() external view returns (address);
|
||||
|
||||
// @dev Get the address of DAI
|
||||
// @return address of DAI contract
|
||||
function dai() external view returns (address);
|
||||
|
||||
// @dev Sell USDC for DAI
|
||||
// @param usr The address of the account trading USDC for DAI.
|
||||
// @param gemAmt The amount of USDC to sell in USDC base units
|
||||
function sellGem(
|
||||
address usr,
|
||||
uint256 gemAmt
|
||||
) external;
|
||||
// @dev Buy USDC for DAI
|
||||
// @param usr The address of the account trading DAI for USDC
|
||||
// @param gemAmt The amount of USDC to buy in USDC base units
|
||||
function buyGem(
|
||||
address usr,
|
||||
uint256 gemAmt
|
||||
) external;
|
||||
}
|
||||
|
||||
interface IVAT {
|
||||
// @dev Get a collateral type by identifier
|
||||
// @param ilkIdentifier bytes32 identifier. Example: ethers.utils.formatBytes32String("PSM-USDC-A")
|
||||
// @return ilk
|
||||
// @return ilk.Art Total Normalised Debt in wad
|
||||
// @return ilk.rate Accumulated Rates in ray
|
||||
// @return ilk.spot Price with Safety Margin in ray
|
||||
// @return ilk.line Debt Ceiling in rad
|
||||
// @return ilk.dust Urn Debt Floor in rad
|
||||
function ilks(
|
||||
bytes32 ilkIdentifier
|
||||
) external view returns (
|
||||
uint256 Art,
|
||||
uint256 rate,
|
||||
uint256 spot,
|
||||
uint256 line,
|
||||
uint256 dust
|
||||
);
|
||||
}
|
||||
|
||||
contract MakerPSMSampler is
|
||||
SamplerUtils
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
/// @dev Information about which PSM module to use
|
||||
struct MakerPsmInfo {
|
||||
address psmAddress;
|
||||
bytes32 ilkIdentifier;
|
||||
address gemTokenAddress;
|
||||
}
|
||||
|
||||
/// @dev Gas limit for MakerPsm calls.
|
||||
uint256 constant private MAKER_PSM_CALL_GAS = 300e3; // 300k
|
||||
|
||||
|
||||
// Maker units
|
||||
// wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)
|
||||
uint256 constant private WAD = 10 ** 18;
|
||||
// ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios)
|
||||
uint256 constant private RAY = 10 ** 27;
|
||||
// rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray)
|
||||
uint256 constant private RAD = 10 ** 45;
|
||||
// See https://github.com/makerdao/dss/blob/master/DEVELOPING.m
|
||||
|
||||
/// @dev Sample sell quotes from Maker PSM
|
||||
function sampleSellsFromMakerPsm(
|
||||
MakerPsmInfo memory psmInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IPSM psm = IPSM(psmInfo.psmAddress);
|
||||
IVAT vat = IVAT(psm.vat());
|
||||
|
||||
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
if (makerToken != psm.dai() && takerToken != psm.dai()) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
uint256 buyAmount = _samplePSMSell(psmInfo, makerToken, takerToken, takerTokenAmounts[i], psm, vat);
|
||||
|
||||
if (buyAmount == 0) {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
function sampleBuysFromMakerPsm(
|
||||
MakerPsmInfo memory psmInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IPSM psm = IPSM(psmInfo.psmAddress);
|
||||
IVAT vat = IVAT(psm.vat());
|
||||
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
if (makerToken != psm.dai() && takerToken != psm.dai()) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
uint256 sellAmount = _samplePSMBuy(psmInfo, makerToken, takerToken, makerTokenAmounts[i], psm, vat);
|
||||
|
||||
if (sellAmount == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
takerTokenAmounts[i] = sellAmount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function _samplePSMSell(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 takerTokenAmount, IPSM psm, IVAT vat)
|
||||
private
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
(uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
|
||||
uint256 gemTokenBaseUnit = uint256(1e6);
|
||||
|
||||
if (takerToken == psmInfo.gemTokenAddress) {
|
||||
// Simulate sellGem
|
||||
// Selling USDC to the PSM, increasing the total debt
|
||||
// Convert USDC 6 decimals to 18 decimals [wad]
|
||||
uint256 takerTokenAmountInWad = takerTokenAmount.safeMul(1e12);
|
||||
|
||||
uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
|
||||
|
||||
// PSM is too full to fit
|
||||
if (newTotalDebtInRad >= debtCeilingInRad) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint256 feeInWad = takerTokenAmountInWad.safeMul(psm.tin()).safeDiv(WAD);
|
||||
uint256 makerTokenAmountInWad = takerTokenAmountInWad.safeSub(feeInWad);
|
||||
|
||||
return makerTokenAmountInWad;
|
||||
} else if (makerToken == psmInfo.gemTokenAddress) {
|
||||
// Simulate buyGem
|
||||
// Buying USDC from the PSM, decreasing the total debt
|
||||
// Selling DAI for USDC, already in 18 decimals [wad]
|
||||
uint256 takerTokenAmountInWad = takerTokenAmount;
|
||||
if (takerTokenAmountInWad > totalDebtInWad) {
|
||||
return 0;
|
||||
}
|
||||
uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
|
||||
|
||||
// PSM is empty, not enough USDC to buy from it
|
||||
if (newTotalDebtInRad <= debtFloorInRad) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint256 feeDivisorInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
|
||||
uint256 makerTokenAmountInGemTokenBaseUnits = takerTokenAmountInWad.safeMul(gemTokenBaseUnit).safeDiv(feeDivisorInWad);
|
||||
|
||||
return makerTokenAmountInGemTokenBaseUnits;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function _samplePSMBuy(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 makerTokenAmount, IPSM psm, IVAT vat)
|
||||
private
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
(uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
|
||||
|
||||
if (takerToken == psmInfo.gemTokenAddress) {
|
||||
// Simulate sellGem
|
||||
// Selling USDC to the PSM, increasing the total debt
|
||||
uint256 makerTokenAmountInWad = makerTokenAmount;
|
||||
uint256 feeDivisorInWad = WAD.safeSub(psm.tin()); // eg. 0.999 * 10 ** 18 with 0.1% tin;
|
||||
uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(WAD).safeDiv(feeDivisorInWad);
|
||||
uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
|
||||
|
||||
// PSM is too full to fit
|
||||
if (newTotalDebtInRad >= debtCeilingInRad) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint256 takerTokenAmountInGemInGemBaseUnits = (takerTokenAmountInWad.safeDiv(1e12)).safeAdd(1); // Add 1 to deal with cut off decimals converting to lower decimals
|
||||
|
||||
return takerTokenAmountInGemInGemBaseUnits;
|
||||
} else if (makerToken == psmInfo.gemTokenAddress) {
|
||||
// Simulate buyGem
|
||||
// Buying USDC from the PSM, decreasing the total debt
|
||||
uint256 makerTokenAmountInWad = makerTokenAmount.safeMul(1e12);
|
||||
uint256 feeMultiplierInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
|
||||
uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(feeMultiplierInWad).safeDiv(WAD);
|
||||
if (takerTokenAmountInWad > totalDebtInWad) {
|
||||
return 0;
|
||||
}
|
||||
uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
|
||||
|
||||
// PSM is empty, not enough USDC to buy
|
||||
if (newTotalDebtInRad <= debtFloorInRad) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return takerTokenAmountInWad;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
169
packages/asset-swapper/contracts/src/MooniswapSampler.sol
Normal file
169
packages/asset-swapper/contracts/src/MooniswapSampler.sol
Normal file
@@ -0,0 +1,169 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IMooniswap.sol";
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
contract MooniswapSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
/// @dev Gas limit for Mooniswap calls.
|
||||
uint256 constant private MOONISWAP_CALL_GAS = 150e3; // 150k
|
||||
|
||||
/// @dev Sample sell quotes from Mooniswap.
|
||||
/// @param registry Address of the Mooniswap Registry.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return pool The contract address for the pool
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromMooniswap(
|
||||
address registry,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (IMooniswap pool, uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
uint256 buyAmount = sampleSingleSellFromMooniswapPool(
|
||||
registry,
|
||||
takerToken,
|
||||
makerToken,
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pool = IMooniswap(
|
||||
IMooniswapRegistry(registry).pools(takerToken, makerToken)
|
||||
);
|
||||
}
|
||||
|
||||
function sampleSingleSellFromMooniswapPool(
|
||||
address registry,
|
||||
address mooniswapTakerToken,
|
||||
address mooniswapMakerToken,
|
||||
uint256 takerTokenAmount
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
// Find the pool for the pair.
|
||||
IMooniswap pool = IMooniswap(
|
||||
IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken)
|
||||
);
|
||||
// If there is no pool then return early
|
||||
if (address(pool) == address(0)) {
|
||||
return 0;
|
||||
}
|
||||
uint256 poolBalance = mooniswapTakerToken == address(0)
|
||||
? address(pool).balance
|
||||
: IERC20TokenV06(mooniswapTakerToken).balanceOf(address(pool));
|
||||
// If the pool balance is smaller than the sell amount
|
||||
// don't sample to avoid multiplication overflow in buys
|
||||
if (poolBalance < takerTokenAmount) {
|
||||
return 0;
|
||||
}
|
||||
try
|
||||
pool.getReturn
|
||||
{gas: MOONISWAP_CALL_GAS}
|
||||
(mooniswapTakerToken, mooniswapMakerToken, takerTokenAmount)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
return amount;
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Mooniswap.
|
||||
/// @param registry Address of the Mooniswap Registry.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||
/// @return pool The contract address for the pool
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromMooniswap(
|
||||
address registry,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (IMooniswap pool, uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
takerTokenAmounts = _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(registry, makerToken),
|
||||
takerTokenData: abi.encode(registry, takerToken),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
|
||||
pool = IMooniswap(
|
||||
IMooniswapRegistry(registry).pools(takerToken, makerToken)
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromMooniswap(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(address registry, address mooniswapTakerToken) = abi.decode(takerTokenData, (address, address));
|
||||
(address _registry, address mooniswapMakerToken) = abi.decode(makerTokenData, (address, address));
|
||||
return sampleSingleSellFromMooniswapPool(
|
||||
registry,
|
||||
mooniswapTakerToken,
|
||||
mooniswapMakerToken,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
}
|
239
packages/asset-swapper/contracts/src/NativeOrderSampler.sol
Normal file
239
packages/asset-swapper/contracts/src/NativeOrderSampler.sol
Normal file
@@ -0,0 +1,239 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
|
||||
|
||||
interface IExchange {
|
||||
|
||||
enum OrderStatus {
|
||||
INVALID,
|
||||
FILLABLE,
|
||||
FILLED,
|
||||
CANCELLED,
|
||||
EXPIRED
|
||||
}
|
||||
|
||||
/// @dev A standard OTC or OO limit order.
|
||||
struct LimitOrder {
|
||||
IERC20TokenV06 makerToken;
|
||||
IERC20TokenV06 takerToken;
|
||||
uint128 makerAmount;
|
||||
uint128 takerAmount;
|
||||
uint128 takerTokenFeeAmount;
|
||||
address maker;
|
||||
address taker;
|
||||
address sender;
|
||||
address feeRecipient;
|
||||
bytes32 pool;
|
||||
uint64 expiry;
|
||||
uint256 salt;
|
||||
}
|
||||
|
||||
/// @dev An RFQ limit order.
|
||||
struct RfqOrder {
|
||||
IERC20TokenV06 makerToken;
|
||||
IERC20TokenV06 takerToken;
|
||||
uint128 makerAmount;
|
||||
uint128 takerAmount;
|
||||
address maker;
|
||||
address taker;
|
||||
address txOrigin;
|
||||
bytes32 pool;
|
||||
uint64 expiry;
|
||||
uint256 salt;
|
||||
}
|
||||
|
||||
/// @dev Info on a limit or RFQ order.
|
||||
struct OrderInfo {
|
||||
bytes32 orderHash;
|
||||
OrderStatus status;
|
||||
uint128 takerTokenFilledAmount;
|
||||
}
|
||||
|
||||
/// @dev Allowed signature types.
|
||||
enum SignatureType {
|
||||
ILLEGAL,
|
||||
INVALID,
|
||||
EIP712,
|
||||
ETHSIGN
|
||||
}
|
||||
|
||||
/// @dev Encoded EC signature.
|
||||
struct Signature {
|
||||
// How to validate the signature.
|
||||
SignatureType signatureType;
|
||||
// EC Signature data.
|
||||
uint8 v;
|
||||
// EC Signature data.
|
||||
bytes32 r;
|
||||
// EC Signature data.
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
/// @dev Get the order info for a limit order.
|
||||
/// @param order The limit order.
|
||||
/// @return orderInfo Info about the order.
|
||||
function getLimitOrderInfo(LimitOrder memory order)
|
||||
external
|
||||
view
|
||||
returns (OrderInfo memory orderInfo);
|
||||
|
||||
/// @dev Get order info, fillable amount, and signature validity for a limit order.
|
||||
/// Fillable amount is determined using balances and allowances of the maker.
|
||||
/// @param order The limit order.
|
||||
/// @param signature The order signature.
|
||||
/// @return orderInfo Info about the order.
|
||||
/// @return actualFillableTakerTokenAmount How much of the order is fillable
|
||||
/// based on maker funds, in taker tokens.
|
||||
/// @return isSignatureValid Whether the signature is valid.
|
||||
function getLimitOrderRelevantState(
|
||||
LimitOrder memory order,
|
||||
Signature calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
OrderInfo memory orderInfo,
|
||||
uint128 actualFillableTakerTokenAmount,
|
||||
bool isSignatureValid
|
||||
);
|
||||
}
|
||||
|
||||
contract NativeOrderSampler {
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibBytesV06 for bytes;
|
||||
|
||||
/// @dev Gas limit for calls to `getOrderFillableTakerAmount()`.
|
||||
uint256 constant internal DEFAULT_CALL_GAS = 200e3; // 200k
|
||||
|
||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||
/// Effectively ignores orders that have empty signatures or
|
||||
/// maker/taker asset amounts (returning 0).
|
||||
/// @param orders Native limit orders to query.
|
||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||
/// @param exchange The V4 exchange.
|
||||
/// @return orderFillableTakerAssetAmounts How much taker asset can be filled
|
||||
/// by each order in `orders`.
|
||||
function getLimitOrderFillableTakerAssetAmounts(
|
||||
IExchange.LimitOrder[] memory orders,
|
||||
IExchange.Signature[] memory orderSignatures,
|
||||
IExchange exchange
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory orderFillableTakerAssetAmounts)
|
||||
{
|
||||
orderFillableTakerAssetAmounts = new uint256[](orders.length);
|
||||
for (uint256 i = 0; i != orders.length; i++) {
|
||||
try
|
||||
this.getLimitOrderFillableTakerAmount
|
||||
{gas: DEFAULT_CALL_GAS}
|
||||
(
|
||||
orders[i],
|
||||
orderSignatures[i],
|
||||
exchange
|
||||
)
|
||||
returns (uint256 amount)
|
||||
{
|
||||
orderFillableTakerAssetAmounts[i] = amount;
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
orderFillableTakerAssetAmounts[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||
/// Effectively ignores orders that have empty signatures or
|
||||
/// @param orders Native orders to query.
|
||||
/// @param orderSignatures Signatures for each respective order in `orders`.
|
||||
/// @param exchange The V4 exchange.
|
||||
/// @return orderFillableMakerAssetAmounts How much maker asset can be filled
|
||||
/// by each order in `orders`.
|
||||
function getLimitOrderFillableMakerAssetAmounts(
|
||||
IExchange.LimitOrder[] memory orders,
|
||||
IExchange.Signature[] memory orderSignatures,
|
||||
IExchange exchange
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory orderFillableMakerAssetAmounts)
|
||||
{
|
||||
orderFillableMakerAssetAmounts = getLimitOrderFillableTakerAssetAmounts(
|
||||
orders,
|
||||
orderSignatures,
|
||||
exchange
|
||||
);
|
||||
// `orderFillableMakerAssetAmounts` now holds taker asset amounts, so
|
||||
// convert them to maker asset amounts.
|
||||
for (uint256 i = 0; i < orders.length; ++i) {
|
||||
if (orderFillableMakerAssetAmounts[i] != 0) {
|
||||
orderFillableMakerAssetAmounts[i] = LibMathV06.getPartialAmountCeil(
|
||||
orderFillableMakerAssetAmounts[i],
|
||||
orders[i].takerAmount,
|
||||
orders[i].makerAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Get the fillable taker amount of an order, taking into account
|
||||
/// order state, maker fees, and maker balances.
|
||||
function getLimitOrderFillableTakerAmount(
|
||||
IExchange.LimitOrder memory order,
|
||||
IExchange.Signature memory signature,
|
||||
IExchange exchange
|
||||
)
|
||||
virtual
|
||||
public
|
||||
view
|
||||
returns (uint256 fillableTakerAmount)
|
||||
{
|
||||
if (signature.signatureType == IExchange.SignatureType.ILLEGAL ||
|
||||
signature.signatureType == IExchange.SignatureType.INVALID ||
|
||||
order.makerAmount == 0 ||
|
||||
order.takerAmount == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
(
|
||||
IExchange.OrderInfo memory orderInfo,
|
||||
uint128 remainingFillableTakerAmount,
|
||||
bool isSignatureValid
|
||||
) = exchange.getLimitOrderRelevantState(order, signature);
|
||||
|
||||
if (
|
||||
orderInfo.status != IExchange.OrderStatus.FILLABLE ||
|
||||
!isSignatureValid ||
|
||||
order.makerToken == IERC20TokenV06(0)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
fillableTakerAmount = uint256(remainingFillableTakerAmount);
|
||||
}
|
||||
}
|
58
packages/asset-swapper/contracts/src/SamplerUtils.sol
Normal file
58
packages/asset-swapper/contracts/src/SamplerUtils.sol
Normal file
@@ -0,0 +1,58 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
|
||||
|
||||
contract SamplerUtils {
|
||||
|
||||
/// @dev Overridable way to get token decimals.
|
||||
/// @param tokenAddress Address of the token.
|
||||
/// @return decimals The decimal places for the token.
|
||||
function _getTokenDecimals(address tokenAddress)
|
||||
virtual
|
||||
internal
|
||||
view
|
||||
returns (uint8 decimals)
|
||||
{
|
||||
return LibERC20TokenV06.compatDecimals(IERC20TokenV06(tokenAddress));
|
||||
}
|
||||
|
||||
function _toSingleValueArray(uint256 v)
|
||||
internal
|
||||
pure
|
||||
returns (uint256[] memory arr)
|
||||
{
|
||||
arr = new uint256[](1);
|
||||
arr[0] = v;
|
||||
}
|
||||
|
||||
/// @dev Assert that the tokens in a trade pair are valid.
|
||||
/// @param makerToken Address of the maker token.
|
||||
/// @param takerToken Address of the taker token.
|
||||
function _assertValidPair(address makerToken, address takerToken)
|
||||
internal
|
||||
pure
|
||||
{
|
||||
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
||||
}
|
||||
}
|
126
packages/asset-swapper/contracts/src/ShellSampler.sol
Normal file
126
packages/asset-swapper/contracts/src/ShellSampler.sol
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./interfaces/IShell.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
contract ShellSampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
|
||||
struct ShellInfo {
|
||||
address poolAddress;
|
||||
}
|
||||
|
||||
/// @dev Default gas limit for Shell calls.
|
||||
uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k
|
||||
|
||||
/// @dev Sample sell quotes from the Shell pool contract
|
||||
/// @param pool Address of the Shell pool contract
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromShell(
|
||||
address pool,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IShell(pool).viewOriginSwap
|
||||
{gas: DEFAULT_CALL_GAS}
|
||||
(takerToken, makerToken, takerTokenAmounts[i])
|
||||
returns (uint256 amount)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Shell pool contract
|
||||
/// @param pool Address of the Shell pool contract
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromShell(
|
||||
address pool,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
return _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(makerToken, pool),
|
||||
takerTokenData: abi.encode(takerToken, pool),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromShell
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromShell(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(address takerToken, address pool) = abi.decode(takerTokenData, (address, address));
|
||||
(address makerToken) = abi.decode(makerTokenData, (address));
|
||||
|
||||
try
|
||||
this.sampleSellsFromShell
|
||||
(pool, takerToken, makerToken, _toSingleValueArray(sellAmount))
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
return amounts[0];
|
||||
} catch (bytes memory) {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
156
packages/asset-swapper/contracts/src/SmoothySampler.sol
Normal file
156
packages/asset-swapper/contracts/src/SmoothySampler.sol
Normal file
@@ -0,0 +1,156 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
// import "./interfaces/ISmoothy.sol";
|
||||
import "./ApproximateBuys.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
import "./interfaces/ISmoothy.sol";
|
||||
|
||||
contract SmoothySampler is
|
||||
SamplerUtils,
|
||||
ApproximateBuys
|
||||
{
|
||||
/// @dev Information for sampling from smoothy sources.
|
||||
struct SmoothyInfo {
|
||||
address poolAddress;
|
||||
bytes4 sellQuoteFunctionSelector;
|
||||
bytes4 buyQuoteFunctionSelector;
|
||||
}
|
||||
|
||||
/// @dev Base gas limit for Smoothy calls.
|
||||
uint256 constant private SMOOTHY_CALL_GAS = 600e3;
|
||||
|
||||
/// @dev Sample sell quotes from Smoothy.
|
||||
/// @param smoothyInfo Smoothy information specific to this token pair.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromSmoothy(
|
||||
SmoothyInfo memory smoothyInfo,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Basically a Curve fork
|
||||
|
||||
// Smoothy only keep a percentage of its tokens available in reserve
|
||||
uint256 poolReserveMakerAmount = ISmoothy(smoothyInfo.poolAddress).getBalance(uint256(toTokenIdx)) -
|
||||
ISmoothy(smoothyInfo.poolAddress)._yBalances(uint256(toTokenIdx));
|
||||
(, , , uint256 decimals) = ISmoothy(smoothyInfo.poolAddress).getTokenStats(uint256(toTokenIdx));
|
||||
poolReserveMakerAmount = poolReserveMakerAmount/(10**(18-decimals));
|
||||
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
smoothyInfo.poolAddress.staticcall.gas(SMOOTHY_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
smoothyInfo.sellQuoteFunctionSelector,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
|
||||
// Make sure the quoted buyAmount is available in the pool reserve
|
||||
if (buyAmount >= poolReserveMakerAmount) {
|
||||
// Assign pool reserve amount for all higher samples to break early
|
||||
for (uint256 j = i; j < numSamples; j++) {
|
||||
makerTokenAmounts[j] = poolReserveMakerAmount;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Smoothy.
|
||||
/// @param smoothyInfo Smoothy information specific to this token pair.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromSmoothy(
|
||||
SmoothyInfo memory smoothyInfo,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
// Buys not supported so approximate it.
|
||||
return _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
makerTokenData: abi.encode(toTokenIdx, smoothyInfo),
|
||||
takerTokenData: abi.encode(fromTokenIdx, smoothyInfo),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromSmoothy
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromSmoothy(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory makerTokenData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
(int128 takerTokenIdx, SmoothyInfo memory smoothyInfo) =
|
||||
abi.decode(takerTokenData, (int128, SmoothyInfo));
|
||||
(int128 makerTokenIdx) =
|
||||
abi.decode(makerTokenData, (int128));
|
||||
(bool success, bytes memory resultData) =
|
||||
address(this).staticcall(abi.encodeWithSelector(
|
||||
this.sampleSellsFromSmoothy.selector,
|
||||
smoothyInfo,
|
||||
takerTokenIdx,
|
||||
makerTokenIdx,
|
||||
_toSingleValueArray(sellAmount)
|
||||
));
|
||||
if (!success) {
|
||||
return 0;
|
||||
}
|
||||
// solhint-disable-next-line indent
|
||||
return abi.decode(resultData, (uint256[]))[0];
|
||||
}
|
||||
}
|
124
packages/asset-swapper/contracts/src/TwoHopSampler.sol
Normal file
124
packages/asset-swapper/contracts/src/TwoHopSampler.sol
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
|
||||
|
||||
contract TwoHopSampler {
|
||||
using LibBytesV06 for bytes;
|
||||
|
||||
struct HopInfo {
|
||||
uint256 sourceIndex;
|
||||
bytes returnData;
|
||||
}
|
||||
|
||||
function sampleTwoHopSell(
|
||||
bytes[] memory firstHopCalls,
|
||||
bytes[] memory secondHopCalls,
|
||||
uint256 sellAmount
|
||||
)
|
||||
public
|
||||
returns (
|
||||
HopInfo memory firstHop,
|
||||
HopInfo memory secondHop,
|
||||
uint256 buyAmount
|
||||
)
|
||||
{
|
||||
uint256 intermediateAssetAmount = 0;
|
||||
for (uint256 i = 0; i != firstHopCalls.length; ++i) {
|
||||
firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, sellAmount);
|
||||
(bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]);
|
||||
if (didSucceed) {
|
||||
uint256 amount = returnData.readUint256(returnData.length - 32);
|
||||
if (amount > intermediateAssetAmount) {
|
||||
intermediateAssetAmount = amount;
|
||||
firstHop.sourceIndex = i;
|
||||
firstHop.returnData = returnData;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intermediateAssetAmount == 0) {
|
||||
return (firstHop, secondHop, buyAmount);
|
||||
}
|
||||
for (uint256 j = 0; j != secondHopCalls.length; ++j) {
|
||||
secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, intermediateAssetAmount);
|
||||
(bool didSucceed, bytes memory returnData) = address(this).call(secondHopCalls[j]);
|
||||
if (didSucceed) {
|
||||
uint256 amount = returnData.readUint256(returnData.length - 32);
|
||||
if (amount > buyAmount) {
|
||||
buyAmount = amount;
|
||||
secondHop.sourceIndex = j;
|
||||
secondHop.returnData = returnData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sampleTwoHopBuy(
|
||||
bytes[] memory firstHopCalls,
|
||||
bytes[] memory secondHopCalls,
|
||||
uint256 buyAmount
|
||||
)
|
||||
public
|
||||
returns (
|
||||
HopInfo memory firstHop,
|
||||
HopInfo memory secondHop,
|
||||
uint256 sellAmount
|
||||
)
|
||||
{
|
||||
sellAmount = uint256(-1);
|
||||
uint256 intermediateAssetAmount = uint256(-1);
|
||||
for (uint256 j = 0; j != secondHopCalls.length; ++j) {
|
||||
secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, buyAmount);
|
||||
(bool didSucceed, bytes memory returnData) = address(this).call(secondHopCalls[j]);
|
||||
if (didSucceed) {
|
||||
uint256 amount = returnData.readUint256(returnData.length - 32);
|
||||
if (
|
||||
amount > 0 &&
|
||||
amount < intermediateAssetAmount
|
||||
) {
|
||||
intermediateAssetAmount = amount;
|
||||
secondHop.sourceIndex = j;
|
||||
secondHop.returnData = returnData;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intermediateAssetAmount == uint256(-1)) {
|
||||
return (firstHop, secondHop, sellAmount);
|
||||
}
|
||||
for (uint256 i = 0; i != firstHopCalls.length; ++i) {
|
||||
firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, intermediateAssetAmount);
|
||||
(bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]);
|
||||
if (didSucceed) {
|
||||
uint256 amount = returnData.readUint256(returnData.length - 32);
|
||||
if (
|
||||
amount > 0 &&
|
||||
amount < sellAmount
|
||||
) {
|
||||
sellAmount = amount;
|
||||
firstHop.sourceIndex = i;
|
||||
firstHop.returnData = returnData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
214
packages/asset-swapper/contracts/src/UniswapSampler.sol
Normal file
214
packages/asset-swapper/contracts/src/UniswapSampler.sol
Normal file
@@ -0,0 +1,214 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IUniswapExchangeQuotes.sol";
|
||||
import "./SamplerUtils.sol";
|
||||
|
||||
|
||||
interface IUniswapExchangeFactory {
|
||||
|
||||
/// @dev Get the exchange for a token.
|
||||
/// @param tokenAddress The address of the token contract.
|
||||
function getExchange(address tokenAddress)
|
||||
external
|
||||
view
|
||||
returns (address);
|
||||
}
|
||||
|
||||
|
||||
contract UniswapSampler is
|
||||
SamplerUtils
|
||||
{
|
||||
/// @dev Gas limit for Uniswap calls.
|
||||
uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
|
||||
|
||||
/// @dev Sample sell quotes from Uniswap.
|
||||
/// @param router Address of the Uniswap Router
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswap(
|
||||
address router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
|
||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
|
||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
|
||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
bool didSucceed = true;
|
||||
if (makerToken == address(0)) {
|
||||
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(takerTokenExchange),
|
||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
} else if (takerToken == address(0)) {
|
||||
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(makerTokenExchange),
|
||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
} else {
|
||||
uint256 ethBought;
|
||||
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(takerTokenExchange),
|
||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||
takerTokenAmounts[i]
|
||||
);
|
||||
if (ethBought != 0) {
|
||||
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(makerTokenExchange),
|
||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||
ethBought
|
||||
);
|
||||
} else {
|
||||
makerTokenAmounts[i] = 0;
|
||||
}
|
||||
}
|
||||
// Break early if amounts are 0
|
||||
if (!didSucceed || makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Uniswap.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswap(
|
||||
address router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
|
||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
|
||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
|
||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
bool didSucceed = true;
|
||||
if (makerToken == address(0)) {
|
||||
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(takerTokenExchange),
|
||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||
makerTokenAmounts[i]
|
||||
);
|
||||
} else if (takerToken == address(0)) {
|
||||
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(makerTokenExchange),
|
||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||
makerTokenAmounts[i]
|
||||
);
|
||||
} else {
|
||||
uint256 ethSold;
|
||||
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(makerTokenExchange),
|
||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||
makerTokenAmounts[i]
|
||||
);
|
||||
if (ethSold != 0) {
|
||||
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||
address(takerTokenExchange),
|
||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||
ethSold
|
||||
);
|
||||
} else {
|
||||
takerTokenAmounts[i] = 0;
|
||||
}
|
||||
}
|
||||
// Break early if amounts are 0
|
||||
if (!didSucceed || takerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gracefully calls a Uniswap pricing function.
|
||||
/// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
|
||||
/// @param functionSelector Selector of the target function.
|
||||
/// @param inputAmount Quantity parameter particular to the pricing function.
|
||||
/// @return outputAmount The returned amount from the function call. Will be
|
||||
/// zero if the call fails or if `uniswapExchangeAddress` is zero.
|
||||
function _callUniswapExchangePriceFunction(
|
||||
address uniswapExchangeAddress,
|
||||
bytes4 functionSelector,
|
||||
uint256 inputAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 outputAmount, bool didSucceed)
|
||||
{
|
||||
if (uniswapExchangeAddress == address(0)) {
|
||||
return (outputAmount, didSucceed);
|
||||
}
|
||||
bytes memory resultData;
|
||||
(didSucceed, resultData) =
|
||||
uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
functionSelector,
|
||||
inputAmount
|
||||
));
|
||||
if (didSucceed) {
|
||||
outputAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Retrive an existing Uniswap exchange contract.
|
||||
/// Throws if the exchange does not exist.
|
||||
/// @param router Address of the Uniswap router.
|
||||
/// @param tokenAddress Address of the token contract.
|
||||
/// @return exchange `IUniswapExchangeQuotes` for the token.
|
||||
function _getUniswapExchange(address router, address tokenAddress)
|
||||
private
|
||||
view
|
||||
returns (IUniswapExchangeQuotes exchange)
|
||||
{
|
||||
exchange = IUniswapExchangeQuotes(
|
||||
address(IUniswapExchangeFactory(router)
|
||||
.getExchange(tokenAddress))
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
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.
|
||||
@@ -20,25 +20,21 @@
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBancorV3.sol";
|
||||
import "./interfaces/IUniswapV2Router01.sol";
|
||||
|
||||
|
||||
contract BancorV3Sampler
|
||||
contract UniswapV2Sampler
|
||||
{
|
||||
/// @dev Gas limit for BancorV3 calls.
|
||||
uint256 constant private BancorV3_CALL_GAS = 150e3; // 150k
|
||||
/// @dev Gas limit for UniswapV2 calls.
|
||||
uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
|
||||
|
||||
address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
/// @dev Sample sell quotes from BancorV3.
|
||||
/// @param weth The WETH contract address
|
||||
/// @dev Sample sell quotes from UniswapV2.
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromBancorV3(
|
||||
address weth,
|
||||
function sampleSellsFromUniswapV2(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
@@ -49,20 +45,14 @@ contract BancorV3Sampler
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
if(path[0] == weth){
|
||||
path[0] = ETH;
|
||||
}
|
||||
if(path[1] == weth){
|
||||
path[1] = ETH;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IBancorV3(router).tradeOutputBySourceAmount(path[0], path[1], takerTokenAmounts[i])
|
||||
returns (uint256 amount)
|
||||
IUniswapV2Router01(router).getAmountsOut
|
||||
{gas: UNISWAPV2_CALL_GAS}
|
||||
(takerTokenAmounts[i], path)
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
makerTokenAmounts[i] = amount;
|
||||
makerTokenAmounts[i] = amounts[path.length - 1];
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
@@ -74,15 +64,13 @@ contract BancorV3Sampler
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from BancorV3.
|
||||
/// @param weth The WETH contract address
|
||||
/// @dev Sample buy quotes from UniswapV2.
|
||||
/// @param router Router to look up tokens and amounts
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromBancorV3(
|
||||
address weth,
|
||||
function sampleBuysFromUniswapV2(
|
||||
address router,
|
||||
address[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
@@ -93,20 +81,14 @@ contract BancorV3Sampler
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
if(path[0] == weth){
|
||||
path[0] = ETH;
|
||||
}
|
||||
if(path[1] == weth){
|
||||
path[1] = ETH;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
try
|
||||
IBancorV3(router).tradeInputByTargetAmount(path[0], path[1], makerTokenAmounts[i])
|
||||
returns (uint256 amount)
|
||||
IUniswapV2Router01(router).getAmountsIn
|
||||
{gas: UNISWAPV2_CALL_GAS}
|
||||
(makerTokenAmounts[i], path)
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
takerTokenAmounts[i] = amount;
|
||||
takerTokenAmounts[i] = amounts[0];
|
||||
// Break early if there are 0 amounts
|
||||
if (takerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
@@ -117,4 +99,4 @@ contract BancorV3Sampler
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
363
packages/asset-swapper/contracts/src/UniswapV3Sampler.sol
Normal file
363
packages/asset-swapper/contracts/src/UniswapV3Sampler.sol
Normal file
@@ -0,0 +1,363 @@
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
interface IUniswapV3QuoterV2 {
|
||||
function factory()
|
||||
external
|
||||
view
|
||||
returns (IUniswapV3Factory factory);
|
||||
|
||||
// @notice Returns the amount out received for a given exact input swap without executing the swap
|
||||
// @param path The path of the swap, i.e. each token pair and the pool fee
|
||||
// @param amountIn The amount of the first token to swap
|
||||
// @return amountOut The amount of the last token that would be received
|
||||
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
|
||||
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
|
||||
// @return gasEstimate The estimate of the gas that the swap consumes
|
||||
function quoteExactInput(bytes memory path, uint256 amountIn)
|
||||
external
|
||||
returns (
|
||||
uint256 amountOut,
|
||||
uint160[] memory sqrtPriceX96AfterList,
|
||||
uint32[] memory initializedTicksCrossedList,
|
||||
uint256 gasEstimate
|
||||
);
|
||||
|
||||
// @notice Returns the amount in required for a given exact output swap without executing the swap
|
||||
// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
|
||||
// @param amountOut The amount of the last token to receive
|
||||
// @return amountIn The amount of first token required to be paid
|
||||
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
|
||||
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
|
||||
// @return gasEstimate The estimate of the gas that the swap consumes
|
||||
function quoteExactOutput(bytes memory path, uint256 amountOut)
|
||||
external
|
||||
returns (
|
||||
uint256 amountIn,
|
||||
uint160[] memory sqrtPriceX96AfterList,
|
||||
uint32[] memory initializedTicksCrossedList,
|
||||
uint256 gasEstimate
|
||||
);
|
||||
}
|
||||
|
||||
interface IUniswapV3Factory {
|
||||
function getPool(IERC20TokenV06 a, IERC20TokenV06 b, uint24 fee)
|
||||
external
|
||||
view
|
||||
returns (IUniswapV3Pool pool);
|
||||
}
|
||||
|
||||
interface IUniswapV3Pool {
|
||||
function token0() external view returns (IERC20TokenV06);
|
||||
function token1() external view returns (IERC20TokenV06);
|
||||
function fee() external view returns (uint24);
|
||||
}
|
||||
|
||||
contract UniswapV3Sampler
|
||||
{
|
||||
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
|
||||
uint256 constant private QUOTE_GAS = 700e3;
|
||||
|
||||
/// @dev Sample sell quotes from UniswapV3.
|
||||
/// @param quoter UniswapV3 Quoter contract.
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return uniswapPaths The encoded uniswap path for each sample.
|
||||
/// @return uniswapGasUsed Estimated amount of gas used
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswapV3(
|
||||
IUniswapV3QuoterV2 quoter,
|
||||
IERC20TokenV06[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (
|
||||
bytes[] memory uniswapPaths,
|
||||
uint256[] memory uniswapGasUsed,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
{
|
||||
IUniswapV3Pool[][] memory poolPaths =
|
||||
_getValidPoolPaths(quoter.factory(), path, 0);
|
||||
|
||||
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
|
||||
uniswapPaths = new bytes[](takerTokenAmounts.length);
|
||||
uniswapGasUsed = new uint256[](takerTokenAmounts.length);
|
||||
|
||||
for (uint256 i = 0; i < takerTokenAmounts.length; ++i) {
|
||||
// Pick the best result from all the paths.
|
||||
uint256 topBuyAmount = 0;
|
||||
for (uint256 j = 0; j < poolPaths.length; ++j) {
|
||||
bytes memory uniswapPath = _toUniswapPath(path, poolPaths[j]);
|
||||
try quoter.quoteExactInput
|
||||
{ gas: QUOTE_GAS }
|
||||
(uniswapPath, takerTokenAmounts[i])
|
||||
returns (
|
||||
uint256 buyAmount,
|
||||
uint160[] memory, /* sqrtPriceX96AfterList */
|
||||
uint32[] memory, /* initializedTicksCrossedList */
|
||||
uint256 gasUsed
|
||||
)
|
||||
{
|
||||
if (topBuyAmount <= buyAmount) {
|
||||
topBuyAmount = buyAmount;
|
||||
uniswapPaths[i] = uniswapPath;
|
||||
uniswapGasUsed[i] = gasUsed;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// Break early if we can't complete the sells.
|
||||
if (topBuyAmount == 0) {
|
||||
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
|
||||
// then reset if no valid valid quote was found
|
||||
uniswapPaths[i] = "";
|
||||
uniswapGasUsed[i] = 0;
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = topBuyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from UniswapV3.
|
||||
/// @param quoter UniswapV3 Quoter contract.
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return uniswapPaths The encoded uniswap path for each sample.
|
||||
/// @return uniswapGasUsed Estimated amount of gas used
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswapV3(
|
||||
IUniswapV3QuoterV2 quoter,
|
||||
IERC20TokenV06[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (
|
||||
bytes[] memory uniswapPaths,
|
||||
uint256[] memory uniswapGasUsed,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
{
|
||||
IUniswapV3Pool[][] memory poolPaths =
|
||||
_getValidPoolPaths(quoter.factory(), path, 0);
|
||||
IERC20TokenV06[] memory reversedPath = _reverseTokenPath(path);
|
||||
|
||||
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||
uniswapPaths = new bytes[](makerTokenAmounts.length);
|
||||
uniswapGasUsed = new uint256[](makerTokenAmounts.length);
|
||||
|
||||
for (uint256 i = 0; i < makerTokenAmounts.length; ++i) {
|
||||
// Pick the best result from all the paths.
|
||||
uint256 topSellAmount = 0;
|
||||
for (uint256 j = 0; j < poolPaths.length; ++j) {
|
||||
// quoter requires path to be reversed for buys.
|
||||
bytes memory uniswapPath = _toUniswapPath(
|
||||
reversedPath,
|
||||
_reversePoolPath(poolPaths[j])
|
||||
);
|
||||
try
|
||||
quoter.quoteExactOutput
|
||||
{ gas: QUOTE_GAS }
|
||||
(uniswapPath, makerTokenAmounts[i])
|
||||
returns (
|
||||
uint256 sellAmount,
|
||||
uint160[] memory, /* sqrtPriceX96AfterList */
|
||||
uint32[] memory, /* initializedTicksCrossedList */
|
||||
uint256 gasUsed
|
||||
)
|
||||
{
|
||||
if (topSellAmount == 0 || topSellAmount >= sellAmount) {
|
||||
topSellAmount = sellAmount;
|
||||
// But the output path should still be encoded for sells.
|
||||
uniswapPaths[i] = _toUniswapPath(path, poolPaths[j]);
|
||||
uniswapGasUsed[i] = gasUsed;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// Break early if we can't complete the buys.
|
||||
if (topSellAmount == 0) {
|
||||
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
|
||||
// then reset if no valid valid quote was found
|
||||
uniswapPaths[i] = "";
|
||||
uniswapGasUsed[i] = 0;
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = topSellAmount;
|
||||
}
|
||||
}
|
||||
|
||||
function _getValidPoolPaths(
|
||||
IUniswapV3Factory factory,
|
||||
IERC20TokenV06[] memory tokenPath,
|
||||
uint256 startIndex
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (IUniswapV3Pool[][] memory poolPaths)
|
||||
{
|
||||
require(
|
||||
tokenPath.length - startIndex >= 2,
|
||||
"UniswapV3Sampler/tokenPath too short"
|
||||
);
|
||||
uint24[4] memory validPoolFees = [
|
||||
// The launch pool fees. Could get hairier if they add more.
|
||||
uint24(0.0001e6),
|
||||
uint24(0.0005e6),
|
||||
uint24(0.003e6),
|
||||
uint24(0.01e6)
|
||||
];
|
||||
IUniswapV3Pool[] memory validPools =
|
||||
new IUniswapV3Pool[](validPoolFees.length);
|
||||
uint256 numValidPools = 0;
|
||||
{
|
||||
IERC20TokenV06 inputToken = tokenPath[startIndex];
|
||||
IERC20TokenV06 outputToken = tokenPath[startIndex + 1];
|
||||
for (uint256 i = 0; i < validPoolFees.length; ++i) {
|
||||
IUniswapV3Pool pool =
|
||||
factory.getPool(inputToken, outputToken, validPoolFees[i]);
|
||||
if (_isValidPool(pool)) {
|
||||
validPools[numValidPools++] = pool;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (numValidPools == 0) {
|
||||
// No valid pools for this hop.
|
||||
return poolPaths;
|
||||
}
|
||||
if (startIndex + 2 == tokenPath.length) {
|
||||
// End of path.
|
||||
poolPaths = new IUniswapV3Pool[][](numValidPools);
|
||||
for (uint256 i = 0; i < numValidPools; ++i) {
|
||||
poolPaths[i] = new IUniswapV3Pool[](1);
|
||||
poolPaths[i][0] = validPools[i];
|
||||
}
|
||||
return poolPaths;
|
||||
}
|
||||
// Get paths for subsequent hops.
|
||||
IUniswapV3Pool[][] memory subsequentPoolPaths =
|
||||
_getValidPoolPaths(factory, tokenPath, startIndex + 1);
|
||||
if (subsequentPoolPaths.length == 0) {
|
||||
// Could not complete the path.
|
||||
return poolPaths;
|
||||
}
|
||||
// Combine our pools with the next hop paths.
|
||||
poolPaths = new IUniswapV3Pool[][](
|
||||
numValidPools * subsequentPoolPaths.length
|
||||
);
|
||||
for (uint256 i = 0; i < numValidPools; ++i) {
|
||||
for (uint256 j = 0; j < subsequentPoolPaths.length; ++j) {
|
||||
uint256 o = i * subsequentPoolPaths.length + j;
|
||||
// Prepend pool to the subsequent path.
|
||||
poolPaths[o] =
|
||||
new IUniswapV3Pool[](1 + subsequentPoolPaths[j].length);
|
||||
poolPaths[o][0] = validPools[i];
|
||||
for (uint256 k = 0; k < subsequentPoolPaths[j].length; ++k) {
|
||||
poolPaths[o][1 + k] = subsequentPoolPaths[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
return poolPaths;
|
||||
}
|
||||
|
||||
function _reverseTokenPath(IERC20TokenV06[] memory tokenPath)
|
||||
private
|
||||
pure
|
||||
returns (IERC20TokenV06[] memory reversed)
|
||||
{
|
||||
reversed = new IERC20TokenV06[](tokenPath.length);
|
||||
for (uint256 i = 0; i < tokenPath.length; ++i) {
|
||||
reversed[i] = tokenPath[tokenPath.length - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
function _reversePoolPath(IUniswapV3Pool[] memory poolPath)
|
||||
private
|
||||
pure
|
||||
returns (IUniswapV3Pool[] memory reversed)
|
||||
{
|
||||
reversed = new IUniswapV3Pool[](poolPath.length);
|
||||
for (uint256 i = 0; i < poolPath.length; ++i) {
|
||||
reversed[i] = poolPath[poolPath.length - i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
function _isValidPool(IUniswapV3Pool pool)
|
||||
private
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
// Check if it has been deployed.
|
||||
{
|
||||
uint256 codeSize;
|
||||
assembly {
|
||||
codeSize := extcodesize(pool)
|
||||
}
|
||||
if (codeSize == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Must have a balance of both tokens.
|
||||
if (pool.token0().balanceOf(address(pool)) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (pool.token1().balanceOf(address(pool)) == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function _toUniswapPath(
|
||||
IERC20TokenV06[] memory tokenPath,
|
||||
IUniswapV3Pool[] memory poolPath
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (bytes memory uniswapPath)
|
||||
{
|
||||
require(
|
||||
tokenPath.length >= 2 && tokenPath.length == poolPath.length + 1,
|
||||
"UniswapV3Sampler/invalid path lengths"
|
||||
);
|
||||
// Uniswap paths are tightly packed as:
|
||||
// [token0, token0token1PairFee, token1, token1Token2PairFee, token2, ...]
|
||||
uniswapPath = new bytes(tokenPath.length * 20 + poolPath.length * 3);
|
||||
uint256 o;
|
||||
assembly { o := add(uniswapPath, 32) }
|
||||
for (uint256 i = 0; i < tokenPath.length; ++i) {
|
||||
if (i > 0) {
|
||||
uint24 poolFee = poolPath[i - 1].fee();
|
||||
assembly {
|
||||
mstore(o, shl(232, poolFee))
|
||||
o := add(o, 3)
|
||||
}
|
||||
}
|
||||
IERC20TokenV06 token = tokenPath[i];
|
||||
assembly {
|
||||
mstore(o, shl(96, token))
|
||||
o := add(o, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
packages/asset-swapper/contracts/src/UtilitySampler.sol
Normal file
95
packages/asset-swapper/contracts/src/UtilitySampler.sol
Normal file
@@ -0,0 +1,95 @@
|
||||
|
||||
// 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
|
||||
contract UtilitySampler {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
IERC20TokenV06 private immutable UTILITY_ETH_ADDRESS = IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||
|
||||
function getTokenDecimals(IERC20TokenV06[] memory tokens)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory decimals)
|
||||
{
|
||||
decimals = new uint256[](tokens.length);
|
||||
for (uint256 i = 0; i != tokens.length; i++) {
|
||||
decimals[i] = tokens[i] == UTILITY_ETH_ADDRESS
|
||||
? 18
|
||||
: tokens[i].compatDecimals();
|
||||
}
|
||||
}
|
||||
|
||||
function getBalanceOf(IERC20TokenV06[] memory tokens, address account)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory balances)
|
||||
{
|
||||
balances = new uint256[](tokens.length);
|
||||
for (uint256 i = 0; i != tokens.length; i++) {
|
||||
balances[i] = tokens[i] == UTILITY_ETH_ADDRESS
|
||||
? account.balance
|
||||
: tokens[i].compatBalanceOf(account);
|
||||
}
|
||||
}
|
||||
|
||||
function getAllowanceOf(IERC20TokenV06[] memory tokens, address account, address spender)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory allowances)
|
||||
{
|
||||
allowances = new uint256[](tokens.length);
|
||||
for (uint256 i = 0; i != tokens.length; i++) {
|
||||
allowances[i] = tokens[i] == UTILITY_ETH_ADDRESS
|
||||
? 0
|
||||
: tokens[i].compatAllowance(account, spender);
|
||||
}
|
||||
}
|
||||
|
||||
function isContract(address account)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
uint256 size;
|
||||
assembly { size := extcodesize(account) }
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
function getGasLeft()
|
||||
public
|
||||
returns (uint256)
|
||||
{
|
||||
return gasleft();
|
||||
}
|
||||
|
||||
function getBlockNumber()
|
||||
public
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return block.number;
|
||||
}
|
||||
}
|
@@ -1,134 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 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;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import './ApproximateBuys.sol';
|
||||
import './SamplerUtils.sol';
|
||||
|
||||
struct VeloRoute {
|
||||
address from;
|
||||
address to;
|
||||
bool stable;
|
||||
}
|
||||
|
||||
interface IVelodromeRouter {
|
||||
function getAmountOut(
|
||||
uint256 amountIn,
|
||||
address tokenIn,
|
||||
address tokenOut
|
||||
) external view returns (uint256 amount, bool stable);
|
||||
|
||||
function getAmountsOut(uint256 amountIn, VeloRoute[] calldata routes)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
}
|
||||
|
||||
contract VelodromeSampler is SamplerUtils, ApproximateBuys {
|
||||
/// @dev Sample sell quotes from Velodrome
|
||||
/// @param router Address of Velodrome router.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order).
|
||||
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
|
||||
function sampleSellsFromVelodrome(
|
||||
IVelodromeRouter router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
) public view returns (bool stable, uint256[] memory makerTokenAmounts) {
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// Sampling should not mix stable and volatile pools.
|
||||
// Find the most liquid pool based on max(takerTokenAmounts) and stick with it.
|
||||
stable = _isMostLiquidPoolStablePool(router, takerToken, makerToken, takerTokenAmounts);
|
||||
VeloRoute[] memory routes = new VeloRoute[](1);
|
||||
routes[0] = VeloRoute({ from: takerToken, to: makerToken, stable: stable });
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
makerTokenAmounts[i] = router.getAmountsOut(takerTokenAmounts[i], routes)[1];
|
||||
// Break early if there are 0 amounts
|
||||
if (makerTokenAmounts[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample buy quotes from Velodrome.
|
||||
/// @param router Address of Velodrome router.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||
function sampleBuysFromVelodrome(
|
||||
IVelodromeRouter router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
) public view returns (bool stable, uint256[] memory takerTokenAmounts) {
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
|
||||
// Sampling should not mix stable and volatile pools.
|
||||
// Find the most liquid pool based on the reverse swap (maker -> taker) and stick with it.
|
||||
stable = _isMostLiquidPoolStablePool(router, makerToken, takerToken, makerTokenAmounts);
|
||||
|
||||
takerTokenAmounts = _sampleApproximateBuys(
|
||||
ApproximateBuyQuoteOpts({
|
||||
takerTokenData: abi.encode(router, VeloRoute({ from: takerToken, to: makerToken, stable: stable })),
|
||||
makerTokenData: abi.encode(router, VeloRoute({ from: makerToken, to: takerToken, stable: stable })),
|
||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromVelodrome
|
||||
}),
|
||||
makerTokenAmounts
|
||||
);
|
||||
}
|
||||
|
||||
function _sampleSellForApproximateBuyFromVelodrome(
|
||||
bytes memory takerTokenData,
|
||||
bytes memory, /* makerTokenData */
|
||||
uint256 sellAmount
|
||||
) internal view returns (uint256) {
|
||||
(IVelodromeRouter router, VeloRoute memory route) = abi.decode(takerTokenData, (IVelodromeRouter, VeloRoute));
|
||||
|
||||
VeloRoute[] memory routes = new VeloRoute[](1);
|
||||
routes[0] = route;
|
||||
return router.getAmountsOut(sellAmount, routes)[1];
|
||||
}
|
||||
|
||||
/// @dev Returns whether the most liquid pool is a stable pool.
|
||||
/// @param router Address of Velodrome router.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token buy amount for each sample (sorted in ascending order)
|
||||
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||
function _isMostLiquidPoolStablePool(
|
||||
IVelodromeRouter router,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
) internal view returns (bool stable) {
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
(, stable) = router.getAmountOut(takerTokenAmounts[numSamples - 1], takerToken, makerToken);
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IBalancer {
|
||||
function isBound(address t) external view returns (bool);
|
||||
function getDenormalizedWeight(address token) external view returns (uint256);
|
||||
function getBalance(address token) external view returns (uint256);
|
||||
function getSwapFee() external view returns (uint256);
|
||||
function calcOutGivenIn(
|
||||
uint256 tokenBalanceIn,
|
||||
uint256 tokenWeightIn,
|
||||
uint256 tokenBalanceOut,
|
||||
uint256 tokenWeightOut,
|
||||
uint256 tokenAmountIn,
|
||||
uint256 swapFee
|
||||
) external pure returns (uint256 tokenAmountOut);
|
||||
function calcInGivenOut(
|
||||
uint256 tokenBalanceIn,
|
||||
uint256 tokenWeightIn,
|
||||
uint256 tokenBalanceOut,
|
||||
uint256 tokenWeightOut,
|
||||
uint256 tokenAmountOut,
|
||||
uint256 swapFee
|
||||
) external pure returns (uint256 tokenAmountIn);
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
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.
|
||||
@@ -18,26 +18,16 @@
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
interface IBancorV3 {
|
||||
|
||||
/**
|
||||
* @dev returns the output amount when trading by providing the source amount
|
||||
*/
|
||||
function tradeOutputBySourceAmount(
|
||||
address sourceToken,
|
||||
address targetToken,
|
||||
uint256 sourceAmount
|
||||
) external view returns (uint256);
|
||||
interface IBancor {}
|
||||
|
||||
/**
|
||||
* @dev returns the input amount when trading by providing the target amount
|
||||
*/
|
||||
function tradeInputByTargetAmount(
|
||||
address sourceToken,
|
||||
address targetToken,
|
||||
uint256 targetAmount
|
||||
) external view returns (uint256);
|
||||
interface IBancorNetwork {
|
||||
function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory);
|
||||
function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256);
|
||||
}
|
||||
|
||||
}
|
||||
interface IBancorRegistry {
|
||||
function getAddress(bytes32 _contractName) external view returns (address);
|
||||
function BANCOR_NETWORK() external view returns (bytes32);
|
||||
}
|
72
packages/asset-swapper/contracts/src/interfaces/ICurve.sol
Normal file
72
packages/asset-swapper/contracts/src/interfaces/ICurve.sol
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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;
|
||||
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
interface ICurve {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dy);
|
||||
|
||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param buyAmount The amount of token being bought.
|
||||
function get_dx_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dx);
|
||||
|
||||
/// @dev Get the underlying token address from the token index
|
||||
/// @param i The token index.
|
||||
function underlying_coins(
|
||||
int128 i
|
||||
)
|
||||
external
|
||||
returns (address tokenAddress);
|
||||
}
|
33
packages/asset-swapper/contracts/src/interfaces/IMStable.sol
Normal file
33
packages/asset-swapper/contracts/src/interfaces/IMStable.sol
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IMStable {
|
||||
|
||||
function getSwapOutput(
|
||||
address _input,
|
||||
address _output,
|
||||
uint256 _quantity
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 swapOutput);
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IMooniswapRegistry {
|
||||
|
||||
function pools(address token1, address token2) external view returns(address);
|
||||
}
|
||||
|
||||
interface IMooniswap {
|
||||
|
||||
function getReturn(
|
||||
address fromToken,
|
||||
address destToken,
|
||||
uint256 amount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns(uint256 returnAmount);
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IMultiBridge {
|
||||
|
||||
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
|
||||
/// @param tokenAddress The address of the ERC20 token to transfer.
|
||||
/// @param from Address to transfer asset from.
|
||||
/// @param to Address to transfer asset to.
|
||||
/// @param amount Amount of asset to transfer.
|
||||
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
|
||||
/// @return success The magic bytes `0xdc1600f3` if successful.
|
||||
function bridgeTransferFrom(
|
||||
address tokenAddress,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success);
|
||||
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount);
|
||||
}
|
43
packages/asset-swapper/contracts/src/interfaces/IShell.sol
Normal file
43
packages/asset-swapper/contracts/src/interfaces/IShell.sol
Normal file
@@ -0,0 +1,43 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IShell {
|
||||
|
||||
function viewOriginSwap (
|
||||
address from,
|
||||
address to,
|
||||
uint256 fromAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 toAmount);
|
||||
|
||||
function viewTargetSwap (
|
||||
address from,
|
||||
address to,
|
||||
uint256 toAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 fromAmount);
|
||||
}
|
||||
|
45
packages/asset-swapper/contracts/src/interfaces/ISmoothy.sol
Normal file
45
packages/asset-swapper/contracts/src/interfaces/ISmoothy.sol
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface ISmoothy {
|
||||
|
||||
function getBalance (
|
||||
uint256 tid
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance);
|
||||
|
||||
function _yBalances (
|
||||
uint256 tid
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 balance);
|
||||
|
||||
function getTokenStats (
|
||||
uint256 tid
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 softWeight, uint256 hardWeight, uint256 balance, uint256 decimals);
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IUniswapExchangeQuotes {
|
||||
|
||||
function getEthToTokenInputPrice(
|
||||
uint256 ethSold
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 tokensBought);
|
||||
|
||||
function getEthToTokenOutputPrice(
|
||||
uint256 tokensBought
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 ethSold);
|
||||
|
||||
function getTokenToEthInputPrice(
|
||||
uint256 tokensSold
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 ethBought);
|
||||
|
||||
function getTokenToEthOutputPrice(
|
||||
uint256 ethBought
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 tokensSold);
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
// 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;
|
||||
|
||||
|
||||
interface IUniswapV2Router01 {
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts);
|
||||
}
|
135
packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol
Normal file
135
packages/asset-swapper/contracts/test/TestNativeOrderSampler.sol
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/NativeOrderSampler.sol";
|
||||
import "../src/UtilitySampler.sol";
|
||||
|
||||
|
||||
contract TestNativeOrderSamplerToken {
|
||||
mapping (address => uint256) public balanceOf;
|
||||
mapping (address => mapping(address => uint256)) public allowance;
|
||||
|
||||
function setBalanceAndAllowance(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 balance,
|
||||
uint256 allowance_
|
||||
)
|
||||
external
|
||||
{
|
||||
balanceOf[owner] = balance;
|
||||
allowance[owner][spender] = allowance_;
|
||||
}
|
||||
}
|
||||
|
||||
contract TestNativeOrderSampler is
|
||||
NativeOrderSampler,
|
||||
UtilitySampler
|
||||
{
|
||||
uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1;
|
||||
bytes32 private constant VALID_SIGNATURE_HASH = bytes32(hex"01");
|
||||
|
||||
function createTokens(uint256 count)
|
||||
external
|
||||
returns (TestNativeOrderSamplerToken[] memory tokens)
|
||||
{
|
||||
tokens = new TestNativeOrderSamplerToken[](count);
|
||||
for (uint256 i = 0; i < count; ++i) {
|
||||
tokens[i] = new TestNativeOrderSamplerToken();
|
||||
}
|
||||
}
|
||||
|
||||
function setTokenBalanceAndAllowance(
|
||||
TestNativeOrderSamplerToken token,
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 balance,
|
||||
uint256 allowance
|
||||
)
|
||||
external
|
||||
{
|
||||
token.setBalanceAndAllowance(owner, spender, balance, allowance);
|
||||
}
|
||||
|
||||
// IExchange.getLimitOrderRelevantState()
|
||||
function getLimitOrderRelevantState(
|
||||
IExchange.LimitOrder memory order,
|
||||
IExchange.Signature calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
IExchange.OrderInfo memory orderInfo,
|
||||
uint128 actualFillableTakerTokenAmount,
|
||||
bool isSignatureValid
|
||||
)
|
||||
{
|
||||
// The order salt determines everything.
|
||||
orderInfo.orderHash = keccak256(abi.encode(order.salt));
|
||||
if (uint8(order.salt) == 0xFF) {
|
||||
orderInfo.status = IExchange.OrderStatus.FILLED;
|
||||
} else {
|
||||
orderInfo.status = IExchange.OrderStatus.FILLABLE;
|
||||
}
|
||||
|
||||
isSignatureValid = signature.r == VALID_SIGNATURE_HASH;
|
||||
|
||||
// The expiration time is the filled taker asset amount.
|
||||
orderInfo.takerTokenFilledAmount = uint128(order.expiry);
|
||||
|
||||
// Calculate how much is fillable in maker terms given the filled taker amount
|
||||
uint256 fillableMakerTokenAmount = LibMathV06.getPartialAmountFloor(
|
||||
uint256(
|
||||
order.takerAmount
|
||||
- orderInfo.takerTokenFilledAmount
|
||||
),
|
||||
uint256(order.takerAmount),
|
||||
uint256(order.makerAmount)
|
||||
);
|
||||
|
||||
// Take the min of the balance/allowance and the fillable maker amount
|
||||
fillableMakerTokenAmount = LibSafeMathV06.min256(
|
||||
fillableMakerTokenAmount,
|
||||
_getSpendableERC20BalanceOf(order.makerToken, order.maker)
|
||||
);
|
||||
|
||||
// Convert to taker terms
|
||||
actualFillableTakerTokenAmount = LibMathV06.getPartialAmountCeil(
|
||||
fillableMakerTokenAmount,
|
||||
uint256(order.makerAmount),
|
||||
uint256(order.takerAmount)
|
||||
).safeDowncastToUint128();
|
||||
}
|
||||
|
||||
function _getSpendableERC20BalanceOf(
|
||||
IERC20TokenV06 token,
|
||||
address owner
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
return LibSafeMathV06.min256(
|
||||
token.allowance(owner, address(this)),
|
||||
token.balanceOf(owner)
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/asset-swapper",
|
||||
"version": "16.62.1",
|
||||
"version": "16.60.1",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -33,26 +33,13 @@
|
||||
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
|
||||
"contracts:gen": "contracts-gen generate",
|
||||
"contracts:copy": "contracts-gen copy",
|
||||
<<<<<<< HEAD
|
||||
"publish:private": "yarn build && gitpkg publish"
|
||||
=======
|
||||
"publish:private": "yarn build && gitpkg publish",
|
||||
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))",
|
||||
"list:deps": "yarn lerna list -l"
|
||||
>>>>>>> 9b131199a (add weth/eth wrap/unwrap support for bancorv3)
|
||||
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "BalanceChecker,FakeTaker",
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
"abis": "./test/generated-artifacts/@(BalanceChecker|FakeTaker).json",
|
||||
=======
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SmoothySampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
|
||||
>>>>>>> f5c486050 (added bancor mixin/sampler and started linking up with asset-swapper)
|
||||
=======
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SmoothySampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json",
|
||||
>>>>>>> 1cc59ab1a (feat: Add Velodrome support [TKR-432] (#494))
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SmoothySampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
|
||||
"postpublish": {
|
||||
"assets": []
|
||||
}
|
||||
@@ -73,14 +60,14 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.34",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/contract-addresses": "^6.16.0",
|
||||
"@0x/contract-wrappers": "^13.20.4",
|
||||
"@0x/contracts-erc20": "^3.3.32",
|
||||
"@0x/contracts-zero-ex": "^0.35.0",
|
||||
"@0x/contract-addresses": "^6.14.0",
|
||||
"@0x/contract-wrappers": "^13.20.2",
|
||||
"@0x/contracts-erc20": "^3.3.30",
|
||||
"@0x/contracts-zero-ex": "^0.33.0",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/neon-router": "^0.3.5",
|
||||
"@0x/protocol-utils": "^11.15.0",
|
||||
"@0x/protocol-utils": "^11.13.0",
|
||||
"@0x/quote-server": "^6.0.6",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
@@ -93,15 +80,9 @@
|
||||
"@ethersproject/contracts": "^5.0.1",
|
||||
"@ethersproject/providers": "^5.0.4",
|
||||
"@ethersproject/strings": "^5.0.10",
|
||||
<<<<<<< HEAD
|
||||
"@open-rpc/client-js": "^1.7.1",
|
||||
"axios": "^0.24.0",
|
||||
"axios-mock-adapter": "^1.20.0",
|
||||
=======
|
||||
"axios": "^0.21.1",
|
||||
"axios-mock-adapter": "^1.19.0",
|
||||
"balancer-labs-sor-v1": "npm:@balancer-labs/sor@0.3.2",
|
||||
>>>>>>> 4057bdab9 (Publish)
|
||||
"cream-sor": "^0.3.3",
|
||||
"decimal.js": "^10.2.0",
|
||||
"ethereum-types": "^3.7.0",
|
||||
@@ -118,9 +99,10 @@
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.23",
|
||||
"@0x/contracts-utils": "^4.8.13",
|
||||
"@0x/contracts-test-utils": "^5.4.21",
|
||||
"@0x/contracts-utils": "^4.8.11",
|
||||
"@0x/mesh-rpc-client": "^9.4.2",
|
||||
"@0x/migrations": "^8.1.19",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
|
@@ -6,8 +6,10 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as BalanceChecker from '../generated-artifacts/BalanceChecker.json';
|
||||
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as FakeTaker from '../generated-artifacts/FakeTaker.json';
|
||||
export const artifacts = {
|
||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||
BalanceChecker: BalanceChecker as ContractArtifact,
|
||||
FakeTaker: FakeTaker as ContractArtifact,
|
||||
};
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { ChainId } from '@0x/contract-addresses';
|
||||
import { SignatureType } from '@0x/protocol-utils';
|
||||
import { BigNumber, logUtils } from '@0x/utils';
|
||||
|
||||
@@ -10,9 +11,12 @@ import {
|
||||
RfqRequestOpts,
|
||||
SwapQuoteGetOutputOpts,
|
||||
SwapQuoteRequestOpts,
|
||||
SwapQuoterOpts,
|
||||
} from './types';
|
||||
import {
|
||||
DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||
} from './utils/market_operation_utils/constants';
|
||||
|
||||
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
|
||||
@@ -24,7 +28,6 @@ const ONE_SECOND_MS = 1000;
|
||||
const ONE_MINUTE_SECS = 60;
|
||||
const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS;
|
||||
const DEFAULT_PER_PAGE = 1000;
|
||||
const ZERO_AMOUNT = new BigNumber(0);
|
||||
const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180;
|
||||
|
||||
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
|
||||
@@ -39,6 +42,21 @@ 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;
|
||||
|
||||
export const ZERO_AMOUNT = new BigNumber(0);
|
||||
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
||||
chainId: ChainId.Mainnet,
|
||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||
...DEFAULT_ORDER_PRUNER_OPTS,
|
||||
samplerGasLimit: 500e6,
|
||||
ethGasStationUrl: ETH_GAS_STATION_API_URL,
|
||||
rfqt: {
|
||||
integratorsWhitelist: [],
|
||||
makerAssetOfferings: {},
|
||||
txOriginBlacklist: new Set(),
|
||||
},
|
||||
tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID[ChainId.Mainnet],
|
||||
};
|
||||
|
||||
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
|
||||
isFromETH: false,
|
||||
isToETH: false,
|
||||
@@ -73,6 +91,8 @@ export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) =>
|
||||
const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
export const INVALID_SIGNATURE = { signatureType: SignatureType.Invalid, v: 1, r: EMPTY_BYTES32, s: EMPTY_BYTES32 };
|
||||
|
||||
export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_operation_utils/constants';
|
||||
|
||||
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
|
||||
|
||||
// tslint:disable-next-line: custom-no-magic-numbers
|
||||
@@ -91,6 +111,8 @@ export const constants = {
|
||||
ONE_AMOUNT: new BigNumber(1),
|
||||
ONE_SECOND_MS,
|
||||
ONE_MINUTE_MS,
|
||||
DEFAULT_SWAP_QUOTER_OPTS,
|
||||
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID,
|
||||
DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||
DEFAULT_EXCHANGE_PROXY_SWAP_QUOTE_GET_OPTS,
|
||||
DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS,
|
||||
|
@@ -109,7 +109,6 @@ export {
|
||||
SwapQuoteGetOutputOpts,
|
||||
SwapQuoteInfo,
|
||||
SwapQuoteOrdersBreakdown,
|
||||
SwapQuoteMultiHopBreakdown,
|
||||
SwapQuoteRequestOpts,
|
||||
SwapQuoterError,
|
||||
SwapQuoterOpts,
|
||||
@@ -118,8 +117,6 @@ export {
|
||||
} from './types';
|
||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||
export {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
IRfqClient,
|
||||
RfqClientV1Price,
|
||||
RfqClientV1PriceRequest,
|
||||
@@ -131,29 +128,45 @@ export {
|
||||
export {
|
||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||
DEFAULT_GAS_SCHEDULE,
|
||||
>>>>>>> a7f23a982 (feat: add IRfqClient (#467))
|
||||
SOURCE_FLAGS,
|
||||
BUY_SOURCE_FILTER_BY_CHAIN_ID,
|
||||
SELL_SOURCE_FILTER_BY_CHAIN_ID,
|
||||
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
||||
} from './utils/market_operation_utils/constants';
|
||||
export {
|
||||
Parameters,
|
||||
SamplerContractCall,
|
||||
SamplerContractOperation,
|
||||
} from './utils/market_operation_utils/sampler_contract_operation';
|
||||
export {
|
||||
BalancerFillData,
|
||||
BancorFillData,
|
||||
CollapsedFill,
|
||||
CurveFillData,
|
||||
CurveFunctionSelectors,
|
||||
CurveInfo,
|
||||
DexSample,
|
||||
DODOFillData,
|
||||
ERC20BridgeSource,
|
||||
ExchangeProxyOverhead,
|
||||
FeeSchedule,
|
||||
Fill,
|
||||
FillData,
|
||||
GetMarketOrdersRfqOpts,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
LiquidityProviderFillData,
|
||||
>>>>>>> 2d16f83e3 (Offboard/clean up Oasis, CoFix, and legacy Kyber [TKR-405] (#482))
|
||||
LiquidityProviderRegistry,
|
||||
MarketDepth,
|
||||
MarketDepthSide,
|
||||
MooniswapFillData,
|
||||
MultiHopFillData,
|
||||
NativeCollapsedFill,
|
||||
NativeRfqOrderFillData,
|
||||
NativeLimitOrderFillData,
|
||||
NativeFillData,
|
||||
OptimizedMarketOrder,
|
||||
SourceQuoteOperation,
|
||||
TokenAdjacencyGraph,
|
||||
UniswapV2FillData,
|
||||
} from './utils/market_operation_utils/types';
|
||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||
export {
|
||||
@@ -172,7 +185,7 @@ export {
|
||||
PriceComparisonsReport,
|
||||
} from './utils/quote_report_generator';
|
||||
export { QuoteRequestor, V4RFQIndicativeQuoteMM } from './utils/quote_requestor';
|
||||
export { BalanceCheckerContract, FakeTakerContract } from './wrappers';
|
||||
export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers';
|
||||
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
|
||||
export type Native = ERC20BridgeSource.Native;
|
||||
export type MultiHop = ERC20BridgeSource.MultiHop;
|
||||
|
57
packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts
Normal file
57
packages/asset-swapper/src/noop_samplers/AaveV2Sampler.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { ZERO_AMOUNT } from '../constants';
|
||||
export interface AaveInfo {
|
||||
lendingPool: string;
|
||||
aToken: string;
|
||||
underlyingToken: string;
|
||||
}
|
||||
// tslint:disable-next-line:no-unnecessary-class
|
||||
export class AaveV2Sampler {
|
||||
public static sampleSellsFromAaveV2(
|
||||
aaveInfo: AaveInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> aToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
|
||||
) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = takerTokenAmounts.length;
|
||||
|
||||
const makerTokenAmounts = new Array(numSamples);
|
||||
makerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
public static sampleBuysFromAaveV2(
|
||||
aaveInfo: AaveInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
makerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> aToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
|
||||
) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = makerTokenAmounts.length;
|
||||
const takerTokenAmounts = new Array(numSamples);
|
||||
takerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
}
|
57
packages/asset-swapper/src/noop_samplers/GeistSampler.ts
Normal file
57
packages/asset-swapper/src/noop_samplers/GeistSampler.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { ZERO_AMOUNT } from '../constants';
|
||||
export interface GeistInfo {
|
||||
lendingPool: string;
|
||||
gToken: string;
|
||||
underlyingToken: string;
|
||||
}
|
||||
// tslint:disable-next-line:no-unnecessary-class
|
||||
export class GeistSampler {
|
||||
public static sampleSellsFromGeist(
|
||||
geistInfo: GeistInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> gToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
|
||||
) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = takerTokenAmounts.length;
|
||||
|
||||
const makerTokenAmounts = new Array(numSamples);
|
||||
makerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
public static sampleBuysFromGeist(
|
||||
geistInfo: GeistInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
makerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> gToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
|
||||
) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = makerTokenAmounts.length;
|
||||
const takerTokenAmounts = new Array(numSamples);
|
||||
takerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
}
|
@@ -12,40 +12,33 @@ import {
|
||||
FillQuoteTransformerSide,
|
||||
findTransformerNonce,
|
||||
} from '@0x/protocol-utils';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../constants';
|
||||
import {
|
||||
Address,
|
||||
AffiliateFeeType,
|
||||
Bytes,
|
||||
CalldataInfo,
|
||||
ExchangeProxyContractOpts,
|
||||
MarketBuySwapQuote,
|
||||
MarketOperation,
|
||||
MarketSellSwapQuote,
|
||||
SwapQuote,
|
||||
SwapQuoteConsumerBase,
|
||||
SwapQuoteConsumerOpts,
|
||||
SwapQuoteExecutionOpts,
|
||||
SwapQuoteGetOutputOpts,
|
||||
SwapQuoteLiquidityProviderBridgeOrder,
|
||||
SwapQuoteUniswapV2BridgeOrder,
|
||||
SwapQuoteUniswapV3BridgeOrder,
|
||||
SwapQuoteCurveBridgeOrder,
|
||||
SwapQuoteMooniswapBridgeOrder,
|
||||
SwapQuoteHop,
|
||||
SwapQuoteGenericBridgeOrder,
|
||||
SwapQuoteOrder,
|
||||
} from '../types';
|
||||
import { valueByChainId } from '../utils/utils';
|
||||
import { assert } from '../utils/assert';
|
||||
import {
|
||||
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
|
||||
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
|
||||
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
||||
} from '../utils/market_operation_utils/constants';
|
||||
import { poolEncoder } from '../utils/market_operation_utils/orders';
|
||||
import {
|
||||
CurveFillData,
|
||||
ERC20BridgeSource,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
FinalUniswapV3FillData,
|
||||
LiquidityProviderFillData,
|
||||
MooniswapFillData,
|
||||
@@ -53,7 +46,6 @@ import {
|
||||
OptimizedMarketBridgeOrder,
|
||||
OptimizedMarketOrder,
|
||||
UniswapV2FillData,
|
||||
>>>>>>> 955ad4971 (add real VIP support for eligible RFQT swaps (#458))
|
||||
} from '../utils/market_operation_utils/types';
|
||||
|
||||
import {
|
||||
@@ -62,7 +54,6 @@ import {
|
||||
MultiplexSubcall,
|
||||
multiplexTransformERC20Encoder,
|
||||
multiplexUniswapEncoder,
|
||||
multiplexBatchSellEncoder,
|
||||
} from './multiplex_encoders';
|
||||
import {
|
||||
getFQTTransformerDataFromOptimizedOrders,
|
||||
@@ -84,32 +75,16 @@ const PANCAKE_SWAP_FORKS = [
|
||||
ERC20BridgeSource.BakerySwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.CafeSwap,
|
||||
ERC20BridgeSource.CheeseSwap,
|
||||
ERC20BridgeSource.JulSwap,
|
||||
];
|
||||
|
||||
const FAKE_PROVIDER: any = {
|
||||
sendAsync(): void {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0x561b94454b65614ae3db0897b74303f4acf7cc75',
|
||||
[ChainId.Ropsten]: '0xae241c6fc7f28f6dc0cb58b4112ba7f63fcaf5e2',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
|
||||
[ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
|
||||
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
public readonly chainId: ChainId;
|
||||
public readonly transformerNonces: {
|
||||
@@ -122,8 +97,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
|
||||
private readonly _exchangeProxy: IZeroExContract;
|
||||
|
||||
constructor(public readonly contractAddresses: ContractAddresses, options: SwapQuoteConsumerOpts) {
|
||||
const { chainId } = options;
|
||||
constructor(public readonly contractAddresses: ContractAddresses, options: Partial<SwapQuoteConsumerOpts> = {}) {
|
||||
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
||||
assert.isNumber('chainId', chainId);
|
||||
this.chainId = chainId;
|
||||
this.contractAddresses = contractAddresses;
|
||||
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
|
||||
@@ -177,14 +153,15 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
ethAmount = ethAmount.plus(sellAmount);
|
||||
}
|
||||
|
||||
const slippedOrders = slipNonNativeOrders(quote);
|
||||
|
||||
// VIP routes.
|
||||
if (
|
||||
this.chainId === ChainId.Mainnet &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap])
|
||||
) {
|
||||
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV2BridgeOrder;
|
||||
const { source } = order;
|
||||
const { fillData } = order;
|
||||
const source = slippedOrders[0].source;
|
||||
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
|
||||
return {
|
||||
calldataHexString: this._exchangeProxy
|
||||
.sellToUniswap(
|
||||
@@ -213,20 +190,19 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
this.chainId === ChainId.Mainnet &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.UniswapV3])
|
||||
) {
|
||||
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV3BridgeOrder;
|
||||
const { fillData } = order;
|
||||
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<FinalUniswapV3FillData>).fillData;
|
||||
let _calldataHexString;
|
||||
if (isFromETH) {
|
||||
_calldataHexString = this._exchangeProxy
|
||||
.sellEthForTokenToUniswapV3(fillData.encodedPath, minBuyAmount, NULL_ADDRESS)
|
||||
.sellEthForTokenToUniswapV3(fillData.uniswapPath, minBuyAmount, NULL_ADDRESS)
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (isToETH) {
|
||||
_calldataHexString = this._exchangeProxy
|
||||
.sellTokenForEthToUniswapV3(fillData.encodedPath, sellAmount, minBuyAmount, NULL_ADDRESS)
|
||||
.sellTokenForEthToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS)
|
||||
.getABIEncodedTransactionData();
|
||||
} else {
|
||||
_calldataHexString = this._exchangeProxy
|
||||
.sellTokenForTokenToUniswapV3(fillData.encodedPath, sellAmount, minBuyAmount, NULL_ADDRESS)
|
||||
.sellTokenForTokenToUniswapV3(fillData.uniswapPath, sellAmount, minBuyAmount, NULL_ADDRESS)
|
||||
.getABIEncodedTransactionData();
|
||||
}
|
||||
return {
|
||||
@@ -246,11 +222,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
ERC20BridgeSource.BakerySwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.CafeSwap,
|
||||
ERC20BridgeSource.CheeseSwap,
|
||||
ERC20BridgeSource.JulSwap,
|
||||
])
|
||||
) {
|
||||
const order = quote.hops[0].orders[0] as SwapQuoteUniswapV2BridgeOrder;
|
||||
const { source, fillData } = order;
|
||||
const source = slippedOrders[0].source;
|
||||
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
|
||||
return {
|
||||
calldataHexString: this._exchangeProxy
|
||||
.sellToPancakeSwap(
|
||||
@@ -279,13 +257,14 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
[ChainId.Mainnet, ChainId.BSC].includes(this.chainId) &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.LiquidityProvider])
|
||||
) {
|
||||
const { fillData } = quote.hops[0].orders[0] as SwapQuoteLiquidityProviderBridgeOrder;
|
||||
const fillData = (slippedOrders[0] as OptimizedMarketBridgeOrder<LiquidityProviderFillData>).fillData;
|
||||
const target = fillData.poolAddress;
|
||||
return {
|
||||
calldataHexString: this._exchangeProxy
|
||||
.sellToLiquidityProvider(
|
||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
||||
fillData.poolAddress,
|
||||
target,
|
||||
NULL_ADDRESS,
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
@@ -307,7 +286,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
// ETH buy/sell is supported
|
||||
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
|
||||
) {
|
||||
const { fillData } = quote.hops[0].orders[0] as SwapQuoteCurveBridgeOrder;
|
||||
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
|
||||
return {
|
||||
calldataHexString: this._exchangeProxy
|
||||
.sellToLiquidityProvider(
|
||||
@@ -318,8 +297,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
encodeCurveLiquidityProviderData({
|
||||
curveAddress: fillData.poolAddress,
|
||||
exchangeFunctionSelector: fillData.exchangeFunctionSelector,
|
||||
curveAddress: fillData.pool.poolAddress,
|
||||
exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector,
|
||||
fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
|
||||
toCoinIdx: new BigNumber(fillData.toTokenIdx),
|
||||
}),
|
||||
@@ -336,7 +315,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
this.chainId === ChainId.Mainnet &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
|
||||
) {
|
||||
const { fillData } = quote.hops[0].orders[0] as SwapQuoteMooniswapBridgeOrder;
|
||||
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
|
||||
return {
|
||||
calldataHexString: this._exchangeProxy
|
||||
.sellToLiquidityProvider(
|
||||
@@ -346,7 +325,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
NULL_ADDRESS,
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
encodeAddress(fillData.poolAddress),
|
||||
poolEncoder.encode([fillData.poolAddress]),
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
||||
@@ -402,7 +381,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
|
||||
return {
|
||||
calldataHexString: this._encodeMultiplexBatchFillCalldata(
|
||||
quote.hops[0],
|
||||
{ ...quote, orders: slippedOrders },
|
||||
optsWithDefaults,
|
||||
),
|
||||
ethAmount,
|
||||
@@ -411,13 +390,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
gasOverhead: ZERO_AMOUNT,
|
||||
};
|
||||
}
|
||||
|
||||
// Sort hops so they always flow taker -> maker
|
||||
const orderedHops = isBuyQuote(quote) ? quote.hops.slice().reverse() : quote.hops;
|
||||
if (this.chainId === ChainId.Mainnet && isMultiplexMultiHopFillCompatible(quote, optsWithDefaults)) {
|
||||
return {
|
||||
calldataHexString: this._encodeMultiplexMultiHopFillCalldata(
|
||||
orderedHops,
|
||||
{ ...quote, orders: slippedOrders },
|
||||
optsWithDefaults,
|
||||
),
|
||||
ethAmount,
|
||||
@@ -441,26 +417,45 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
});
|
||||
}
|
||||
|
||||
for (const [i, hop] of orderedHops.entries()) {
|
||||
let fillAmount = !isBuyQuote(quote)
|
||||
? shouldSellEntireBalance ? MAX_UINT256 : hop.takerAmount
|
||||
: hop.makerAmount;
|
||||
let side = !isBuyQuote(quote) ? FillQuoteTransformerSide.Sell : FillQuoteTransformerSide.Buy;
|
||||
if (orderedHops.length > 1) { // Multi-hop.
|
||||
// Multi-hop is always a sell.
|
||||
side = FillQuoteTransformerSide.Sell;
|
||||
// Subsequent multi-hops always sell entire balance.
|
||||
fillAmount = i > 0 ? MAX_UINT256 : hop.takerAmount;
|
||||
}
|
||||
// If it's two hop we have an intermediate token this is needed to encode the individual FQT
|
||||
// and we also want to ensure no dust amount is left in the flash wallet
|
||||
const intermediateToken = quote.isTwoHop ? slippedOrders[0].makerToken : NULL_ADDRESS;
|
||||
// This transformer will fill the quote.
|
||||
if (quote.isTwoHop) {
|
||||
const [firstHopOrder, secondHopOrder] = slippedOrders;
|
||||
transforms.push({
|
||||
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
|
||||
data: encodeFillQuoteTransformerData({
|
||||
side,
|
||||
fillAmount,
|
||||
sellToken: hop.takerToken,
|
||||
buyToken: hop.makerToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders(hop.orders),
|
||||
side: FillQuoteTransformerSide.Sell,
|
||||
sellToken,
|
||||
buyToken: intermediateToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders([firstHopOrder]),
|
||||
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||
fillAmount: shouldSellEntireBalance ? MAX_UINT256 : firstHopOrder.takerAmount,
|
||||
}),
|
||||
});
|
||||
transforms.push({
|
||||
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
|
||||
data: encodeFillQuoteTransformerData({
|
||||
side: FillQuoteTransformerSide.Sell,
|
||||
buyToken,
|
||||
sellToken: intermediateToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders([secondHopOrder]),
|
||||
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||
fillAmount: MAX_UINT256,
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
const fillAmount = isBuyQuote(quote) ? quote.makerTokenFillAmount : quote.takerTokenFillAmount;
|
||||
transforms.push({
|
||||
deploymentNonce: this.transformerNonces.fillQuoteTransformer,
|
||||
data: encodeFillQuoteTransformerData({
|
||||
side: isBuyQuote(quote) ? FillQuoteTransformerSide.Buy : FillQuoteTransformerSide.Sell,
|
||||
sellToken,
|
||||
buyToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders(slippedOrders),
|
||||
refundReceiver: refundReceiver || NULL_ADDRESS,
|
||||
fillAmount: !isBuyQuote(quote) && shouldSellEntireBalance ? MAX_UINT256 : fillAmount,
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -526,6 +521,10 @@ 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) {
|
||||
@@ -567,108 +566,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
throw new Error('Execution not supported for Exchange Proxy quotes');
|
||||
}
|
||||
|
||||
private _encodeMultiplexBatchFillCalldata(hop: SwapQuoteHop, opts: ExchangeProxyContractOpts): string {
|
||||
const subcalls = this._getMultiplexBatchSellSubcalls(hop.orders);
|
||||
if (opts.isFromETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexBatchSellEthForToken(hop.makerToken, subcalls, hop.minMakerAmount)
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (opts.isToETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexBatchSellTokenForEth(
|
||||
hop.takerToken,
|
||||
subcalls,
|
||||
hop.maxTakerAmount,
|
||||
hop.minMakerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
} else {
|
||||
return this._exchangeProxy
|
||||
.multiplexBatchSellTokenForToken(
|
||||
hop.takerToken,
|
||||
hop.makerToken,
|
||||
subcalls,
|
||||
hop.maxTakerAmount,
|
||||
hop.minMakerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
}
|
||||
}
|
||||
|
||||
private _encodeMultiplexMultiHopFillCalldata(hops: SwapQuoteHop[], opts: ExchangeProxyContractOpts): string {
|
||||
private _encodeMultiplexBatchFillCalldata(quote: SwapQuote, opts: ExchangeProxyContractOpts): string {
|
||||
const subcalls = [];
|
||||
for (const hop of hops) {
|
||||
if (hop.orders.length !== 1) {
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.BatchSell,
|
||||
data: multiplexBatchSellEncoder.encode({ subcalls: this._getMultiplexBatchSellSubcalls(hop.orders) }),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const order = hop.orders[0] as SwapQuoteGenericBridgeOrder;
|
||||
switch (order.source) {
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.UniswapV2,
|
||||
data: multiplexUniswapEncoder.encode({
|
||||
tokens: (order as SwapQuoteUniswapV2BridgeOrder).fillData.tokenAddressPath,
|
||||
isSushi: order.source === ERC20BridgeSource.SushiSwap,
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.LiquidityProvider,
|
||||
data: multiplexPlpEncoder.encode({
|
||||
provider: (order as SwapQuoteLiquidityProviderBridgeOrder).fillData.poolAddress,
|
||||
auxiliaryData: NULL_BYTES,
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case ERC20BridgeSource.UniswapV3:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.UniswapV3,
|
||||
data: (order as SwapQuoteUniswapV3BridgeOrder).fillData.encodedPath,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
// Should never happen because we check `isMultiplexMultiHopFillCompatible`
|
||||
// before calling this function.
|
||||
throw new Error(`Multiplex multi-hop unsupported source: ${order.source}`);
|
||||
}
|
||||
}
|
||||
const tokenPath = getTokenPathFromHops(hops);
|
||||
const firstHop = hops[0];
|
||||
const lastHop = hops[hops.length - 1];
|
||||
if (opts.isFromETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellEthForToken(tokenPath, subcalls, lastHop.minMakerAmount)
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (opts.isToETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellTokenForEth(
|
||||
tokenPath,
|
||||
subcalls,
|
||||
firstHop.maxTakerAmount,
|
||||
lastHop.minMakerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
} else {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellTokenForToken(
|
||||
tokenPath,
|
||||
subcalls,
|
||||
firstHop.maxTakerAmount,
|
||||
lastHop.minMakerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
}
|
||||
}
|
||||
|
||||
private _getMultiplexBatchSellSubcalls(orders: SwapQuoteOrder[]): any[] {
|
||||
const subcalls = [];
|
||||
for_loop: for (const [i, order] of orders.entries()) {
|
||||
for_loop: for (const [i, order] of quote.orders.entries()) {
|
||||
switch_statement: switch (order.source) {
|
||||
case ERC20BridgeSource.Native:
|
||||
if (order.type !== FillQuoteTransformerOrderType.Rfq) {
|
||||
@@ -689,9 +589,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.UniswapV2,
|
||||
sellAmount: (order as SwapQuoteUniswapV2BridgeOrder).maxTakerAmount,
|
||||
sellAmount: order.takerAmount,
|
||||
data: multiplexUniswapEncoder.encode({
|
||||
tokens: (order as SwapQuoteUniswapV2BridgeOrder).fillData.tokenAddressPath,
|
||||
tokens: (order.fillData as UniswapV2FillData).tokenAddressPath,
|
||||
isSushi: order.source === ERC20BridgeSource.SushiSwap,
|
||||
}),
|
||||
});
|
||||
@@ -699,46 +599,43 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.LiquidityProvider,
|
||||
sellAmount: (order as SwapQuoteLiquidityProviderBridgeOrder).maxTakerAmount,
|
||||
sellAmount: order.takerAmount,
|
||||
data: multiplexPlpEncoder.encode({
|
||||
provider: (order as SwapQuoteLiquidityProviderBridgeOrder).fillData.poolAddress,
|
||||
provider: (order.fillData as LiquidityProviderFillData).poolAddress,
|
||||
auxiliaryData: NULL_BYTES,
|
||||
}),
|
||||
});
|
||||
break switch_statement;
|
||||
case ERC20BridgeSource.UniswapV3:
|
||||
const fillData = (order as OptimizedMarketBridgeOrder<FinalUniswapV3FillData>).fillData;
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.UniswapV3,
|
||||
sellAmount: (order as SwapQuoteUniswapV3BridgeOrder).maxTakerAmount,
|
||||
data: (order as SwapQuoteUniswapV3BridgeOrder).fillData.encodedPath,
|
||||
sellAmount: order.takerAmount,
|
||||
data: fillData.uniswapPath,
|
||||
});
|
||||
break switch_statement;
|
||||
default:
|
||||
const fqtData = encodeFillQuoteTransformerData({
|
||||
side: FillQuoteTransformerSide.Sell,
|
||||
sellToken: order.takerToken,
|
||||
buyToken: order.makerToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders(orders.slice(i)),
|
||||
sellToken: quote.takerToken,
|
||||
buyToken: quote.makerToken,
|
||||
...getFQTTransformerDataFromOptimizedOrders(quote.orders.slice(i)),
|
||||
refundReceiver: NULL_ADDRESS,
|
||||
fillAmount: MAX_UINT256,
|
||||
});
|
||||
const transformations = [
|
||||
{ deploymentNonce: this.transformerNonces.fillQuoteTransformer, data: fqtData },
|
||||
// TODO(lawrence): needed?
|
||||
// {
|
||||
// deploymentNonce: this.transformerNonces.payTakerTransformer,
|
||||
// data: encodePayTakerTransformerData({
|
||||
// tokens: [hop.takerToken],
|
||||
// amounts: [],
|
||||
// }),
|
||||
// },
|
||||
{
|
||||
deploymentNonce: this.transformerNonces.payTakerTransformer,
|
||||
data: encodePayTakerTransformerData({
|
||||
tokens: [quote.takerToken],
|
||||
amounts: [],
|
||||
}),
|
||||
},
|
||||
];
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.TransformERC20,
|
||||
sellAmount: BigNumber.sum(
|
||||
...orders.slice(i)
|
||||
.map(o => (o as SwapQuoteGenericBridgeOrder).maxTakerAmount),
|
||||
),
|
||||
sellAmount: BigNumber.sum(...quote.orders.slice(i).map(o => o.takerAmount)),
|
||||
data: multiplexTransformERC20Encoder.encode({
|
||||
transformations,
|
||||
}),
|
||||
@@ -746,21 +643,123 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
break for_loop;
|
||||
}
|
||||
}
|
||||
return subcalls;
|
||||
}
|
||||
}
|
||||
|
||||
function getTokenPathFromHops(hops: SwapQuoteHop[]): Address[] {
|
||||
const path = [];
|
||||
for (const [i, hop] of hops.entries()) {
|
||||
path.push(hop.takerToken);
|
||||
if (i === hops.length - 1) {
|
||||
path.push(hop.makerToken);
|
||||
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 subcalls = [];
|
||||
const [firstHopOrder, secondHopOrder] = quote.orders;
|
||||
const intermediateToken = firstHopOrder.makerToken;
|
||||
const tokens = [quote.takerToken, intermediateToken, quote.makerToken];
|
||||
|
||||
for (const order of [firstHopOrder, secondHopOrder]) {
|
||||
switch (order.source) {
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
subcalls.push({
|
||||
id: MultiplexSubcall.UniswapV2,
|
||||
data: multiplexUniswapEncoder.encode({
|
||||
tokens: (order.fillData as UniswapV2FillData).tokenAddressPath,
|
||||
isSushi: order.source === ERC20BridgeSource.SushiSwap,
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case ERC20BridgeSource.LiquidityProvider:
|
||||
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:
|
||||
// Should never happen because we check `isMultiplexMultiHopFillCompatible`
|
||||
// before calling this function.
|
||||
throw new Error(`Multiplex multi-hop unsupported source: ${order.source}`);
|
||||
}
|
||||
}
|
||||
if (opts.isFromETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellEthForToken(tokens, subcalls, quote.worstCaseQuoteInfo.makerAmount)
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (opts.isToETH) {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellTokenForEth(
|
||||
tokens,
|
||||
subcalls,
|
||||
quote.worstCaseQuoteInfo.totalTakerAmount,
|
||||
quote.worstCaseQuoteInfo.makerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
} else {
|
||||
return this._exchangeProxy
|
||||
.multiplexMultiHopSellTokenForToken(
|
||||
tokens,
|
||||
subcalls,
|
||||
quote.worstCaseQuoteInfo.totalTakerAmount,
|
||||
quote.worstCaseQuoteInfo.makerAmount,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function encodeAddress(address: Address): Bytes {
|
||||
return hexUtils.leftPad(hexUtils.slice(address, 0, 20));
|
||||
function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] {
|
||||
const slippage = getMaxQuoteSlippageRate(quote);
|
||||
if (slippage === 0) {
|
||||
return quote.orders;
|
||||
}
|
||||
return quote.orders.map(o => {
|
||||
if (o.source === ERC20BridgeSource.Native) {
|
||||
return o;
|
||||
}
|
||||
return {
|
||||
...o,
|
||||
...(quote.type === MarketOperation.Sell
|
||||
? {
|
||||
makerAmount: o.makerAmount.eq(MAX_UINT256)
|
||||
? MAX_UINT256
|
||||
: o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN),
|
||||
}
|
||||
: {
|
||||
takerAmount: o.takerAmount.eq(MAX_UINT256)
|
||||
? MAX_UINT256
|
||||
: o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getMaxQuoteSlippageRate(quote: MarketBuySwapQuote | MarketSellSwapQuote): number {
|
||||
return quote.worstCaseQuoteInfo.slippage;
|
||||
}
|
||||
|
@@ -12,7 +12,6 @@ export enum MultiplexSubcall {
|
||||
BatchSell,
|
||||
MultiHopSell,
|
||||
}
|
||||
|
||||
export const multiplexTransformERC20Encoder = AbiEncoder.create([
|
||||
{
|
||||
name: 'transformations',
|
||||
@@ -23,30 +22,15 @@ export const multiplexTransformERC20Encoder = AbiEncoder.create([
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
export const multiplexRfqEncoder = AbiEncoder.create([
|
||||
{ name: 'order', type: 'tuple', components: RfqOrder.STRUCT_ABI },
|
||||
{ name: 'signature', type: 'tuple', components: SIGNATURE_ABI },
|
||||
]);
|
||||
|
||||
export const multiplexUniswapEncoder = AbiEncoder.create([
|
||||
{ name: 'tokens', type: 'address[]' },
|
||||
{ name: 'isSushi', type: 'bool' },
|
||||
]);
|
||||
|
||||
export const multiplexPlpEncoder = AbiEncoder.create([
|
||||
{ name: 'provider', type: 'address' },
|
||||
{ name: 'auxiliaryData', type: 'bytes' },
|
||||
]);
|
||||
|
||||
export const multiplexBatchSellEncoder = AbiEncoder.create([
|
||||
{
|
||||
name: 'subcalls',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'id', type: 'uint8' },
|
||||
{ name: 'sellAmount', type: 'uint256' },
|
||||
{ name: 'data', type: 'bytes' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
@@ -2,17 +2,17 @@ import { FillQuoteTransformerData, FillQuoteTransformerOrderType } from '@0x/pro
|
||||
|
||||
import { ExchangeProxyContractOpts, MarketBuySwapQuote, MarketOperation, SwapQuote } from '../types';
|
||||
import {
|
||||
createBridgeDataForBridgeOrder,
|
||||
getErc20BridgeSourceToBridgeSource,
|
||||
} from '../utils/market_operation_utils/orders';
|
||||
import {
|
||||
ERC20BridgeSource,
|
||||
NativeLimitOrderFillData,
|
||||
NativeRfqOrderFillData,
|
||||
OptimizedMarketBridgeOrder,
|
||||
OptimizedMarketOrder,
|
||||
OptimizedMarketOrderBase,
|
||||
} from '../utils/market_operation_utils/types';
|
||||
import {
|
||||
SwapQuoteGenericBridgeOrder,
|
||||
SwapQuoteOrder,
|
||||
SwapQuoteLimitOrder,
|
||||
SwapQuoteRfqOrder,
|
||||
} from '../types';
|
||||
|
||||
const MULTIPLEX_BATCH_FILL_SOURCES = [
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
@@ -29,19 +29,16 @@ export function isMultiplexBatchFillCompatible(quote: SwapQuote, opts: ExchangeP
|
||||
if (requiresTransformERC20(opts)) {
|
||||
return false;
|
||||
}
|
||||
// Must not be multi-hop.
|
||||
if (quote.hops.length > 1) {
|
||||
if (quote.isTwoHop) {
|
||||
return false;
|
||||
}
|
||||
// Must not contain limit orders.
|
||||
const allOrderTypes = quote.hops.map(h => h.orders.map(o => o.type)).flat(2);
|
||||
if (allOrderTypes.includes(FillQuoteTransformerOrderType.Limit)) {
|
||||
if (quote.orders.map(o => o.type).includes(FillQuoteTransformerOrderType.Limit)) {
|
||||
return false;
|
||||
}
|
||||
// Use Multiplex if the non-fallback sources are a subset of
|
||||
// {UniswapV2, Sushiswap, RFQ, PLP, UniswapV3}
|
||||
const nonFallbackSources = quote.hops.map(h => h.orders.filter(o => !o.isFallback).map(o => o.source)).flat(2);
|
||||
return nonFallbackSources.every(s => MULTIPLEX_BATCH_FILL_SOURCES.includes(s));
|
||||
const nonFallbackSources = Object.keys(quote.sourceBreakdown);
|
||||
return nonFallbackSources.every(source => MULTIPLEX_BATCH_FILL_SOURCES.includes(source as ERC20BridgeSource));
|
||||
}
|
||||
|
||||
const MULTIPLEX_MULTIHOP_FILL_SOURCES = [
|
||||
@@ -58,12 +55,14 @@ export function isMultiplexMultiHopFillCompatible(quote: SwapQuote, opts: Exchan
|
||||
if (requiresTransformERC20(opts)) {
|
||||
return false;
|
||||
}
|
||||
// Must be multi-hop.
|
||||
if (quote.hops.length < 2) {
|
||||
if (!quote.isTwoHop) {
|
||||
return false;
|
||||
}
|
||||
const sources = quote.hops.map(h => h.orders.map(o => o.source)).flat(2);
|
||||
return sources.every(s => MULTIPLEX_MULTIHOP_FILL_SOURCES.includes(s));
|
||||
const [firstHopOrder, secondHopOrder] = quote.orders;
|
||||
return (
|
||||
MULTIPLEX_MULTIHOP_FILL_SOURCES.includes(firstHopOrder.source) &&
|
||||
MULTIPLEX_MULTIHOP_FILL_SOURCES.includes(secondHopOrder.source)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,11 +77,11 @@ export function isDirectSwapCompatible(
|
||||
if (requiresTransformERC20(opts)) {
|
||||
return false;
|
||||
}
|
||||
// Must be a single hop with a single order.
|
||||
if (quote.hops.length !== 1 || quote.hops[0].orders.length !== 1) {
|
||||
// Must be a single order.
|
||||
if (quote.orders.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
const order = quote.hops[0].orders[0];
|
||||
const order = quote.orders[0];
|
||||
if (!directSources.includes(order.source)) {
|
||||
return false;
|
||||
}
|
||||
@@ -96,24 +95,24 @@ export function isBuyQuote(quote: SwapQuote): quote is MarketBuySwapQuote {
|
||||
return quote.type === MarketOperation.Buy;
|
||||
}
|
||||
|
||||
function isBridgeOrder(x: SwapQuoteOrder): x is SwapQuoteGenericBridgeOrder {
|
||||
function isOptimizedBridgeOrder(x: OptimizedMarketOrder): x is OptimizedMarketBridgeOrder {
|
||||
return x.type === FillQuoteTransformerOrderType.Bridge;
|
||||
}
|
||||
|
||||
// function isOptimizedLimitOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase<NativeLimitOrderFillData> {
|
||||
// return x.type === FillQuoteTransformerOrderType.Limit;
|
||||
// }
|
||||
//
|
||||
// function isOptimizedRfqOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
||||
// return x.type === FillQuoteTransformerOrderType.Rfq;
|
||||
// }
|
||||
function isOptimizedLimitOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase<NativeLimitOrderFillData> {
|
||||
return x.type === FillQuoteTransformerOrderType.Limit;
|
||||
}
|
||||
|
||||
function isOptimizedRfqOrder(x: OptimizedMarketOrder): x is OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
||||
return x.type === FillQuoteTransformerOrderType.Rfq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given `OptimizedMarketOrder`s into bridge, limit, and RFQ orders for
|
||||
* FillQuoteTransformer.
|
||||
*/
|
||||
export function getFQTTransformerDataFromOptimizedOrders(
|
||||
orders: SwapQuoteOrder[],
|
||||
orders: OptimizedMarketOrder[],
|
||||
): Pick<FillQuoteTransformerData, 'bridgeOrders' | 'limitOrders' | 'rfqOrders' | 'fillSequence'> {
|
||||
const fqtData: Pick<FillQuoteTransformerData, 'bridgeOrders' | 'limitOrders' | 'rfqOrders' | 'fillSequence'> = {
|
||||
bridgeOrders: [],
|
||||
@@ -123,25 +122,25 @@ export function getFQTTransformerDataFromOptimizedOrders(
|
||||
};
|
||||
|
||||
for (const order of orders) {
|
||||
if (isBridgeOrder(order)) {
|
||||
if (isOptimizedBridgeOrder(order)) {
|
||||
fqtData.bridgeOrders.push({
|
||||
bridgeData: order.fillData.encodedFillData,
|
||||
makerTokenAmount: order.minMakerAmount,
|
||||
takerTokenAmount: order.maxTakerAmount,
|
||||
bridgeData: createBridgeDataForBridgeOrder(order),
|
||||
makerTokenAmount: order.makerAmount,
|
||||
takerTokenAmount: order.takerAmount,
|
||||
source: getErc20BridgeSourceToBridgeSource(order.source),
|
||||
});
|
||||
// } else if (isOptimizedLimitOrder(order)) {
|
||||
// fqtData.limitOrders.push({
|
||||
// order: order.fillData.order,
|
||||
// signature: order.fillData.signature,
|
||||
// maxTakerTokenFillAmount: order.takerAmount,
|
||||
// });
|
||||
// } else if (isOptimizedRfqOrder(order)) {
|
||||
// fqtData.rfqOrders.push({
|
||||
// order: order.fillData.order,
|
||||
// signature: order.fillData.signature,
|
||||
// maxTakerTokenFillAmount: order.takerAmount,
|
||||
// });
|
||||
} else if (isOptimizedLimitOrder(order)) {
|
||||
fqtData.limitOrders.push({
|
||||
order: order.fillData.order,
|
||||
signature: order.fillData.signature,
|
||||
maxTakerTokenFillAmount: order.takerAmount,
|
||||
});
|
||||
} else if (isOptimizedRfqOrder(order)) {
|
||||
fqtData.rfqOrders.push({
|
||||
order: order.fillData.order,
|
||||
signature: order.fillData.signature,
|
||||
maxTakerTokenFillAmount: order.takerAmount,
|
||||
});
|
||||
} else {
|
||||
// Should never happen
|
||||
throw new Error('Unknown Order type');
|
||||
|
@@ -20,12 +20,13 @@ export class SwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
private readonly _contractAddresses: ContractAddresses;
|
||||
private readonly _exchangeProxyConsumer: ExchangeProxySwapQuoteConsumer;
|
||||
|
||||
public static getSwapQuoteConsumer(options: SwapQuoteConsumerOpts): SwapQuoteConsumer {
|
||||
public static getSwapQuoteConsumer(options: Partial<SwapQuoteConsumerOpts> = {}): SwapQuoteConsumer {
|
||||
return new SwapQuoteConsumer(options);
|
||||
}
|
||||
|
||||
constructor(options: SwapQuoteConsumerOpts) {
|
||||
const { chainId } = options;
|
||||
constructor(options: Partial<SwapQuoteConsumerOpts> = {}) {
|
||||
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
|
||||
assert.isNumber('chainId', chainId);
|
||||
|
||||
this.chainId = chainId;
|
||||
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
|
||||
|
@@ -1,15 +1,16 @@
|
||||
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { FillQuoteTransformerOrderType, LimitOrder } from '@0x/protocol-utils';
|
||||
import { BigNumber, providerUtils } from '@0x/utils';
|
||||
import Axios, { AxiosInstance } from 'axios';
|
||||
import { SupportedProvider, ZeroExProvider } from 'ethereum-types';
|
||||
import { BlockParamLiteral, MethodAbi, SupportedProvider, ZeroExProvider } from 'ethereum-types';
|
||||
import { FastABI } from 'fast-abi';
|
||||
import { Agent as HttpAgent } from 'http';
|
||||
import { Agent as HttpsAgent } from 'https';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { constants, INVALID_SIGNATURE, KEEP_ALIVE_TTL } from './constants';
|
||||
import {
|
||||
Address,
|
||||
AssetSwapperContractAddresses,
|
||||
MarketBuySwapQuote,
|
||||
MarketOperation,
|
||||
@@ -18,10 +19,6 @@ import {
|
||||
SignedNativeOrder,
|
||||
SwapQuote,
|
||||
SwapQuoteInfo,
|
||||
SwapQuoteHop,
|
||||
SwapQuoteOrder,
|
||||
SwapQuoteGenericBridgeOrder,
|
||||
SwapQuoteNativeOrder,
|
||||
SwapQuoteOrdersBreakdown,
|
||||
SwapQuoteRequestOpts,
|
||||
SwapQuoterOpts,
|
||||
@@ -30,26 +27,25 @@ import {
|
||||
import { assert } from './utils/assert';
|
||||
import { IRfqClient } from './utils/irfq_client';
|
||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||
import { ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
||||
import { SamplerClient } from './utils/market_operation_utils/sampler';
|
||||
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
||||
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
||||
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
||||
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
||||
import {
|
||||
ERC20BridgeSource,
|
||||
FeeSchedule,
|
||||
FillData,
|
||||
GetMarketOrdersOpts,
|
||||
MarketDepth,
|
||||
MarketDepthSide,
|
||||
MarketSideLiquidity,
|
||||
OptimizedHop,
|
||||
OptimizedOrder,
|
||||
OptimizedBridgeOrder,
|
||||
OptimizedLimitOrder,
|
||||
OptimizedRfqOrder,
|
||||
OptimizedGenericBridgeOrder,
|
||||
OptimizedMarketOrder,
|
||||
OptimizerResultWithReport,
|
||||
} from './utils/market_operation_utils/types';
|
||||
import { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||
import { QuoteRequestor } from './utils/quote_requestor';
|
||||
import { QuoteFillResult, simulateBestCaseFill, simulateWorstCaseFill } from './utils/quote_simulation';
|
||||
import { ERC20BridgeSamplerContract } from './wrappers';
|
||||
|
||||
export abstract class Orderbook {
|
||||
public abstract getOrdersAsync(
|
||||
@@ -90,15 +86,20 @@ export class SwapQuoter {
|
||||
*
|
||||
* @return An instance of SwapQuoter
|
||||
*/
|
||||
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: SwapQuoterOpts) {
|
||||
constructor(supportedProvider: SupportedProvider, orderbook: Orderbook, options: Partial<SwapQuoterOpts> = {}) {
|
||||
const {
|
||||
chainId,
|
||||
expiryBufferMs,
|
||||
permittedOrderFeeTypes,
|
||||
samplerGasLimit,
|
||||
rfqt,
|
||||
} = options;
|
||||
tokenAdjacencyGraph,
|
||||
liquidityProviderRegistry,
|
||||
} = { ...constants.DEFAULT_SWAP_QUOTER_OPTS, ...options };
|
||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||
assert.isValidOrderbook('orderbook', orderbook);
|
||||
assert.isNumber('chainId', chainId);
|
||||
assert.isNumber('expiryBufferMs', expiryBufferMs);
|
||||
this.chainId = chainId;
|
||||
this.provider = provider;
|
||||
this.orderbook = orderbook;
|
||||
@@ -113,12 +114,51 @@ export class SwapQuoter {
|
||||
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||
options.ethGasStationUrl,
|
||||
);
|
||||
// Allow the sampler bytecode to be overwritten using geths override functionality
|
||||
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
||||
// Allow address of the Sampler to be overridden, i.e in Ganache where overrides do not work
|
||||
const samplerAddress = (options.samplerOverrides && options.samplerOverrides.to) || SAMPLER_ADDRESS;
|
||||
const defaultCodeOverrides = samplerBytecode
|
||||
? {
|
||||
[samplerAddress]: { code: samplerBytecode },
|
||||
}
|
||||
: {};
|
||||
const samplerOverrides = _.assign(
|
||||
{ block: BlockParamLiteral.Latest, overrides: defaultCodeOverrides },
|
||||
options.samplerOverrides,
|
||||
);
|
||||
const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[], { BigNumber });
|
||||
const samplerContract = new ERC20BridgeSamplerContract(
|
||||
samplerAddress,
|
||||
this.provider,
|
||||
{
|
||||
gas: samplerGasLimit,
|
||||
},
|
||||
{},
|
||||
undefined,
|
||||
{
|
||||
encodeInput: (fnName: string, values: any) => fastAbi.encodeInput(fnName, values),
|
||||
decodeOutput: (fnName: string, data: string) => fastAbi.decodeOutput(fnName, data),
|
||||
},
|
||||
);
|
||||
|
||||
this._marketOperationUtils = new MarketOperationUtils(
|
||||
SamplerClient.createFromChainIdAndEndpoint(
|
||||
new DexOrderSampler(
|
||||
this.chainId,
|
||||
options.samplerServiceUrl,
|
||||
samplerContract,
|
||||
samplerOverrides,
|
||||
undefined, // pools caches for balancer and cream
|
||||
tokenAdjacencyGraph,
|
||||
liquidityProviderRegistry,
|
||||
this.chainId === ChainId.Mainnet // Enable Bancor only on Mainnet
|
||||
? async () => BancorService.createAsync(provider)
|
||||
: async () => undefined,
|
||||
),
|
||||
this._contractAddresses,
|
||||
{
|
||||
chainId,
|
||||
exchangeAddress: this._contractAddresses.exchange,
|
||||
},
|
||||
);
|
||||
|
||||
this._quoteRequestorHttpClient = Axios.create({
|
||||
@@ -177,6 +217,7 @@ export class SwapQuoter {
|
||||
MarketOperation.Buy,
|
||||
makerTokenBuyAmounts[i],
|
||||
gasPrice,
|
||||
opts.gasSchedule,
|
||||
opts.bridgeSlippage,
|
||||
);
|
||||
} else {
|
||||
@@ -203,50 +244,49 @@ export class SwapQuoter {
|
||||
takerAssetAmount: BigNumber,
|
||||
options: Partial<SwapQuoteRequestOpts> = {},
|
||||
): Promise<MarketDepth> {
|
||||
throw new Error(`Not implemented`);
|
||||
// assert.isString('makerToken', makerToken);
|
||||
// assert.isString('takerToken', takerToken);
|
||||
// const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
|
||||
//
|
||||
// let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||
// ? [[], []]
|
||||
// : await Promise.all([
|
||||
// this.orderbook.getOrdersAsync(makerToken, takerToken),
|
||||
// this.orderbook.getOrdersAsync(takerToken, makerToken),
|
||||
// ]);
|
||||
// if (!sellOrders || sellOrders.length === 0) {
|
||||
// sellOrders = [createDummyOrder(makerToken, takerToken)];
|
||||
// }
|
||||
// if (!buyOrders || buyOrders.length === 0) {
|
||||
// buyOrders = [createDummyOrder(takerToken, makerToken)];
|
||||
// }
|
||||
//
|
||||
// const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
||||
// const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
|
||||
// const { side } = marketSideLiquidity;
|
||||
//
|
||||
// return [
|
||||
// ...dexQuotes,
|
||||
// nativeOrders.map(o => {
|
||||
// return {
|
||||
// input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
|
||||
// output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
|
||||
// fillData: o,
|
||||
// source: ERC20BridgeSource.Native,
|
||||
// };
|
||||
// }),
|
||||
// ];
|
||||
// };
|
||||
// const [bids, asks] = await Promise.all([
|
||||
// this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
|
||||
// this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
|
||||
// ]);
|
||||
// return {
|
||||
// bids: getMarketDepthSide(bids),
|
||||
// asks: getMarketDepthSide(asks),
|
||||
// makerTokenDecimals: asks.makerTokenDecimals,
|
||||
// takerTokenDecimals: asks.takerTokenDecimals,
|
||||
// };
|
||||
assert.isString('makerToken', makerToken);
|
||||
assert.isString('takerToken', takerToken);
|
||||
const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
|
||||
|
||||
let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
|
||||
? [[], []]
|
||||
: await Promise.all([
|
||||
this.orderbook.getOrdersAsync(makerToken, takerToken),
|
||||
this.orderbook.getOrdersAsync(takerToken, makerToken),
|
||||
]);
|
||||
if (!sellOrders || sellOrders.length === 0) {
|
||||
sellOrders = [createDummyOrder(makerToken, takerToken)];
|
||||
}
|
||||
if (!buyOrders || buyOrders.length === 0) {
|
||||
buyOrders = [createDummyOrder(takerToken, makerToken)];
|
||||
}
|
||||
|
||||
const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
||||
const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
|
||||
const { side } = marketSideLiquidity;
|
||||
|
||||
return [
|
||||
...dexQuotes,
|
||||
nativeOrders.map(o => {
|
||||
return {
|
||||
input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
|
||||
output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
|
||||
fillData: o,
|
||||
source: ERC20BridgeSource.Native,
|
||||
};
|
||||
}),
|
||||
];
|
||||
};
|
||||
const [bids, asks] = await Promise.all([
|
||||
this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
|
||||
this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
|
||||
]);
|
||||
return {
|
||||
bids: getMarketDepthSide(bids),
|
||||
asks: getMarketDepthSide(asks),
|
||||
makerTokenDecimals: asks.makerTokenDecimals,
|
||||
takerTokenDecimals: asks.takerTokenDecimals,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,10 +361,15 @@ export class SwapQuoter {
|
||||
}
|
||||
|
||||
// ** Prepare options for fetching market side liquidity **
|
||||
// Scale fees by gas price.
|
||||
const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;
|
||||
const calcOpts: GetMarketOrdersOpts = {
|
||||
...opts,
|
||||
...cloneOpts,
|
||||
gasPrice,
|
||||
exchangeProxyOverhead: opts.exchangeProxyOverhead,
|
||||
feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData: FillData) =>
|
||||
gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)),
|
||||
),
|
||||
exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
|
||||
};
|
||||
// pass the QuoteRequestor on if rfqt enabled
|
||||
if (calcOpts.rfqt !== undefined) {
|
||||
@@ -355,13 +400,12 @@ export class SwapQuoter {
|
||||
marketOperation,
|
||||
assetFillAmount,
|
||||
gasPrice,
|
||||
opts.gasSchedule,
|
||||
opts.bridgeSlippage,
|
||||
);
|
||||
|
||||
// Use the raw gas, not scaled by gas price
|
||||
const exchangeProxyOverhead = BigNumber.sum(
|
||||
...result.hops.map(h => opts.exchangeProxyOverhead(h.sourceFlags)),
|
||||
).toNumber();
|
||||
const exchangeProxyOverhead = opts.exchangeProxyOverhead(result.sourceFlags).toNumber();
|
||||
swapQuote.bestCaseQuoteInfo.gas += exchangeProxyOverhead;
|
||||
swapQuote.worstCaseQuoteInfo.gas += exchangeProxyOverhead;
|
||||
|
||||
@@ -455,23 +499,27 @@ function createSwapQuote(
|
||||
optimizerResult: OptimizerResultWithReport,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
side: MarketOperation,
|
||||
operation: MarketOperation,
|
||||
assetFillAmount: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
gasSchedule: FeeSchedule,
|
||||
slippage: number,
|
||||
): SwapQuote {
|
||||
const {
|
||||
hops,
|
||||
optimizedOrders,
|
||||
quoteReport,
|
||||
extendedQuoteReportSources,
|
||||
sourceFlags,
|
||||
takerAmountPerEth,
|
||||
makerAmountPerEth,
|
||||
priceComparisonsReport,
|
||||
} = optimizerResult;
|
||||
const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop];
|
||||
|
||||
const quoteHops = hops.map(hop => toSwapQuoteHop(hop, side, slippage));
|
||||
const { bestCaseQuoteInfo, worstCaseQuoteInfo, sourceBreakdown } =
|
||||
calculateQuoteInfo(quoteHops, side, assetFillAmount, gasPrice, slippage);
|
||||
// Calculate quote info
|
||||
const { bestCaseQuoteInfo, worstCaseQuoteInfo, sourceBreakdown } = isTwoHop
|
||||
? calculateTwoHopQuoteInfo(optimizedOrders, operation, gasSchedule, slippage)
|
||||
: calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, gasSchedule, slippage);
|
||||
|
||||
// Put together the swap quote
|
||||
const { makerTokenDecimals, takerTokenDecimals, blockNumber } = optimizerResult.marketSideLiquidity;
|
||||
@@ -479,6 +527,7 @@ function createSwapQuote(
|
||||
makerToken,
|
||||
takerToken,
|
||||
gasPrice,
|
||||
orders: optimizedOrders,
|
||||
bestCaseQuoteInfo,
|
||||
worstCaseQuoteInfo,
|
||||
sourceBreakdown,
|
||||
@@ -488,230 +537,125 @@ function createSwapQuote(
|
||||
makerAmountPerEth,
|
||||
quoteReport,
|
||||
extendedQuoteReportSources,
|
||||
isTwoHop,
|
||||
priceComparisonsReport,
|
||||
blockNumber,
|
||||
};
|
||||
|
||||
if (side === MarketOperation.Buy) {
|
||||
if (operation === MarketOperation.Buy) {
|
||||
return {
|
||||
...swapQuote,
|
||||
type: MarketOperation.Buy,
|
||||
makerTokenFillAmount: assetFillAmount,
|
||||
maxSlippage: slippage,
|
||||
hops: quoteHops,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...swapQuote,
|
||||
type: MarketOperation.Sell,
|
||||
takerTokenFillAmount: assetFillAmount,
|
||||
maxSlippage: slippage,
|
||||
hops: quoteHops,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function toSwapQuoteHop(hop: OptimizedHop, side: MarketOperation, slippage: number): SwapQuoteHop {
|
||||
const orders = hop.orders.map(o => toSwapQuoteOrder(o, side, slippage));
|
||||
const takerAmount = side === MarketOperation.Sell ? hop.inputAmount : hop.outputAmount;
|
||||
const makerAmount = side === MarketOperation.Sell ? hop.outputAmount : hop.inputAmount;
|
||||
return {
|
||||
orders,
|
||||
makerAmount: roundMakerAmount(side, makerAmount),
|
||||
takerAmount: roundTakerAmount(side, takerAmount),
|
||||
makerToken: side === MarketOperation.Sell ? hop.outputToken : hop.inputToken,
|
||||
takerToken: side === MarketOperation.Sell ? hop.inputToken : hop.outputToken,
|
||||
minMakerAmount: slipMakerAmount(side, makerAmount, slippage),
|
||||
maxTakerAmount: slipTakerAmount(side, takerAmount, slippage),
|
||||
sourceFlags: hop.sourceFlags,
|
||||
};
|
||||
}
|
||||
|
||||
function roundMakerAmount(side: MarketOperation, makerAmount: BigNumber): BigNumber {
|
||||
const rm = side === MarketOperation.Sell ? BigNumber.ROUND_DOWN : BigNumber.ROUND_UP;
|
||||
return makerAmount.integerValue(rm);
|
||||
}
|
||||
|
||||
function roundTakerAmount(side: MarketOperation, takerAmount: BigNumber): BigNumber {
|
||||
const rm = side === MarketOperation.Sell ? BigNumber.ROUND_UP : BigNumber.ROUND_UP;
|
||||
return takerAmount.integerValue(rm);
|
||||
}
|
||||
|
||||
function slipMakerAmount(side: MarketOperation, makerAmount: BigNumber, slippage: number): BigNumber {
|
||||
return roundMakerAmount(
|
||||
side,
|
||||
side === MarketOperation.Sell ? makerAmount.times(1 - slippage) : makerAmount,
|
||||
);
|
||||
}
|
||||
|
||||
function slipTakerAmount(side: MarketOperation, takerAmount: BigNumber, slippage: number): BigNumber {
|
||||
return roundTakerAmount(
|
||||
side,
|
||||
side === MarketOperation.Sell ? takerAmount : takerAmount.times(1 + slippage),
|
||||
);
|
||||
}
|
||||
|
||||
function toSwapQuoteOrder(order: OptimizedOrder, side: MarketOperation, slippage: number): SwapQuoteGenericBridgeOrder | SwapQuoteNativeOrder {
|
||||
const { inputToken, outputToken, inputAmount, outputAmount, ...rest } = order;
|
||||
const common = {
|
||||
...rest,
|
||||
takerToken: side === MarketOperation.Sell ? inputToken : outputToken,
|
||||
makerToken: side === MarketOperation.Sell ? outputToken : inputToken,
|
||||
takerAmount: side === MarketOperation.Sell ? inputAmount : outputAmount,
|
||||
makerAmount: side === MarketOperation.Sell ? outputAmount : inputAmount,
|
||||
};
|
||||
if (isBridgeOrder(order)) {
|
||||
return {
|
||||
...common,
|
||||
minMakerAmount: slipMakerAmount(
|
||||
side,
|
||||
side === MarketOperation.Sell
|
||||
? order.outputAmount
|
||||
: order.inputAmount,
|
||||
slippage,
|
||||
),
|
||||
maxTakerAmount: slipTakerAmount(
|
||||
side,
|
||||
side === MarketOperation.Sell
|
||||
? order.inputAmount
|
||||
: order.outputAmount,
|
||||
slippage,
|
||||
),
|
||||
};
|
||||
}
|
||||
return common as SwapQuoteNativeOrder;
|
||||
}
|
||||
|
||||
function isBridgeOrder(order: OptimizedOrder): order is OptimizedGenericBridgeOrder {
|
||||
return order.type === FillQuoteTransformerOrderType.Bridge;
|
||||
}
|
||||
|
||||
function calculateQuoteInfo(
|
||||
hops: SwapQuoteHop[],
|
||||
side: MarketOperation,
|
||||
fillAmount: BigNumber,
|
||||
optimizedOrders: OptimizedMarketOrder[],
|
||||
operation: MarketOperation,
|
||||
assetFillAmount: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
gasSchedule: FeeSchedule,
|
||||
slippage: number,
|
||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
||||
const getNextFillAmount = (fillResults: QuoteFillResult[]) => {
|
||||
if (fillResults.length === 0) {
|
||||
return fillAmount;
|
||||
}
|
||||
const lastFillResult = fillResults[fillResults.length - 1];
|
||||
const { totalTakerAssetAmount, makerAssetAmount } = lastFillResult;
|
||||
return side === MarketOperation.Sell
|
||||
? makerAssetAmount : totalTakerAssetAmount;
|
||||
};
|
||||
|
||||
const bestCaseFillResults = [];
|
||||
const worstCaseFillResults = [];
|
||||
const tokenPath = [];
|
||||
for (const [i, hop] of hops.entries()) {
|
||||
if (i === 0 || i < hops.length - 1) {
|
||||
if (side == MarketOperation.Sell) {
|
||||
tokenPath.push(hop.takerToken);
|
||||
} else {
|
||||
tokenPath.unshift(hop.makerToken);
|
||||
}
|
||||
}
|
||||
if (i === tokenPath.length - 1) {
|
||||
if (side === MarketOperation.Sell) {
|
||||
tokenPath.push(hop.makerToken);
|
||||
} else {
|
||||
tokenPath.unshift(hop.takerToken);
|
||||
}
|
||||
}
|
||||
const bestCaseFillResult = simulateBestCaseFill({
|
||||
gasPrice,
|
||||
side,
|
||||
orders: hop.orders,
|
||||
fillAmount: getNextFillAmount(bestCaseFillResults),
|
||||
opts: {},
|
||||
});
|
||||
bestCaseFillResults.push(bestCaseFillResult);
|
||||
|
||||
const worstCaseFillResult = simulateWorstCaseFill({
|
||||
gasPrice,
|
||||
side,
|
||||
orders: hop.orders,
|
||||
fillAmount: getNextFillAmount(worstCaseFillResults),
|
||||
opts: { slippage },
|
||||
});
|
||||
worstCaseFillResults.push(worstCaseFillResult);
|
||||
}
|
||||
|
||||
const combinedBestCaseFillResult = combineQuoteFillResults(side, bestCaseFillResults);
|
||||
const combinedWorstCaseFillResult = combineQuoteFillResults(side, worstCaseFillResults);
|
||||
const sourceBreakdown = getSwapQuoteOrdersBreakdown(side, tokenPath, bestCaseFillResults);
|
||||
return {
|
||||
sourceBreakdown,
|
||||
bestCaseQuoteInfo: fillResultsToQuoteInfo(combinedBestCaseFillResult),
|
||||
worstCaseQuoteInfo: fillResultsToQuoteInfo(combinedWorstCaseFillResult),
|
||||
};
|
||||
}
|
||||
|
||||
function combineQuoteFillResults(side: MarketOperation, fillResults: QuoteFillResult[]): QuoteFillResult {
|
||||
if (fillResults.length === 0) {
|
||||
throw new Error(`Empty fillResults array`);
|
||||
}
|
||||
const orderedFillResults = side === MarketOperation.Sell ? fillResults : fillResults.slice().reverse();
|
||||
const lastResult = orderedFillResults[orderedFillResults.length - 1];
|
||||
const r = {
|
||||
...orderedFillResults[0],
|
||||
makerAssetAmount: lastResult.makerAssetAmount,
|
||||
totalMakerAssetAmount: lastResult.totalMakerAssetAmount,
|
||||
};
|
||||
for (const fr of orderedFillResults.slice(1)) {
|
||||
r.gas += fr.gas + 30e3;
|
||||
r.protocolFeeAmount = r.protocolFeeAmount.plus(fr.protocolFeeAmount);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function getSwapQuoteOrdersBreakdown(side: MarketOperation, tokenPath: Address[], hopFillResults: QuoteFillResult[]): SwapQuoteOrdersBreakdown {
|
||||
const cumulativeFillRatioBySource: Partial<{ [key in ERC20BridgeSource]: number }> = {};
|
||||
for (const hop of hopFillResults) {
|
||||
const hopTotalFillAmount = side === MarketOperation.Sell
|
||||
? hop.totalTakerAssetAmount
|
||||
: hop.totalMakerAssetAmount;
|
||||
for (const [source, sourceFillAmount] of Object.entries(hop.fillAmountBySource)) {
|
||||
cumulativeFillRatioBySource[source as ERC20BridgeSource] =
|
||||
(cumulativeFillRatioBySource[source as ERC20BridgeSource] || 0)
|
||||
+ sourceFillAmount.div(hopTotalFillAmount).toNumber();
|
||||
}
|
||||
}
|
||||
const globalFillRatiosSum = Object.values(cumulativeFillRatioBySource).reduce((a, v) => a! + v!, 0);
|
||||
if (!globalFillRatiosSum) {
|
||||
return {};
|
||||
}
|
||||
const breakdown: SwapQuoteOrdersBreakdown = {};
|
||||
for (const [source, fillRatio] of Object.entries(cumulativeFillRatioBySource)) {
|
||||
(breakdown as any)[source] = fillRatio! / globalFillRatiosSum;
|
||||
}
|
||||
const hopBreakdowns = hopFillResults.map(hop => {
|
||||
const hopTotalFillAmount = side === MarketOperation.Sell
|
||||
? hop.totalTakerAssetAmount
|
||||
: hop.totalMakerAssetAmount;
|
||||
return Object.assign(
|
||||
{},
|
||||
...Object.entries(hop.fillAmountBySource).map(([source, sourceFillAmount]) => ({
|
||||
[source as ERC20BridgeSource]: sourceFillAmount.div(hopTotalFillAmount).toNumber(),
|
||||
})),
|
||||
);
|
||||
const bestCaseFillResult = simulateBestCaseFill({
|
||||
gasPrice,
|
||||
orders: optimizedOrders,
|
||||
side: operation,
|
||||
fillAmount: assetFillAmount,
|
||||
opts: { gasSchedule },
|
||||
});
|
||||
if (hopFillResults.length > 1) {
|
||||
return {
|
||||
|
||||
const worstCaseFillResult = simulateWorstCaseFill({
|
||||
gasPrice,
|
||||
orders: optimizedOrders,
|
||||
side: operation,
|
||||
fillAmount: assetFillAmount,
|
||||
opts: { gasSchedule, slippage },
|
||||
});
|
||||
|
||||
return {
|
||||
bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult, 0),
|
||||
worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult, slippage),
|
||||
sourceBreakdown: getSwapQuoteOrdersBreakdown(bestCaseFillResult.fillAmountBySource),
|
||||
};
|
||||
}
|
||||
|
||||
function calculateTwoHopQuoteInfo(
|
||||
optimizedOrders: OptimizedMarketOrder[],
|
||||
operation: MarketOperation,
|
||||
gasSchedule: FeeSchedule,
|
||||
slippage: number,
|
||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
||||
const [firstHopOrder, secondHopOrder] = optimizedOrders;
|
||||
const [firstHopFill] = firstHopOrder.fills;
|
||||
const [secondHopFill] = secondHopOrder.fills;
|
||||
const gas = new BigNumber(
|
||||
gasSchedule[ERC20BridgeSource.MultiHop]!({
|
||||
firstHopSource: _.pick(firstHopFill, 'source', 'fillData'),
|
||||
secondHopSource: _.pick(secondHopFill, 'source', 'fillData'),
|
||||
}),
|
||||
).toNumber();
|
||||
const isSell = operation === MarketOperation.Sell;
|
||||
|
||||
return {
|
||||
bestCaseQuoteInfo: {
|
||||
makerAmount: isSell ? secondHopFill.output : secondHopFill.input,
|
||||
takerAmount: isSell ? firstHopFill.input : firstHopFill.output,
|
||||
totalTakerAmount: isSell ? firstHopFill.input : firstHopFill.output,
|
||||
feeTakerTokenAmount: constants.ZERO_AMOUNT,
|
||||
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
|
||||
gas,
|
||||
slippage: 0,
|
||||
},
|
||||
// TODO jacob consolidate this with quote simulation worstCase
|
||||
worstCaseQuoteInfo: {
|
||||
makerAmount: isSell
|
||||
? secondHopOrder.makerAmount.times(1 - slippage).integerValue()
|
||||
: secondHopOrder.makerAmount,
|
||||
takerAmount: isSell
|
||||
? firstHopOrder.takerAmount
|
||||
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
|
||||
totalTakerAmount: isSell
|
||||
? firstHopOrder.takerAmount
|
||||
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
|
||||
feeTakerTokenAmount: constants.ZERO_AMOUNT,
|
||||
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
|
||||
gas,
|
||||
slippage,
|
||||
},
|
||||
sourceBreakdown: {
|
||||
[ERC20BridgeSource.MultiHop]: {
|
||||
proportion: 1,
|
||||
tokenPath: tokenPath,
|
||||
breakdowns: side === MarketOperation.Sell ? hopBreakdowns : hopBreakdowns.reverse(),
|
||||
proportion: new BigNumber(1),
|
||||
intermediateToken: secondHopOrder.takerToken,
|
||||
hops: [firstHopFill.source, secondHopFill.source],
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getSwapQuoteOrdersBreakdown(fillAmountBySource: { [source: string]: BigNumber }): SwapQuoteOrdersBreakdown {
|
||||
const totalFillAmount = BigNumber.sum(...Object.values(fillAmountBySource));
|
||||
const breakdown: SwapQuoteOrdersBreakdown = {};
|
||||
Object.entries(fillAmountBySource).forEach(([s, fillAmount]) => {
|
||||
const source = s as keyof SwapQuoteOrdersBreakdown;
|
||||
if (source === ERC20BridgeSource.MultiHop) {
|
||||
// TODO jacob has a different breakdown
|
||||
} else {
|
||||
breakdown[source] = fillAmount.div(totalFillAmount);
|
||||
}
|
||||
});
|
||||
return breakdown;
|
||||
}
|
||||
|
||||
function fillResultsToQuoteInfo(fr: QuoteFillResult): SwapQuoteInfo {
|
||||
function fillResultsToQuoteInfo(fr: QuoteFillResult, slippage: number): SwapQuoteInfo {
|
||||
return {
|
||||
makerAmount: fr.totalMakerAssetAmount,
|
||||
takerAmount: fr.takerAssetAmount,
|
||||
@@ -719,6 +663,7 @@ function fillResultsToQuoteInfo(fr: QuoteFillResult): SwapQuoteInfo {
|
||||
feeTakerTokenAmount: fr.takerFeeTakerAssetAmount,
|
||||
protocolFeeInWeiAmount: fr.protocolFeeAmount,
|
||||
gas: fr.gas,
|
||||
slippage,
|
||||
};
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import { ChainId } from '@0x/contract-addresses';
|
||||
import { BlockParam, ContractAddresses, GethCallOverrides } from '@0x/contract-wrappers';
|
||||
import {
|
||||
FillQuoteTransformerLimitOrderInfo,
|
||||
FillQuoteTransformerOrderType,
|
||||
FillQuoteTransformerRfqOrderInfo,
|
||||
LimitOrderFields,
|
||||
RfqOrder,
|
||||
RfqOrderFields,
|
||||
@@ -18,22 +16,13 @@ import {
|
||||
ERC20BridgeSource,
|
||||
GetMarketOrdersOpts,
|
||||
LiquidityProviderRegistry,
|
||||
LiquidityProviderFillData,
|
||||
OptimizedMarketOrder,
|
||||
TokenAdjacencyGraph,
|
||||
BridgeFillData,
|
||||
CurveFillData,
|
||||
UniswapV2FillData,
|
||||
UniswapV3FillData,
|
||||
NativeOrderFillData,
|
||||
MooniswapFillData,
|
||||
} from './utils/market_operation_utils/types';
|
||||
export { SamplerMetrics } from './utils/market_operation_utils/types';
|
||||
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
|
||||
import { MetricsProxy } from './utils/quote_requestor';
|
||||
|
||||
export type Address = string;
|
||||
export type Bytes = string;
|
||||
|
||||
/**
|
||||
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
||||
* permittedOrderFeeTypes: A set of all the takerFee types that OrderPruner will filter for
|
||||
@@ -49,9 +38,19 @@ export interface SignedOrder<T> {
|
||||
signature: Signature;
|
||||
}
|
||||
|
||||
export type SignedRfqOrder = SignedOrder<RfqOrderFields>;
|
||||
export type SignedLimitOrder = SignedOrder<LimitOrderFields>;
|
||||
export type SignedNativeOrder = SignedLimitOrder | SignedRfqOrder;
|
||||
export type SignedNativeOrder = SignedOrder<LimitOrderFields> | SignedOrder<RfqOrderFields>;
|
||||
export type NativeOrderWithFillableAmounts = SignedNativeOrder & NativeOrderFillableAmountFields;
|
||||
|
||||
/**
|
||||
* fillableMakerAmount: Amount of makerAsset that is fillable
|
||||
* fillableTakerAmount: Amount of takerAsset that is fillable
|
||||
* fillableTakerFeeAmount: Amount of takerFee paid to fill fillableTakerAmount
|
||||
*/
|
||||
export interface NativeOrderFillableAmountFields {
|
||||
fillableMakerAmount: BigNumber;
|
||||
fillableTakerAmount: BigNumber;
|
||||
fillableTakerFeeAmount: BigNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the metadata to call a smart contract with calldata.
|
||||
@@ -168,73 +167,21 @@ export interface SwapQuoteBase {
|
||||
takerToken: string;
|
||||
makerToken: string;
|
||||
gasPrice: BigNumber;
|
||||
hops: SwapQuoteHop[];
|
||||
orders: OptimizedMarketOrder[];
|
||||
bestCaseQuoteInfo: SwapQuoteInfo;
|
||||
worstCaseQuoteInfo: SwapQuoteInfo;
|
||||
sourceBreakdown: SwapQuoteOrdersBreakdown;
|
||||
quoteReport?: QuoteReport;
|
||||
extendedQuoteReportSources?: ExtendedQuoteReportSources;
|
||||
priceComparisonsReport?: PriceComparisonsReport;
|
||||
isTwoHop: boolean;
|
||||
makerTokenDecimals: number;
|
||||
takerTokenDecimals: number;
|
||||
takerAmountPerEth: BigNumber;
|
||||
makerAmountPerEth: BigNumber;
|
||||
maxSlippage: number;
|
||||
blockNumber: number;
|
||||
}
|
||||
|
||||
export interface SwapQuoteHop {
|
||||
takerToken: Address;
|
||||
makerToken: Address;
|
||||
makerAmount: BigNumber;
|
||||
takerAmount: BigNumber;
|
||||
minMakerAmount: BigNumber;
|
||||
maxTakerAmount: BigNumber;
|
||||
sourceFlags: bigint;
|
||||
orders: SwapQuoteOrder[];
|
||||
}
|
||||
|
||||
export interface SwapQuoteOrder {
|
||||
type: FillQuoteTransformerOrderType; // should correspond with TFillData
|
||||
source: ERC20BridgeSource;
|
||||
makerToken: string;
|
||||
takerToken: string;
|
||||
gasCost: number;
|
||||
makerAmount: BigNumber;
|
||||
takerAmount: BigNumber;
|
||||
isFallback: boolean;
|
||||
fillData?: any;
|
||||
}
|
||||
|
||||
export interface SwapQuoteBridgeOrder<TFillData extends BridgeFillData> extends SwapQuoteOrder {
|
||||
fillData: TFillData;
|
||||
minMakerAmount: BigNumber;
|
||||
maxTakerAmount: BigNumber;
|
||||
}
|
||||
|
||||
export interface SwapQuoteGenericBridgeOrder extends SwapQuoteBridgeOrder<BridgeFillData> {}
|
||||
|
||||
export interface SwapQuoteUniswapV2BridgeOrder extends SwapQuoteBridgeOrder<UniswapV2FillData> {}
|
||||
|
||||
export interface SwapQuoteUniswapV3BridgeOrder extends SwapQuoteBridgeOrder<UniswapV3FillData> {}
|
||||
|
||||
export interface SwapQuoteLiquidityProviderBridgeOrder extends SwapQuoteBridgeOrder<LiquidityProviderFillData> {}
|
||||
|
||||
export interface SwapQuoteMooniswapBridgeOrder extends SwapQuoteBridgeOrder<MooniswapFillData> {}
|
||||
|
||||
export interface SwapQuoteCurveBridgeOrder extends SwapQuoteBridgeOrder<CurveFillData> {}
|
||||
|
||||
export interface SwapQuoteLimitOrder extends SwapQuoteOrder {
|
||||
type: FillQuoteTransformerOrderType.Limit;
|
||||
fillData: NativeOrderFillData;
|
||||
}
|
||||
|
||||
export interface SwapQuoteRfqOrder extends SwapQuoteOrder {
|
||||
type: FillQuoteTransformerOrderType.Rfq;
|
||||
fillData: NativeOrderFillData;
|
||||
}
|
||||
|
||||
export type SwapQuoteNativeOrder = SwapQuoteLimitOrder | SwapQuoteRfqOrder;
|
||||
|
||||
/**
|
||||
* takerAssetFillAmount: The amount of takerAsset sold for makerAsset.
|
||||
* type: Specified MarketOperation the SwapQuote is provided for
|
||||
@@ -271,23 +218,22 @@ export interface SwapQuoteInfo {
|
||||
makerAmount: BigNumber;
|
||||
protocolFeeInWeiAmount: BigNumber;
|
||||
gas: number;
|
||||
slippage: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* percentage breakdown of each liquidity source used in quote
|
||||
*/
|
||||
export type SwapQuoteOrdersBreakdown = Partial<
|
||||
{ [key in Exclude<ERC20BridgeSource, typeof ERC20BridgeSource.MultiHop>]: number } & {
|
||||
[ERC20BridgeSource.MultiHop]: SwapQuoteMultiHopBreakdown;
|
||||
{ [key in Exclude<ERC20BridgeSource, typeof ERC20BridgeSource.MultiHop>]: BigNumber } & {
|
||||
[ERC20BridgeSource.MultiHop]: {
|
||||
proportion: BigNumber;
|
||||
intermediateToken: string;
|
||||
hops: ERC20BridgeSource[];
|
||||
};
|
||||
}
|
||||
>;
|
||||
|
||||
export interface SwapQuoteMultiHopBreakdown {
|
||||
proportion: number;
|
||||
tokenPath: Address[];
|
||||
breakdowns: Partial<{ [key in ERC20BridgeSource]: number }>[];
|
||||
};
|
||||
|
||||
/**
|
||||
* nativeExclusivelyRFQ: if set to `true`, Swap quote will exclude Open Orderbook liquidity.
|
||||
* If set to `true` and `ERC20BridgeSource.Native` is part of the `excludedSources`
|
||||
@@ -385,13 +331,15 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
||||
chainId: ChainId;
|
||||
orderRefreshIntervalMs: number;
|
||||
expiryBufferMs: number;
|
||||
// ethereumRpcUrl?: string;
|
||||
ethereumRpcUrl?: string;
|
||||
contractAddresses?: AssetSwapperContractAddresses;
|
||||
samplerGasLimit?: number;
|
||||
// multiBridgeAddress?: string;
|
||||
multiBridgeAddress?: string;
|
||||
ethGasStationUrl?: string;
|
||||
rfqt?: SwapQuoterRfqOpts;
|
||||
samplerServiceUrl: string;
|
||||
samplerOverrides?: SamplerOverrides;
|
||||
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
||||
liquidityProviderRegistry?: LiquidityProviderRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,6 +415,8 @@ export interface SamplerCallResult {
|
||||
data: string;
|
||||
}
|
||||
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||
|
||||
export enum AltQuoteModel {
|
||||
Firm = 'firm',
|
||||
Indicative = 'indicative',
|
||||
|
@@ -0,0 +1,106 @@
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { gql, request } from 'graphql-request';
|
||||
|
||||
import { constants } from '../../constants';
|
||||
|
||||
const RESERVES_GQL_QUERY = gql`
|
||||
{
|
||||
reserves(
|
||||
first: 300
|
||||
where: { isActive: true, isFrozen: false }
|
||||
orderBy: totalLiquidity
|
||||
orderDirection: desc
|
||||
) {
|
||||
id
|
||||
underlyingAsset
|
||||
aToken {
|
||||
id
|
||||
}
|
||||
pool {
|
||||
id
|
||||
lendingPool
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export interface AaveReserve {
|
||||
id: string;
|
||||
underlyingAsset: string;
|
||||
aToken: {
|
||||
id: string;
|
||||
};
|
||||
pool: {
|
||||
id: string;
|
||||
lendingPool: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Cache {
|
||||
[key: string]: AaveReserve[];
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
|
||||
|
||||
/**
|
||||
* Fetches Aave V2 reserve information from the official subgraph(s).
|
||||
* The reserve information is updated every 30 minutes and cached
|
||||
* so that it can be accessed with the underlying token's address
|
||||
*/
|
||||
export class AaveV2ReservesCache {
|
||||
private _cache: Cache = {};
|
||||
constructor(private readonly _subgraphUrl: string) {
|
||||
const resfreshReserves = async () => this.fetchAndUpdateReservesAsync();
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
resfreshReserves();
|
||||
setInterval(resfreshReserves, RESERVES_REFRESH_INTERVAL_MS);
|
||||
}
|
||||
/**
|
||||
* Fetches Aave V2 reserves from the subgraph and updates the cache
|
||||
*/
|
||||
public async fetchAndUpdateReservesAsync(): Promise<void> {
|
||||
try {
|
||||
const { reserves } = await request<{ reserves: AaveReserve[] }>(this._subgraphUrl, RESERVES_GQL_QUERY);
|
||||
const newCache = reserves.reduce<Cache>((memo, reserve) => {
|
||||
const underlyingAsset = reserve.underlyingAsset.toLowerCase();
|
||||
if (!memo[underlyingAsset]) {
|
||||
memo[underlyingAsset] = [];
|
||||
}
|
||||
|
||||
memo[underlyingAsset].push(reserve);
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
this._cache = newCache;
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to update Aave V2 reserves cache: ${err.message}`);
|
||||
// Empty cache just to be safe
|
||||
this._cache = {};
|
||||
}
|
||||
}
|
||||
public get(takerToken: string, makerToken: string): AaveReserve | undefined {
|
||||
// Deposit takerToken into reserve
|
||||
if (this._cache[takerToken.toLowerCase()]) {
|
||||
const matchingReserve = this._cache[takerToken.toLowerCase()].find(
|
||||
r => r.aToken.id === makerToken.toLowerCase(),
|
||||
);
|
||||
if (matchingReserve) {
|
||||
return matchingReserve;
|
||||
}
|
||||
}
|
||||
|
||||
// Withdraw makerToken from reserve
|
||||
if (this._cache[makerToken.toLowerCase()]) {
|
||||
const matchingReserve = this._cache[makerToken.toLowerCase()].find(
|
||||
r => r.aToken.id === takerToken.toLowerCase(),
|
||||
);
|
||||
if (matchingReserve) {
|
||||
return matchingReserve;
|
||||
}
|
||||
}
|
||||
|
||||
// No match
|
||||
return undefined;
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
import { SupportedProvider } from '@0x/dev-utils';
|
||||
import { SDK } from '@bancor/sdk';
|
||||
import { Ethereum } from '@bancor/sdk/dist/blockchains/ethereum';
|
||||
import { BlockchainType } from '@bancor/sdk/dist/types';
|
||||
|
||||
import { MAINNET_TOKENS } from './constants';
|
||||
|
||||
const findToken = (tokenAddress: string, graph: object): string =>
|
||||
// If we're looking for WETH it is stored by Bancor as the 0xeee address
|
||||
tokenAddress.toLowerCase() === MAINNET_TOKENS.WETH.toLowerCase()
|
||||
? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'
|
||||
: Object.keys(graph).filter(k => k.toLowerCase() === tokenAddress.toLowerCase())[0];
|
||||
|
||||
export class BancorService {
|
||||
public static async createAsync(provider: SupportedProvider): Promise<BancorService> {
|
||||
const sdk = await SDK.create({ ethereumNodeEndpoint: provider });
|
||||
const service = new BancorService(sdk);
|
||||
return service;
|
||||
}
|
||||
|
||||
constructor(public sdk: SDK) {}
|
||||
public getPaths(_fromToken: string, _toToken: string): string[][] {
|
||||
// HACK: We reach into the blockchain object and pull in it's cache of tokens
|
||||
// and we use it's internal non-async getPathsFunc
|
||||
try {
|
||||
const blockchain = this.sdk._core.blockchains[BlockchainType.Ethereum] as Ethereum;
|
||||
const fromToken = findToken(_fromToken, blockchain.graph);
|
||||
const toToken = findToken(_toToken, blockchain.graph);
|
||||
return blockchain.getPathsFunc.bind(blockchain)(fromToken, toToken);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,6 +7,7 @@ import {
|
||||
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
|
||||
BELT_BSC_INFOS,
|
||||
BISWAP_ROUTER_BY_CHAIN_ID,
|
||||
CAFESWAP_ROUTER_BY_CHAIN_ID,
|
||||
CHEESESWAP_ROUTER_BY_CHAIN_ID,
|
||||
COMETHSWAP_ROUTER_BY_CHAIN_ID,
|
||||
COMPONENT_POOLS_BY_CHAIN_ID,
|
||||
@@ -25,10 +26,9 @@ import {
|
||||
FIREBIRDONESWAP_BSC_INFOS,
|
||||
FIREBIRDONESWAP_POLYGON_INFOS,
|
||||
IRONSWAP_POLYGON_INFOS,
|
||||
KNIGHTSWAP_ROUTER_BY_CHAIN_ID,
|
||||
JETSWAP_ROUTER_BY_CHAIN_ID,
|
||||
JULSWAP_ROUTER_BY_CHAIN_ID,
|
||||
MAX_DODOV2_POOLS_QUERIED,
|
||||
MDEX_ROUTER_BY_CHAIN_ID,
|
||||
MESHSWAP_ROUTER_BY_CHAIN_ID,
|
||||
MOBIUSMONEY_CELO_INFOS,
|
||||
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
|
||||
MSTABLE_POOLS_BY_CHAIN_ID,
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
|
||||
PANGOLIN_ROUTER_BY_CHAIN_ID,
|
||||
PLATYPUS_AVALANCHE_INFOS,
|
||||
POLYDEX_ROUTER_BY_CHAIN_ID,
|
||||
QUICKSWAP_ROUTER_BY_CHAIN_ID,
|
||||
SADDLE_MAINNET_INFOS,
|
||||
SHELL_POOLS_BY_CHAIN_ID,
|
||||
@@ -525,12 +526,16 @@ export function uniswapV2LikeRouterAddress(
|
||||
| ERC20BridgeSource.PancakeSwapV2
|
||||
| ERC20BridgeSource.BakerySwap
|
||||
| ERC20BridgeSource.ApeSwap
|
||||
| ERC20BridgeSource.CafeSwap
|
||||
| ERC20BridgeSource.CheeseSwap
|
||||
| ERC20BridgeSource.JulSwap
|
||||
| ERC20BridgeSource.QuickSwap
|
||||
| ERC20BridgeSource.ComethSwap
|
||||
| ERC20BridgeSource.Dfyn
|
||||
| ERC20BridgeSource.WaultSwap
|
||||
| ERC20BridgeSource.Polydex
|
||||
| ERC20BridgeSource.ShibaSwap
|
||||
| ERC20BridgeSource.JetSwap
|
||||
| ERC20BridgeSource.TraderJoe
|
||||
| ERC20BridgeSource.Pangolin
|
||||
| ERC20BridgeSource.UbeSwap
|
||||
@@ -538,10 +543,7 @@ export function uniswapV2LikeRouterAddress(
|
||||
| ERC20BridgeSource.SpookySwap
|
||||
| ERC20BridgeSource.SpiritSwap
|
||||
| ERC20BridgeSource.BiSwap
|
||||
| ERC20BridgeSource.Yoshi
|
||||
| ERC20BridgeSource.MDex
|
||||
| ERC20BridgeSource.KnightSwap
|
||||
| ERC20BridgeSource.MeshSwap,
|
||||
| ERC20BridgeSource.Yoshi,
|
||||
): string {
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
@@ -558,8 +560,12 @@ export function uniswapV2LikeRouterAddress(
|
||||
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.ApeSwap:
|
||||
return APESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.CafeSwap:
|
||||
return CAFESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.CheeseSwap:
|
||||
return CHEESESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.JulSwap:
|
||||
return JULSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.QuickSwap:
|
||||
return QUICKSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.ComethSwap:
|
||||
@@ -568,8 +574,12 @@ export function uniswapV2LikeRouterAddress(
|
||||
return DFYN_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.WaultSwap:
|
||||
return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.Polydex:
|
||||
return POLYDEX_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.ShibaSwap:
|
||||
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:
|
||||
@@ -586,12 +596,6 @@ export function uniswapV2LikeRouterAddress(
|
||||
return BISWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.Yoshi:
|
||||
return YOSHI_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.MeshSwap:
|
||||
return MESHSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.MDex:
|
||||
return MDEX_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.KnightSwap:
|
||||
return KNIGHTSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
default:
|
||||
throw new Error(`Unknown UniswapV2 like source ${source}`);
|
||||
}
|
||||
|
@@ -1,12 +1,17 @@
|
||||
import { Web3Wrapper } from '@0x/dev-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||
import { BigNumber, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { MarketOperation } from '../../types';
|
||||
|
||||
import { COMPARISON_PRICE_DECIMALS } from './constants';
|
||||
import { COMPARISON_PRICE_DECIMALS, SOURCE_FLAGS } from './constants';
|
||||
import {
|
||||
ComparisonPrice,
|
||||
ERC20BridgeSource,
|
||||
ExchangeProxyOverhead,
|
||||
FeeEstimate,
|
||||
FeeSchedule,
|
||||
MarketSideLiquidity,
|
||||
} from './types';
|
||||
|
||||
@@ -24,20 +29,41 @@ export function getComparisonPrices(
|
||||
adjustedRate: BigNumber,
|
||||
amount: BigNumber,
|
||||
marketSideLiquidity: MarketSideLiquidity,
|
||||
gasPrice: BigNumber,
|
||||
feeSchedule: FeeSchedule,
|
||||
exchangeProxyOverhead: ExchangeProxyOverhead,
|
||||
): ComparisonPrice {
|
||||
let wholeOrder: BigNumber | undefined;
|
||||
let feeInEth = gasPrice.times(100e3);
|
||||
let feeInEth: BigNumber | number;
|
||||
|
||||
// HACK: get the fee penalty of a single 0x native order
|
||||
// The FeeSchedule function takes in a `FillData` object and returns a fee estimate in ETH
|
||||
// We don't have fill data here, we just want the cost of a single native order, so we pass in undefined
|
||||
// This works because the feeSchedule returns a constant for Native orders, this will need
|
||||
// to be tweaked if the feeSchedule for native orders uses the fillData passed in
|
||||
// 2 potential issues: there is no native fee schedule or the fee schedule depends on fill data
|
||||
if (feeSchedule[ERC20BridgeSource.Native] === undefined) {
|
||||
logUtils.warn('ComparisonPrice function did not find native order fee schedule');
|
||||
|
||||
return { wholeOrder };
|
||||
} else {
|
||||
try {
|
||||
const fillFeeInEth = new BigNumber(
|
||||
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }),
|
||||
);
|
||||
const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
|
||||
feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);
|
||||
} catch {
|
||||
logUtils.warn('Native order fee schedule requires fill data');
|
||||
|
||||
return { wholeOrder };
|
||||
}
|
||||
}
|
||||
|
||||
const [inputAmountPerEth, outputAmountPerEth] = [
|
||||
marketSideLiquidity.tokenAmountPerEth[marketSideLiquidity.inputToken],
|
||||
marketSideLiquidity.tokenAmountPerEth[marketSideLiquidity.outputToken],
|
||||
];
|
||||
// Calc native order fee penalty in output unit (maker units for sells, taker unit for buys)
|
||||
const feePenalty = !outputAmountPerEth.isZero()
|
||||
? outputAmountPerEth.times(feeInEth)
|
||||
const feePenalty = !marketSideLiquidity.outputAmountPerEth.isZero()
|
||||
? marketSideLiquidity.outputAmountPerEth.times(feeInEth)
|
||||
: // if it's a sell, the input token is the taker token
|
||||
inputAmountPerEth
|
||||
marketSideLiquidity.inputAmountPerEth
|
||||
.times(feeInEth)
|
||||
.times(marketSideLiquidity.side === MarketOperation.Sell ? adjustedRate : adjustedRate.pow(-1));
|
||||
|
||||
|
@@ -0,0 +1,78 @@
|
||||
import { logUtils } from '@0x/utils';
|
||||
import axios from 'axios';
|
||||
|
||||
import { constants } from '../../constants';
|
||||
|
||||
export interface CToken {
|
||||
tokenAddress: string;
|
||||
underlyingAddress: string;
|
||||
}
|
||||
|
||||
interface CTokenApiResponse {
|
||||
cToken: Array<{
|
||||
token_address: string;
|
||||
underlying_address: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface Cache {
|
||||
[key: string]: CToken;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
|
||||
|
||||
/**
|
||||
* Fetches a list of CTokens from Compound's official API.
|
||||
* The token information is updated every 30 minutes and cached
|
||||
* so that it can be accessed with the underlying token's address.
|
||||
*/
|
||||
export class CompoundCTokenCache {
|
||||
private _cache: Cache = {};
|
||||
constructor(private readonly _apiUrl: string, private readonly _wethAddress: string) {
|
||||
const refreshCTokenCache = async () => this.fetchAndUpdateCTokensAsync();
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
refreshCTokenCache();
|
||||
setInterval(refreshCTokenCache, CTOKEN_REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
public async fetchAndUpdateCTokensAsync(): Promise<void> {
|
||||
try {
|
||||
const { data } = await axios.get<CTokenApiResponse>(`${this._apiUrl}/ctoken`);
|
||||
const newCache = data?.cToken.reduce<Cache>((memo, cToken) => {
|
||||
// NOTE: Re-map cETH with null underlying token address to WETH address (we only handle WETH internally)
|
||||
const underlyingAddressClean = cToken.underlying_address
|
||||
? cToken.underlying_address.toLowerCase()
|
||||
: this._wethAddress;
|
||||
|
||||
const tokenData: CToken = {
|
||||
tokenAddress: cToken.token_address.toLowerCase(),
|
||||
underlyingAddress: underlyingAddressClean,
|
||||
};
|
||||
memo[underlyingAddressClean] = tokenData;
|
||||
return memo;
|
||||
}, {});
|
||||
|
||||
this._cache = newCache;
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to update Compound cToken cache: ${err.message}`);
|
||||
// NOTE: Safe to keep already cached data as tokens should only be added to the list
|
||||
}
|
||||
}
|
||||
public get(takerToken: string, makerToken: string): CToken | undefined {
|
||||
// mint cToken
|
||||
let cToken = this._cache[takerToken.toLowerCase()];
|
||||
if (cToken && makerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
|
||||
return cToken;
|
||||
}
|
||||
|
||||
// redeem cToken
|
||||
cToken = this._cache[makerToken.toLowerCase()];
|
||||
if (cToken && takerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
|
||||
return cToken;
|
||||
}
|
||||
|
||||
// No match
|
||||
return undefined;
|
||||
}
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import { formatBytes32String } from '@ethersproject/strings';
|
||||
|
||||
import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
|
||||
import { valueByChainId } from '../utils';
|
||||
|
||||
import { SourceFilters } from './source_filters';
|
||||
import {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
AaveV2FillData,
|
||||
BalancerV2BatchSwapFillData,
|
||||
BancorFillData,
|
||||
@@ -16,13 +15,13 @@ import {
|
||||
CurveFunctionSelectors,
|
||||
CurveInfo,
|
||||
DODOFillData,
|
||||
>>>>>>> 470e9a469 (AS: Balancer V2 batchSwap (#462))
|
||||
ERC20BridgeSource,
|
||||
FeeSchedule,
|
||||
FillData,
|
||||
FinalUniswapV3FillData,
|
||||
GeistFillData,
|
||||
GetMarketOrdersOpts,
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
isFinalUniswapV3FillData,
|
||||
LidoFillData,
|
||||
LidoInfo,
|
||||
LiquidityProviderFillData,
|
||||
LiquidityProviderRegistry,
|
||||
@@ -30,12 +29,15 @@ import {
|
||||
MultiHopFillData,
|
||||
PlatypusInfo,
|
||||
PsmInfo,
|
||||
>>>>>>> 9a28e51f5 (rebased dev and merged)
|
||||
TokenAdjacencyGraph,
|
||||
UniswapV2FillData,
|
||||
UniswapV3FillData,
|
||||
} from './types';
|
||||
|
||||
// tslint:disable: custom-no-magic-numbers no-bitwise
|
||||
|
||||
export const ERC20_PROXY_ID = '0xf47261b0';
|
||||
export const WALLET_SIGNATURE = '0x04';
|
||||
export const ONE_ETHER = new BigNumber(1e18);
|
||||
export const NEGATIVE_INF = new BigNumber('-Infinity');
|
||||
export const POSITIVE_INF = new BigNumber('Infinity');
|
||||
@@ -45,8 +47,29 @@ export const ONE_HOUR_IN_SECONDS = 60 * 60;
|
||||
export const ONE_SECOND_MS = 1000;
|
||||
export const NULL_BYTES = '0x';
|
||||
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
export const SAMPLER_ADDRESS = '0x5555555555555555555555555555555555555555';
|
||||
export const COMPARISON_PRICE_DECIMALS = 10;
|
||||
|
||||
// TODO(kimpers): Consolidate this implementation with the one in @0x/token-metadata
|
||||
function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } {
|
||||
// TODO I don't like this but iterating through enums is weird
|
||||
return {
|
||||
[ChainId.Mainnet]: defaultValue,
|
||||
[ChainId.Ropsten]: defaultValue,
|
||||
[ChainId.Rinkeby]: defaultValue,
|
||||
[ChainId.Kovan]: defaultValue,
|
||||
[ChainId.Ganache]: defaultValue,
|
||||
[ChainId.BSC]: defaultValue,
|
||||
[ChainId.Polygon]: defaultValue,
|
||||
[ChainId.PolygonMumbai]: defaultValue,
|
||||
[ChainId.Avalanche]: defaultValue,
|
||||
[ChainId.Fantom]: defaultValue,
|
||||
[ChainId.Celo]: defaultValue,
|
||||
[ChainId.Optimism]: defaultValue,
|
||||
...(rest || {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Valid sources for market sell.
|
||||
*/
|
||||
@@ -56,18 +79,10 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
ERC20BridgeSource.Kyber,
|
||||
>>>>>>> 9eadc5fc2 (chore: Offboard Eth2Dai [TKR-356] (#470))
|
||||
=======
|
||||
>>>>>>> 2d16f83e3 (Offboard/clean up Oasis, CoFix, and legacy Kyber [TKR-405] (#482))
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
ERC20BridgeSource.BalancerV2,
|
||||
ERC20BridgeSource.Bancor,
|
||||
ERC20BridgeSource.BancorV3,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -120,15 +135,16 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Smoothy,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.CafeSwap,
|
||||
ERC20BridgeSource.CheeseSwap,
|
||||
ERC20BridgeSource.JulSwap,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.WaultSwap,
|
||||
ERC20BridgeSource.FirebirdOneSwap,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.ACryptos,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.BiSwap,
|
||||
ERC20BridgeSource.MDex,
|
||||
ERC20BridgeSource.KnightSwap,
|
||||
]),
|
||||
[ChainId.Polygon]: new SourceFilters([
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -141,17 +157,18 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Dodo,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.WaultSwap,
|
||||
ERC20BridgeSource.Polydex,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.FirebirdOneSwap,
|
||||
ERC20BridgeSource.BalancerV2,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.IronSwap,
|
||||
ERC20BridgeSource.AaveV2,
|
||||
ERC20BridgeSource.UniswapV3,
|
||||
ERC20BridgeSource.Synapse,
|
||||
ERC20BridgeSource.MeshSwap,
|
||||
]),
|
||||
[ChainId.Avalanche]: new SourceFilters([
|
||||
ERC20BridgeSource.MultiHop,
|
||||
@@ -172,6 +189,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.Geist,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.MorpheusSwap,
|
||||
ERC20BridgeSource.SpiritSwap,
|
||||
ERC20BridgeSource.SpookySwap,
|
||||
@@ -191,7 +209,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.Velodrome,
|
||||
]),
|
||||
},
|
||||
new SourceFilters([]),
|
||||
@@ -206,18 +223,10 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
ERC20BridgeSource.Kyber,
|
||||
>>>>>>> 9eadc5fc2 (chore: Offboard Eth2Dai [TKR-356] (#470))
|
||||
=======
|
||||
>>>>>>> 2d16f83e3 (Offboard/clean up Oasis, CoFix, and legacy Kyber [TKR-405] (#482))
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
ERC20BridgeSource.BalancerV2,
|
||||
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
|
||||
ERC20BridgeSource.BancorV3,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Shell,
|
||||
@@ -270,15 +279,17 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Smoothy,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.CafeSwap,
|
||||
ERC20BridgeSource.CheeseSwap,
|
||||
ERC20BridgeSource.JulSwap,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.WaultSwap,
|
||||
ERC20BridgeSource.FirebirdOneSwap,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.ACryptos,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.Synapse,
|
||||
ERC20BridgeSource.BiSwap,
|
||||
ERC20BridgeSource.MDex,
|
||||
ERC20BridgeSource.KnightSwap,
|
||||
]),
|
||||
[ChainId.Polygon]: new SourceFilters([
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -291,17 +302,18 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Dodo,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.WaultSwap,
|
||||
ERC20BridgeSource.Polydex,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.FirebirdOneSwap,
|
||||
ERC20BridgeSource.BalancerV2,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.IronSwap,
|
||||
ERC20BridgeSource.AaveV2,
|
||||
ERC20BridgeSource.UniswapV3,
|
||||
ERC20BridgeSource.Synapse,
|
||||
ERC20BridgeSource.MeshSwap,
|
||||
]),
|
||||
[ChainId.Avalanche]: new SourceFilters([
|
||||
ERC20BridgeSource.MultiHop,
|
||||
@@ -322,6 +334,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.Geist,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.MorpheusSwap,
|
||||
ERC20BridgeSource.SpiritSwap,
|
||||
ERC20BridgeSource.SpookySwap,
|
||||
@@ -341,7 +354,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.Velodrome,
|
||||
]),
|
||||
},
|
||||
new SourceFilters([]),
|
||||
@@ -381,33 +393,43 @@ export const SOURCE_FLAGS: { [key in ERC20BridgeSource]: bigint } & {
|
||||
})),
|
||||
);
|
||||
|
||||
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 MIRROR_WRAPPED_TOKENS = {
|
||||
mAAPL: '0xd36932143f6ebdedd872d5fb0651f4b72fd15a84',
|
||||
mSLV: '0x9d1555d8cb3c846bb4f7d5b1b1080872c3166676',
|
||||
mIAU: '0x1d350417d9787e000cc1b95d70e9536dcd91f373',
|
||||
mAMZN: '0x0cae9e4d663793c2a2a0b211c1cf4bbca2b9caa7',
|
||||
mGOOGL: '0x4b70ccd1cf9905be1faed025eadbd3ab124efe9a',
|
||||
mTSLA: '0x21ca39943e91d704678f5d00b6616650f066fd63',
|
||||
mQQQ: '0x13b02c8de71680e71f0820c996e4be43c2f57d15',
|
||||
mTWTR: '0xedb0414627e6f1e3f082de65cd4f9c693d78cca9',
|
||||
mMSFT: '0x41bbedd7286daab5910a1f15d12cbda839852bd7',
|
||||
mNFLX: '0xc8d674114bac90148d11d3c1d33c61835a0f9dcd',
|
||||
mBABA: '0x676ce85f66adb8d7b8323aeefe17087a3b8cb363',
|
||||
mUSO: '0x31c63146a635eb7465e5853020b39713ac356991',
|
||||
mVIXY: '0xf72fcd9dcf0190923fadd44811e240ef4533fc86',
|
||||
mLUNA: '0xd2877702675e6ceb975b4a1dff9fb7baf4c91ea9',
|
||||
};
|
||||
|
||||
// Mainnet tokens
|
||||
// Not an exhaustive list, just enough so we don't repeat ourselves
|
||||
export const MAINNET_TOKENS = {
|
||||
WETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
// Stable Coins
|
||||
DAI: '0x6b175474e89094c44da98b954eedeac495271d0f',
|
||||
USDC: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7',
|
||||
sUSD: '0x57ab1ec28d129707052df4df418d58a2d46d5f51',
|
||||
BUSD: '0x4fabb145d64652a948d72533023f6e7a623c7c53',
|
||||
TUSD: '0x0000000000085d4780b73119b644ae5ecd22b376',
|
||||
PAX: '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
|
||||
GUSD: '0x056fd409e1d7a124bd7017459dfea2f387b6d5cd',
|
||||
HUSD: '0xdf574c24545e5ffecb9a659c229253d4111d87e1',
|
||||
mUSD: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5',
|
||||
USDN: '0x674c6ad92fd080e4004b2312b45f796a192d27a0',
|
||||
dUSD: '0x5bc25f649fc4e26069ddf4cf4010f9f706c23831',
|
||||
USDP: '0x1456688345527be1f37e9e627da0837d6f08c925',
|
||||
// Bitcoins
|
||||
WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
RenBTC: '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d',
|
||||
sBTC: '0xfe18be6b3bd88a2d2a7f928d00292e7a9963cfc6',
|
||||
tBTC: '0x8daebade922df735c38c80c7ebd708af50815faa',
|
||||
@@ -427,7 +449,6 @@ export const MAINNET_TOKENS = {
|
||||
sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620',
|
||||
sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb',
|
||||
stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84',
|
||||
wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0',
|
||||
LINK: '0x514910771af9ca656af840dff83e8264ecf986ca',
|
||||
MANA: '0x0f5d2fb29fb7d3cfee444a200298f468908cc942',
|
||||
KNC: '0xdefa4e8a7bcba345f687a2f1456f5edd9ce97202',
|
||||
@@ -447,9 +468,9 @@ export const MAINNET_TOKENS = {
|
||||
alETH: '0x0100546f2cd4c9d97f798ffc9755e47865ff7ee6',
|
||||
HT: '0x6f259637dcD74C767781E37Bc6133cd6A68aa161',
|
||||
// Mirror Protocol
|
||||
>>>>>>> 87308e769 (Update saddle mainnet pools (#450))
|
||||
UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd',
|
||||
MIR: '0x09a3ecafa817268f77be1283176b946c4ff2e608',
|
||||
...MIRROR_WRAPPED_TOKENS,
|
||||
// StableSwap "open pools" (crv.finance)
|
||||
STABLEx: '0xcd91538b91b4ba7797d39a2f66e63810b50a33d0',
|
||||
alUSD: '0xbc6da0fe9ad5f3b0d58160288917aa56653660e9',
|
||||
@@ -492,26 +513,33 @@ export const BSC_TOKENS = {
|
||||
USDT: '0x55d398326f99059ff775485246999027b3197955',
|
||||
USDC: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
|
||||
DAI: '0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3',
|
||||
PAX: '0xb7f8cd00c5a06c0537e2abff0b58033d02e5e094',
|
||||
UST: '0x23396cf899ca06c4472205fc903bdb4de249d6fc',
|
||||
VAI: '0x4bd17003473389a42daf6a0a729f6fdb328bbbd7',
|
||||
WEX: '0xa9c41a46a6b3531d28d5c32f6633dd2ff05dfb90',
|
||||
WETH: '0x2170ed0880ac9a755fd29b2688956bd959f933f8',
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
BTCB: '0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c',
|
||||
renBTC: '0xfce146bf3146100cfe5db4129cf6c82b0ef4ad8c',
|
||||
pBTC: '0xed28a457a5a76596ac48d87c0f577020f6ea1c4c',
|
||||
nUSD: '0x23b891e5c62e0955ae2bd185990103928ab817b3',
|
||||
BSW: '0x965F527D9159dCe6288a2219DB51fc6Eef120dD1',
|
||||
>>>>>>> f55eaa867 (Add BiSwap (as UniV2 clone) on BSC (#471))
|
||||
};
|
||||
|
||||
export const POLYGON_TOKENS = {
|
||||
DAI: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063',
|
||||
USDC: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
|
||||
USDT: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
|
||||
amDAI: '0x27f8d03b3a2196956ed754badc28d73be8830a6e',
|
||||
amUSDC: '0x1a13f4ca1d028320a707d99520abfefca3998b7f',
|
||||
amUSDT: '0x60d55f02a771d515e077c9c2403a1ef324885cec',
|
||||
WBTC: '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6',
|
||||
WMATIC: '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270',
|
||||
WETH: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619',
|
||||
renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501',
|
||||
QUICK: '0x831753dd7087cac61ab5644b308642cc1c33dc13',
|
||||
DFYN: '0xc168e40227e4ebd8c1cae80f7a55a4f0e6d66c97',
|
||||
BANANA: '0x5d47baba0d66083c52009271faf3f50dcc01023c',
|
||||
WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb',
|
||||
nUSD: '0xb6c473756050de474286bed418b77aeac39b02af',
|
||||
ANY: '0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8',
|
||||
};
|
||||
@@ -520,6 +548,7 @@ export const AVALANCHE_TOKENS = {
|
||||
WAVAX: '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7',
|
||||
WETH: '0x49d5c2bdffac6ce2bfdb6640f4f80f226bc10bab',
|
||||
WBTC: '0x50b7545627a5162f82a992c33b87adc75187b218',
|
||||
DAI: '0xd586e7f844cea2f87f50152665bcbc2c279d8d70',
|
||||
// bridged USDC
|
||||
USDC: '0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664',
|
||||
// native USDC on Avalanche usdc.e
|
||||
@@ -534,18 +563,11 @@ export const AVALANCHE_TOKENS = {
|
||||
nUSD: '0xcfc37a6ab183dd4aed08c204d1c2773c0b1bdf46',
|
||||
aWETH: '0x53f7c5869a859f0aec3d334ee8b4cf01e3492f21',
|
||||
MIM: '0x130966628846bfd36ff31a822705796e8cb8c18d',
|
||||
<<<<<<< HEAD
|
||||
DAI: '0xd586e7f844cea2f87f50152665bcbc2c279d8d70',
|
||||
=======
|
||||
MAG: '0x1d60109178C48E4A937D8AB71699D8eBb6F7c5dE',
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> d36034d95 (chore/ANY-QUICK on polygon MAG-MIM on avax (#464))
|
||||
=======
|
||||
sAVAX: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be',
|
||||
UST: '0xb599c3590f42f8f995ecfa0f85d2980b76862fc1',
|
||||
FRAX: '0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64',
|
||||
YUSD: '0x111111111111ed1d73f860f57b2798b683f2d325',
|
||||
>>>>>>> 9a28e51f5 (rebased dev and merged)
|
||||
};
|
||||
|
||||
export const CELO_TOKENS = {
|
||||
@@ -621,8 +643,6 @@ export const OPTIMISM_TOKENS = {
|
||||
sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204',
|
||||
};
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
export const CURVE_POOLS = {
|
||||
compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound
|
||||
// 1.USDT is dead
|
||||
@@ -810,9 +830,6 @@ export const ACRYPTOS_POOLS = {
|
||||
acs3btc: '0xbe7caa236544d1b9a0e7f91e94b9f5bfd3b5ca81',
|
||||
};
|
||||
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> ba719a963 (Add cvxfxs-fxs curve pool on Ethereum mainnet (#465))
|
||||
=======
|
||||
export const PLATYPUS_AVALANCHE_POOLS = {
|
||||
usd: '0x66357dcace80431aee0a7507e2e361b7e2402370',
|
||||
yusd: '0xc828d995c686aaba78a4ac89dfc8ec0ff4c5be83',
|
||||
@@ -821,7 +838,6 @@ export const PLATYPUS_AVALANCHE_POOLS = {
|
||||
sAVAX: '0x4658ea7e9960d6158a261104aaa160cc953bb6ba',
|
||||
};
|
||||
|
||||
>>>>>>> 9a28e51f5 (rebased dev and merged)
|
||||
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
|
||||
{
|
||||
[ChainId.Mainnet]: [
|
||||
@@ -852,6 +868,7 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
|
||||
POLYGON_TOKENS.DAI,
|
||||
POLYGON_TOKENS.USDT,
|
||||
POLYGON_TOKENS.WBTC,
|
||||
POLYGON_TOKENS.nUSD,
|
||||
],
|
||||
[ChainId.Avalanche]: [
|
||||
AVALANCHE_TOKENS.WAVAX,
|
||||
@@ -915,10 +932,6 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
|
||||
builder
|
||||
.add(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY)
|
||||
.add(MAINNET_TOKENS.BTRFLY, MAINNET_TOKENS.OHMV2);
|
||||
// Lido
|
||||
builder
|
||||
.add(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH)
|
||||
.add(MAINNET_TOKENS.wstETH, MAINNET_TOKENS.stETH);
|
||||
})
|
||||
// Build
|
||||
.build(),
|
||||
@@ -974,8 +987,6 @@ export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
export const NATIVE_FEE_TOKEN_AMOUNT_BY_CHAIN_ID = valueByChainId(
|
||||
{ [ChainId.Mainnet]: ONE_ETHER.times(0.1) },
|
||||
ONE_ETHER,
|
||||
@@ -1953,20 +1964,6 @@ export const BISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const MDEX_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0x7dae51bd3e3376b8c7c4900e9107f12be3af1ba8',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const KNIGHTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0x05e61e0cdcd2170a76f9568a110cee3afdd6c46f',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId(
|
||||
{
|
||||
[ChainId.Mainnet]: ['0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643'],
|
||||
@@ -2054,20 +2051,6 @@ export const BANCOR_REGISTRY_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const BANCORV3_NETWORK_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0xeef417e1d5cc832e619ae18d2f140de2999dd4fb',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const BANCORV3_NETWORK_INFO_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0x8e303d296851b320e6a697bacb979d13c9d6e760',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const SHELL_POOLS_BY_CHAIN_ID = valueByChainId(
|
||||
{
|
||||
[ChainId.Mainnet]: {
|
||||
@@ -2143,13 +2126,11 @@ export const BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN = valueByChainId<string>(
|
||||
export const LIDO_INFO_BY_CHAIN = valueByChainId<LidoInfo>(
|
||||
{
|
||||
[ChainId.Mainnet]: {
|
||||
stEthToken: MAINNET_TOKENS.stETH,
|
||||
wstEthToken: MAINNET_TOKENS.wstETH,
|
||||
stEthToken: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84',
|
||||
wethToken: MAINNET_TOKENS.WETH,
|
||||
},
|
||||
},
|
||||
{
|
||||
wstEthToken: NULL_ADDRESS,
|
||||
stEthToken: NULL_ADDRESS,
|
||||
wethToken: NULL_ADDRESS,
|
||||
},
|
||||
@@ -2246,6 +2227,13 @@ export const APESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const CAFESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0x933daea3a5995fb94b14a7696a5f3ffd7b1e385a',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const CHEESESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0x3047799262d8d2ef41ed2a222205968bc9b0d895',
|
||||
@@ -2253,6 +2241,13 @@ export const CHEESESWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const JULSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0xbd67d157502a23309db761c41965600c2ec788b2',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
//
|
||||
// Polygon
|
||||
//
|
||||
@@ -2285,9 +2280,18 @@ export const WAULTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const MESHSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
export const POLYDEX_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Polygon]: '0x10f4a785f458bc144e3706575924889954946639',
|
||||
[ChainId.Polygon]: '0xe5c67ba380fb2f70a47b489e94bced486bb8fb74',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const JETSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0xbe65b8f75b9f20f4c522e0067a3887fada714800',
|
||||
[ChainId.Polygon]: '0x5c6ec38fb0e2609672bdf628b1fd605a523e5923',
|
||||
[ChainId.Fantom]: '0x845e76a8691423fbc4ecb8dd77556cb61c09ee25',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
@@ -2369,13 +2373,6 @@ export const YOSHI_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const VELODROME_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Optimism]: '0xa132dab612db5cb9fc9ac426a0cc215a3423f9c9',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
|
||||
{
|
||||
[ChainId.Mainnet]: [
|
||||
@@ -2392,7 +2389,9 @@ export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSo
|
||||
ERC20BridgeSource.BakerySwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.ApeSwap,
|
||||
ERC20BridgeSource.CafeSwap,
|
||||
ERC20BridgeSource.CheeseSwap,
|
||||
ERC20BridgeSource.JulSwap,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.Native,
|
||||
],
|
||||
@@ -2449,8 +2448,6 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.CryptoCom]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.ShibaSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.BiSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.MDex]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.KnightSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Balancer]: () => 120e3,
|
||||
[ERC20BridgeSource.BalancerV2]: (fillData?: FillData) => {
|
||||
return 100e3 + ((fillData as BalancerV2BatchSwapFillData).swapSteps.length - 1) * 50e3;
|
||||
@@ -2489,7 +2486,6 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
}
|
||||
return gas;
|
||||
},
|
||||
[ERC20BridgeSource.BancorV3]: () => 250e3, // revisit gas costs with wrap/unwrap
|
||||
[ERC20BridgeSource.KyberDmm]: (fillData?: FillData) => {
|
||||
let gas = 170e3;
|
||||
const path = (fillData as UniswapV2FillData).tokenAddressPath;
|
||||
@@ -2529,18 +2525,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
|
||||
return gas;
|
||||
},
|
||||
[ERC20BridgeSource.Lido]: (fillData?: FillData) => {
|
||||
const lidoFillData = fillData as LidoFillData;
|
||||
const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet];
|
||||
// WETH -> stETH
|
||||
if (lidoFillData.takerToken === wethAddress) {
|
||||
return 226e3;
|
||||
} else if (lidoFillData.takerToken === lidoFillData.stEthTokenAddress) {
|
||||
return 120e3;
|
||||
} else {
|
||||
return 95e3;
|
||||
}
|
||||
},
|
||||
[ERC20BridgeSource.Lido]: () => 226e3,
|
||||
[ERC20BridgeSource.AaveV2]: (fillData?: FillData) => {
|
||||
const aaveFillData = fillData as AaveV2FillData;
|
||||
// NOTE: The Aave deposit method is more expensive than the withdraw
|
||||
@@ -2568,7 +2553,9 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.PancakeSwapV2]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.BakerySwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.ApeSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.CafeSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.CheeseSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.JulSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.WaultSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.ACryptos]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
|
||||
@@ -2578,7 +2565,8 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.QuickSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.ComethSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Dfyn]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.MeshSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Polydex]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.JetSwap]: uniswapV2CloneGasSchedule,
|
||||
|
||||
//
|
||||
// Avalanche
|
||||
@@ -2601,16 +2589,10 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.SpookySwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Yoshi]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Beethovenx]: () => 100e3,
|
||||
|
||||
//
|
||||
// Optimism
|
||||
//
|
||||
[ERC20BridgeSource.Velodrome]: () => 160e3,
|
||||
};
|
||||
|
||||
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };
|
||||
|
||||
>>>>>>> e638268f9 (updated routing)
|
||||
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
|
||||
|
||||
// tslint:enable:custom-no-magic-numbers
|
||||
@@ -2623,6 +2605,10 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice
|
||||
includedSources: [],
|
||||
bridgeSlippage: 0.005,
|
||||
maxFallbackSlippage: 0.05,
|
||||
numSamples: 13,
|
||||
sampleDistributionBase: 1.05,
|
||||
feeSchedule: DEFAULT_FEE_SCHEDULE,
|
||||
gasSchedule: DEFAULT_GAS_SCHEDULE,
|
||||
exchangeProxyOverhead: () => ZERO_AMOUNT,
|
||||
allowFallback: true,
|
||||
shouldGenerateQuoteReport: true,
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
|
||||
import { NativeOrderWithFillableAmounts } from '../native_orders';
|
||||
import { MarketOperation } from '../../types';
|
||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
||||
|
||||
import { POSITIVE_INF, SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
|
||||
import { DexSample, ERC20BridgeSource, Fill, GenericBridgeFill, NativeOrderFill } from './types';
|
||||
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
|
||||
|
||||
// tslint:disable: prefer-for-of no-bitwise completed-docs
|
||||
|
||||
@@ -19,9 +18,12 @@ export function createFills(opts: {
|
||||
targetInput?: BigNumber;
|
||||
outputAmountPerEth?: BigNumber;
|
||||
inputAmountPerEth?: BigNumber;
|
||||
gasPrice: BigNumber;
|
||||
excludedSources?: ERC20BridgeSource[];
|
||||
feeSchedule?: FeeSchedule;
|
||||
}): Fill[][] {
|
||||
const { side } = opts;
|
||||
const excludedSources = opts.excludedSources || [];
|
||||
const feeSchedule = opts.feeSchedule || {};
|
||||
const orders = opts.orders || [];
|
||||
const dexQuotes = opts.dexQuotes || [];
|
||||
const outputAmountPerEth = opts.outputAmountPerEth || ZERO_AMOUNT;
|
||||
@@ -33,15 +35,15 @@ export function createFills(opts: {
|
||||
opts.targetInput,
|
||||
outputAmountPerEth,
|
||||
inputAmountPerEth,
|
||||
opts.gasPrice,
|
||||
feeSchedule,
|
||||
);
|
||||
// Create DEX fills.
|
||||
const dexFills = dexQuotes.map(singleSourceSamples =>
|
||||
dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, opts.gasPrice),
|
||||
dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, feeSchedule),
|
||||
);
|
||||
return [...dexFills, nativeFills]
|
||||
.map(p => clipFillsToInput(p, opts.targetInput))
|
||||
.filter(fills => hasLiquidity(fills));
|
||||
.filter(fills => hasLiquidity(fills) && !excludedSources.includes(fills[0].source));
|
||||
}
|
||||
|
||||
function clipFillsToInput(fills: Fill[], targetInput: BigNumber = POSITIVE_INF): Fill[] {
|
||||
@@ -93,35 +95,25 @@ export function nativeOrdersToFills(
|
||||
targetInput: BigNumber = POSITIVE_INF,
|
||||
outputAmountPerEth: BigNumber,
|
||||
inputAmountPerEth: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
fees: FeeSchedule,
|
||||
filterNegativeAdjustedRateOrders: boolean = true,
|
||||
): NativeOrderFill[] {
|
||||
if (orders.length === 0) {
|
||||
return [];
|
||||
}
|
||||
): Fill[] {
|
||||
const sourcePathId = hexUtils.random();
|
||||
// Create a single path from all orders.
|
||||
let fills: Array<NativeOrderFill & { adjustedRate: BigNumber }> = [];
|
||||
let fills: Array<Fill & { adjustedRate: BigNumber }> = [];
|
||||
for (const o of orders) {
|
||||
const { fillableTakerAmount, fillableMakerAmount, type } = o;
|
||||
// TODO(lawrence): handle taker fees.
|
||||
if (o.fillableTakerFeeAmount.gt(0)) {
|
||||
continue;
|
||||
}
|
||||
let input, output;
|
||||
if (side === MarketOperation.Sell) {
|
||||
input = fillableTakerAmount;
|
||||
output = fillableMakerAmount;
|
||||
} else {
|
||||
input = fillableMakerAmount;
|
||||
output = fillableTakerAmount;
|
||||
}
|
||||
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = o;
|
||||
const makerAmount = fillableMakerAmount;
|
||||
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
|
||||
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 = ethToOutputAmount({
|
||||
input,
|
||||
output,
|
||||
inputAmountPerEth,
|
||||
outputAmountPerEth,
|
||||
ethAmount: gasPrice.times(o.gasCost),
|
||||
ethAmount: fee,
|
||||
});
|
||||
// targetInput can be less than the order size
|
||||
// whilst the penalty is constant, it affects the adjusted output
|
||||
@@ -140,22 +132,17 @@ export function nativeOrdersToFills(
|
||||
continue;
|
||||
}
|
||||
fills.push({
|
||||
type,
|
||||
sourcePathId,
|
||||
adjustedOutput,
|
||||
adjustedRate,
|
||||
adjustedOutput,
|
||||
input: clippedInput,
|
||||
output: clippedOutput,
|
||||
flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
|
||||
index: 0, // TBD
|
||||
parent: undefined, // TBD
|
||||
source: ERC20BridgeSource.Native,
|
||||
gasCost: o.gasCost,
|
||||
data: {
|
||||
order: o.order,
|
||||
signature: o.signature,
|
||||
fillableTakerAmount: o.fillableTakerAmount,
|
||||
},
|
||||
type,
|
||||
fillData: { ...o },
|
||||
});
|
||||
}
|
||||
// Sort by descending adjusted rate.
|
||||
@@ -173,10 +160,10 @@ export function dexSamplesToFills(
|
||||
samples: DexSample[],
|
||||
outputAmountPerEth: BigNumber,
|
||||
inputAmountPerEth: BigNumber,
|
||||
gasPrice: BigNumber,
|
||||
): GenericBridgeFill[] {
|
||||
fees: FeeSchedule,
|
||||
): Fill[] {
|
||||
const sourcePathId = hexUtils.random();
|
||||
const fills: GenericBridgeFill[] = [];
|
||||
const fills: Fill[] = [];
|
||||
// Drop any non-zero entries. This can occur if the any fills on Kyber were UniswapReserves
|
||||
// We need not worry about Kyber fills going to UniswapReserve as the input amount
|
||||
// we fill is the same as we sampled. I.e we received [0,20,30] output from [1,2,3] input
|
||||
@@ -185,13 +172,12 @@ export function dexSamplesToFills(
|
||||
for (let i = 0; i < nonzeroSamples.length; i++) {
|
||||
const sample = nonzeroSamples[i];
|
||||
const prevSample = i === 0 ? undefined : nonzeroSamples[i - 1];
|
||||
const { source, encodedFillData, metadata } = sample;
|
||||
const { source, fillData } = sample;
|
||||
const input = sample.input.minus(prevSample ? prevSample.input : 0);
|
||||
const output = sample.output.minus(prevSample ? prevSample.output : 0);
|
||||
const fee = gasPrice.times(sample.gasCost);
|
||||
|
||||
let penalty = ZERO_AMOUNT;
|
||||
if (i === 0) {
|
||||
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
|
||||
// Only the first fill in a DEX path incurs a penalty.
|
||||
penalty = ethToOutputAmount({
|
||||
input,
|
||||
@@ -209,15 +195,11 @@ export function dexSamplesToFills(
|
||||
output,
|
||||
adjustedOutput,
|
||||
source,
|
||||
fillData,
|
||||
type: FillQuoteTransformerOrderType.Bridge,
|
||||
gasCost: sample.gasCost,
|
||||
index: i,
|
||||
parent: i !== 0 ? fills[fills.length - 1] : undefined,
|
||||
flags: SOURCE_FLAGS[source],
|
||||
data: {
|
||||
...metadata,
|
||||
encodedFillData,
|
||||
},
|
||||
});
|
||||
}
|
||||
return fills;
|
||||
|
@@ -0,0 +1,36 @@
|
||||
import { FANTOM_TOKENS, GEIST_FANTOM_POOLS } from './constants';
|
||||
import { GeistInfo } from './types';
|
||||
|
||||
const gTokenToUnderlyingToken = new Map<string, string>([
|
||||
[FANTOM_TOKENS.gFTM, FANTOM_TOKENS.WFTM],
|
||||
[FANTOM_TOKENS.gfUSDT, FANTOM_TOKENS.fUSDT],
|
||||
[FANTOM_TOKENS.gDAI, FANTOM_TOKENS.DAI],
|
||||
[FANTOM_TOKENS.gUSDC, FANTOM_TOKENS.USDC],
|
||||
[FANTOM_TOKENS.gETH, FANTOM_TOKENS.WETH],
|
||||
[FANTOM_TOKENS.gWBTC, FANTOM_TOKENS.WBTC],
|
||||
[FANTOM_TOKENS.gCRV, FANTOM_TOKENS.WCRV],
|
||||
[FANTOM_TOKENS.gMIM, FANTOM_TOKENS.MIM],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Returns GeistInfo for a certain pair if that pair exists on Geist
|
||||
*/
|
||||
export function getGeistInfoForPair(takerToken: string, makerToken: string): GeistInfo | undefined {
|
||||
let gToken;
|
||||
let underlyingToken;
|
||||
if (gTokenToUnderlyingToken.get(takerToken) === makerToken) {
|
||||
gToken = takerToken;
|
||||
underlyingToken = makerToken;
|
||||
} else if (gTokenToUnderlyingToken.get(makerToken) === takerToken) {
|
||||
gToken = makerToken;
|
||||
underlyingToken = takerToken;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
lendingPool: GEIST_FANTOM_POOLS.lendingPool,
|
||||
gToken,
|
||||
underlyingToken,
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
import { LiquidityProviderRegistry } from './types';
|
||||
|
||||
// tslint:disable completed-docs
|
||||
export function getLiquidityProvidersForPair(
|
||||
registry: LiquidityProviderRegistry,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
): Array<{ providerAddress: string; gasCost: number }> {
|
||||
return Object.entries(registry)
|
||||
.filter(([, plp]) => [makerToken, takerToken].every(t => plp.tokens.includes(t)))
|
||||
.map(([providerAddress]) => {
|
||||
let gasCost: number;
|
||||
if (typeof registry[providerAddress].gasCost === 'number') {
|
||||
gasCost = registry[providerAddress].gasCost as number;
|
||||
} else {
|
||||
gasCost = (registry[providerAddress].gasCost as (takerToken: string, makerToken: string) => number)(
|
||||
takerToken,
|
||||
makerToken,
|
||||
);
|
||||
}
|
||||
return {
|
||||
providerAddress,
|
||||
gasCost,
|
||||
};
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user