Compare commits

..

27 Commits

Author SHA1 Message Date
Github Actions
6324b08b4d Publish
- @0x/contracts-erc20@3.3.34
 - @0x/contracts-test-utils@5.4.25
 - @0x/contracts-treasury@1.4.17
 - @0x/contracts-utils@4.8.15
 - @0x/contracts-zero-ex@0.36.1
 - @0x/asset-swapper@16.66.0
 - @0x/contract-addresses@6.19.0
 - @0x/contract-wrappers@13.20.6
 - @0x/protocol-utils@11.16.1
2022-08-06 01:52:58 +00:00
Github Actions
fe73b63aaa Updated CHANGELOGS & MD docs 2022-08-06 01:52:55 +00:00
Kyu
192d0b17d9 Empty commit to trigger CI (something is wrong with Ido's account lol) 2022-08-06 10:34:15 +09:00
Ido Kleinman
aa74d04083 new contract address for goerli verified deploy (#537)
* new contract address for goerli verified deploy

* v6.18.0

* updated addresses for goerli + mumbai to verified contracts

* contract addresses changelog update

* unbump package version
2022-08-05 18:22:06 -07:00
Kyu
d586f5727d Rename Balancer pool cache files (#536) 2022-08-05 17:53:55 +09:00
Kyu
98fc79a085 Upgrade dependencies in asset-swapper to match the versions used in 0x-api [TKR-484] (#535)
* Upgrade dependencies to match the versions used in 0x-api

* Fix/disable tslint issues from the new version
2022-08-05 14:26:18 +09:00
eobbad
c12a10b96e Add WooFI support (#513)
* Add WooFI interface for sampling

* WooFi Sampler

* Add mixin

* Update bridge adapters

* fix some bugs

* update transformer_utils

* Add BSC support

* yarn prettier

* Capitalize WOOFI in bridge adapters

* Put rebateAddress in a constant, fixed some other stylistic errors

* bug fixes

* Updated CHANGELOGS & MD docs

* Publish

 - @0x/contracts-erc20@3.3.33
 - @0x/contracts-test-utils@5.4.24
 - @0x/contracts-treasury@1.4.16
 - @0x/contracts-utils@4.8.14
 - @0x/contracts-zero-ex@0.36.0
 - @0x/asset-swapper@16.64.0
 - @0x/contract-addresses@6.17.0
 - @0x/contract-wrappers@13.20.5
 - @0x/protocol-utils@11.16.0

* Update reference.mdx (#531)

Align with `DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE` value from c74e31c219/src/constants.ts (L26)

* code cleanup

* remove deusdc curve pool from this pr

* Refactor PoolsCache (part 1) [TKR-500] (#525)

* Make _refreshPoolCacheIfRequiredAsync type-safe and remove Promise.all

* Factor out PoolsCache key logic into a function

* Use Map instead of object in PoolsCache and increase the default timeout

* Clean up PoolsCache and simplify its public interface

* Refactor PoolsCache (part 2) [TKR-500]  (#526)

* Introduce NoOpPoolsCache and use it in unsupported chains for BeethovenX

* Use `NoOpPoolsCache` for `CreamPoolsCache` and `BalancerPoolsCache` on unsupported chains

* Remove `getBidAskLiquidityForMakerTakerAssetPairAsync` (#528)

* Add transfer approval for quote token for multi-hops and fix buy sampler typo

* Use 0x gas api instead of eth gas station api [TKR-502] (#532)

* Use 0x gas api instead of eth gas station api

* Add integration test for `ProtocolFeeUtils`

* Update CHANGELOG.json

* Add polygon, fantom, avalanche support

* yarn prettier

* Updated CHANGELOGS & MD docs

* Publish

 - @0x/asset-swapper@16.65.0

* Remove references to `custom-no-magic-numbers` ts lint rule [TKR-484] (#533)

* Remove references to `custom-no-magic-numbers ts` lint rule

* Run prettier

* resolve Kyu's comments

* remove fqt change

* Add WooFI interface for sampling

* WooFi Sampler

* Add mixin

* Update bridge adapters

* fix some bugs

* update transformer_utils

* Add BSC support

* yarn prettier

* Capitalize WOOFI in bridge adapters

* Put rebateAddress in a constant, fixed some other stylistic errors

* bug fixes

* code cleanup

* remove deusdc curve pool from this pr

* Add transfer approval for quote token for multi-hops and fix buy sampler typo

* Add polygon, fantom, avalanche support

* yarn prettier

* resolve Kyu's comments

* remove fqt change

* merge types.ts

* WOOFi -> WOOFI

* fix lerna run lint

* Changelog

* nit changes

Co-authored-by: Github Actions <github-actions@github.com>
Co-authored-by: Pavel <51318041+pavel-bc@users.noreply.github.com>
Co-authored-by: Kyu <kyuhyun217@gmail.com>
2022-08-03 15:54:09 -04:00
Kyu
d3d4a08f91 Remove references to custom-no-magic-numbers ts lint rule [TKR-484] (#533)
* Remove references to `custom-no-magic-numbers ts` lint rule

* Run prettier
2022-08-01 17:50:40 -07:00
Github Actions
9ce090c8cd Publish
- @0x/asset-swapper@16.65.0
2022-08-01 22:10:47 +00:00
Github Actions
980d60deb8 Updated CHANGELOGS & MD docs 2022-08-01 22:10:42 +00:00
Kyu
d6d79e51e7 Use 0x gas api instead of eth gas station api [TKR-502] (#532)
* Use 0x gas api instead of eth gas station api

* Add integration test for `ProtocolFeeUtils`

* Update CHANGELOG.json
2022-08-01 14:42:45 -07:00
Kyu
3ef5de93bb Remove getBidAskLiquidityForMakerTakerAssetPairAsync (#528) 2022-07-28 09:26:07 -07:00
Kyu
ab7dc33ca4 Refactor PoolsCache (part 2) [TKR-500] (#526)
* Introduce NoOpPoolsCache and use it in unsupported chains for BeethovenX

* Use `NoOpPoolsCache` for `CreamPoolsCache` and `BalancerPoolsCache` on unsupported chains
2022-07-28 09:12:02 -07:00
Kyu
14dcee5bb6 Refactor PoolsCache (part 1) [TKR-500] (#525)
* Make _refreshPoolCacheIfRequiredAsync type-safe and remove Promise.all

* Factor out PoolsCache key logic into a function

* Use Map instead of object in PoolsCache and increase the default timeout

* Clean up PoolsCache and simplify its public interface
2022-07-28 09:04:42 -07:00
Pavel
9856e78609 Update reference.mdx (#531)
Align with `DEFAULT_QUOTE_SLIPPAGE_PERCENTAGE` value from c74e31c219/src/constants.ts (L26)
2022-07-27 12:35:16 -07:00
Github Actions
2801b066b3 Publish
- @0x/contracts-erc20@3.3.33
 - @0x/contracts-test-utils@5.4.24
 - @0x/contracts-treasury@1.4.16
 - @0x/contracts-utils@4.8.14
 - @0x/contracts-zero-ex@0.36.0
 - @0x/asset-swapper@16.64.0
 - @0x/contract-addresses@6.17.0
 - @0x/contract-wrappers@13.20.5
 - @0x/protocol-utils@11.16.0
2022-07-27 19:32:19 +00:00
Github Actions
5bc8b13fc3 Updated CHANGELOGS & MD docs 2022-07-27 19:32:17 +00:00
Kyu
36dba8f5be Update FQT addresses on mainnet and Optimism (#530) 2022-07-27 09:56:50 -07:00
Kyu
ee2c069889 Remove Mooniswap on Ethereum Mainnet (#529)
* Remove Mooniswap from EthereumBridgeAdapter

* Remove Mooniswap sampling on mainnet

* Update CHANGELOG.json
2022-07-27 08:09:27 -07:00
Kyu
6ca14ed7b2 Fix Beethoven X Cache Issue [TKR-486] (#519)
* Replace Beethoven X subgraph url and add a test

* Simplify PoolsCacheMap type

* Make `BalancerV2PoolsCache` optional for Beethoven X

* Update CHANGELOG.json
2022-07-26 09:54:05 -07:00
Kyu
a5babb9a34 Add Synthetix Atomic Swap support [TKR-324] (#518)
* Implement SyntehtixSampler

* Implement MixinSynthetix

* Update transformer_utils.ts

* Add Synthetix mainnet support

* Add Synthetix optimism support

* Fine-tune gas schedule

* Pass read proxy dynamically to SynthetixSampler

* Pass read proxy dynamically to SynthetixMixin

* Fetch Synthetix address from sampler

* Pass Synthetix address directly to MixinSynthetix

* Update CHANGELOG.json
2022-07-25 21:17:27 -07:00
Kyu
661cc4669d Fix formatting 2022-07-22 16:25:31 -07:00
Ido Kleinman
553ba5c868 Add Goerli and Mumbai liquidity and support [TKR-493] (#523)
* deployment contract addresses on goerli

* add Goerli chainID and liquidity support for Sushi, Uni1,2,3

* UniV1 fix for Goerli

* add UniV3 to Mumbai

* remove comments

* lowercase addresses

* update contract-addresses package version

* reset package.json's states
2022-07-22 16:12:58 -07:00
Kyu
d03d2f254d Remove unused dependencies in asset-swapper [TKR-484] (#521)
* Remove unused dependencies

* Remove unused devDependencies
2022-07-19 16:44:40 -07:00
Kyu
feb91a04b0 Fix typos in GMX sampling logic (#520) 2022-07-19 16:18:37 -07:00
Kyu
4d63f33aba Update CODEOWNERS (#522) 2022-07-19 16:09:42 -07:00
Kyu
b72b8b5ffd Refactor TokenAdjacency and TokenAdjacencyBuilder [TKR-324] (#517)
* Add a new TokenAdjacencyGraph implementation

* Replace old TokenAdjacencyGraph with new implementation

* Simplify token adjacency graph in constants.ts

* Fix lint error

* Update CHANGELOG.json
2022-07-18 13:02:56 -07:00
86 changed files with 2677 additions and 804 deletions

View File

@@ -6,15 +6,15 @@
# https://git-scm.com/docs/gitignore#_pattern_format
packages/asset-swapper/ @dekz @mzhu25 @dextracker @kh-chang
packages/asset-swapper/ @dekz @dextracker @kyu-c
# Dev tools & setup
.circleci/ @dekz @mzhu25
packages/contract-addresses/ @dekz @mzhu25 @dextracker @kh-chang
packages/contract-artifacts/ @dekz @mzhu25
packages/protocol-utils/ @dekz @mzhu25
.circleci/ @dekz
packages/contract-addresses/ @dekz @dextracker @kyu-c
packages/contract-artifacts/ @dekz
packages/protocol-utils/ @dekz
# Protocol/smart contracts
contracts/ @dekz @mzhu25 @dextracker
contracts/ @dekz @dextracker

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1659750766,
"version": "3.3.34",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1658950329,
"version": "3.3.33",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1655244958,
"version": "3.3.32",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.34 - _August 6, 2022_
* Dependencies updated
## v3.3.33 - _July 27, 2022_
* Dependencies updated
## v3.3.32 - _June 14, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.32",
"version": "3.3.34",
"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.25",
"@0x/contracts-utils": "^4.8.15",
"@0x/dev-utils": "^4.2.14",
"@0x/sol-compiler": "^4.8.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1659750766,
"version": "5.4.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1658950329,
"version": "5.4.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1655244958,
"version": "5.4.23",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.25 - _August 6, 2022_
* Dependencies updated
## v5.4.24 - _July 27, 2022_
* Dependencies updated
## v5.4.23 - _June 14, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.23",
"version": "5.4.25",
"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.19.0",
"@0x/dev-utils": "^4.2.14",
"@0x/json-schemas": "^6.4.4",
"@0x/order-utils": "^10.4.28",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1659750766,
"version": "1.4.17",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1658950329,
"version": "1.4.16",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1655244958,
"version": "1.4.15",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.4.17 - _August 6, 2022_
* Dependencies updated
## v1.4.16 - _July 27, 2022_
* Dependencies updated
## v1.4.15 - _June 14, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.4.15",
"version": "1.4.17",
"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.19.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.32",
"@0x/contracts-erc20": "^3.3.34",
"@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.25",
"@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.16.1",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1659750766,
"version": "4.8.15",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1658950329,
"version": "4.8.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1655244958,
"version": "4.8.13",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.8.15 - _August 6, 2022_
* Dependencies updated
## v4.8.14 - _July 27, 2022_
* Dependencies updated
## v4.8.13 - _June 14, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.8.13",
"version": "4.8.15",
"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.25",
"@0x/dev-utils": "^4.2.14",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.8.1",

View File

@@ -1,4 +1,23 @@
[
{
"timestamp": 1659750766,
"version": "0.36.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.36.0",
"changes": [
{
"note": "Add Synthetix support in Ethereum and Optimism bridge adapters",
"pr": 518
}
],
"timestamp": 1658950329
},
{
"version": "0.35.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.36.1 - _August 6, 2022_
* Dependencies updated
## v0.36.0 - _July 27, 2022_
* Add Synthetix support in Ethereum and Optimism bridge adapters (#518)
## v0.35.0 - _June 14, 2022_
* Adds support for Velodrome OptimismBridgeAdapter (#494)

View File

@@ -313,7 +313,7 @@ contract FillQuoteTransformer is
if (success) {
results.makerTokenBoughtAmount = abi.decode(resultData, (uint256));
results.takerTokenSoldAmount = takerTokenFillAmount;
}
}
}
// Fill a single limit order.

View File

@@ -30,6 +30,7 @@ import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinPlatypus.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
contract AvalancheBridgeAdapter is
@@ -42,6 +43,7 @@ contract AvalancheBridgeAdapter is
MixinNerve,
MixinPlatypus,
MixinUniswapV2,
MixinWOOFi,
MixinZeroExBridge
{
constructor(IEtherTokenV06 weth)
@@ -120,6 +122,14 @@ contract AvalancheBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.WOOFI) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeWOOFi(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -29,6 +29,7 @@ import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
contract BSCBridgeAdapter is
@@ -40,6 +41,7 @@ contract BSCBridgeAdapter is
MixinMooniswap,
MixinNerve,
MixinUniswapV2,
MixinWOOFi,
MixinZeroExBridge
{
constructor(IEtherTokenV06 weth)
@@ -111,6 +113,14 @@ contract BSCBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.WOOFI) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeWOOFi(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -57,4 +57,6 @@ library BridgeProtocols {
uint128 internal constant PLATYPUS = 27;
uint128 internal constant BANCORV3 = 28;
uint128 internal constant VELODROME = 29;
uint128 internal constant SYNTHETIX = 30;
uint128 internal constant WOOFI = 31;
}

View File

@@ -37,10 +37,10 @@ import "./mixins/MixinDodoV2.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/MixinShell.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
@@ -63,10 +63,10 @@ contract EthereumBridgeAdapter is
MixinKyberDmm,
MixinLido,
MixinMakerPSM,
MixinMooniswap,
MixinMStable,
MixinNerve,
MixinShell,
MixinSynthetix,
MixinUniswap,
MixinUniswapV2,
MixinUniswapV3,
@@ -79,7 +79,6 @@ contract EthereumBridgeAdapter is
MixinCompound(weth)
MixinCurve(weth)
MixinLido(weth)
MixinMooniswap(weth)
MixinUniswap(weth)
{}
@@ -163,14 +162,6 @@ contract EthereumBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.MOONISWAP) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeMooniswap(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.MSTABLE) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeMStable(
@@ -260,6 +251,12 @@ contract EthereumBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeSynthetix(
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -28,6 +28,7 @@ import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
contract FantomBridgeAdapter is
@@ -38,6 +39,7 @@ contract FantomBridgeAdapter is
MixinCurveV2,
MixinNerve,
MixinUniswapV2,
MixinWOOFi,
MixinZeroExBridge
{
constructor(IEtherTokenV06 weth)
@@ -103,6 +105,14 @@ contract FantomBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.WOOFI) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeWOOFi(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -25,6 +25,7 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinSynthetix.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinVelodrome.sol";
import "./mixins/MixinZeroExBridge.sol";
@@ -34,6 +35,7 @@ contract OptimismBridgeAdapter is
MixinCurve,
MixinCurveV2,
MixinNerve,
MixinSynthetix,
MixinUniswapV3,
MixinVelodrome,
MixinZeroExBridge
@@ -93,6 +95,12 @@ contract OptimismBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeSynthetix(
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -34,6 +34,7 @@ import "./mixins/MixinMStable.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinWOOFi.sol";
import "./mixins/MixinZeroExBridge.sol";
contract PolygonBridgeAdapter is
@@ -50,6 +51,7 @@ contract PolygonBridgeAdapter is
MixinNerve,
MixinUniswapV2,
MixinUniswapV3,
MixinWOOFi,
MixinZeroExBridge
{
constructor(IEtherTokenV06 weth)
@@ -157,6 +159,14 @@ contract PolygonBridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.WOOFI) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeWOOFi(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(

View File

@@ -0,0 +1,99 @@
// 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;
interface ISynthetix {
// Ethereum Mainnet
function exchangeAtomically(
bytes32 sourceCurrencyKey,
uint256 sourceAmount,
bytes32 destinationCurrencyKey,
bytes32 trackingCode,
uint256 minAmount
) external returns (uint256 amountReceived);
// Optimism
function exchangeWithTracking(
bytes32 sourceCurrencyKey,
uint256 sourceAmount,
bytes32 destinationCurrencyKey,
address rewardAddress,
bytes32 trackingCode
) external returns (uint256 amountReceived);
}
contract MixinSynthetix {
address private constant rewardAddress =
0x5C80239D97E1eB216b5c3D8fBa5DE5Be5d38e4C9;
bytes32 constant trackingCode =
0x3058000000000000000000000000000000000000000000000000000000000000;
function _tradeSynthetix(uint256 sellAmount, bytes memory bridgeData)
public
returns (uint256 boughtAmount)
{
(
ISynthetix synthetix,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
) = abi.decode(
bridgeData,
(ISynthetix, bytes32, bytes32)
);
boughtAmount = exchange(
synthetix,
sourceCurrencyKey,
destinationCurrencyKey,
sellAmount
);
}
function exchange(
ISynthetix synthetix,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey,
uint256 sellAmount
) internal returns (uint256 boughtAmount) {
uint256 chainId;
assembly {
chainId := chainid()
}
if (chainId == 1) {
boughtAmount = synthetix.exchangeAtomically(
sourceCurrencyKey,
sellAmount,
destinationCurrencyKey,
trackingCode,
0
);
} else {
boughtAmount = synthetix.exchangeWithTracking(
sourceCurrencyKey,
sellAmount,
destinationCurrencyKey,
rewardAddress,
trackingCode
);
}
}
}

View File

@@ -0,0 +1,136 @@
// 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";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../IBridgeAdapter.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
/// @dev WooFI pool interface.
interface IWooPP {
function quoteToken() external view returns (address);
function sellBase(
address baseToken,
uint256 baseAmount,
uint256 minQuoteAmount,
address to,
address rebateTo
) external returns (uint256 quoteAmount);
function sellQuote(
address baseToken,
uint256 quoteAmount,
uint256 minBaseAmount,
address to,
address rebateTo
) external returns (uint256 baseAmount);
/// @dev Query the amount for selling the base token amount.
/// @param baseToken the base token to sell
/// @param baseAmount the amount to sell
/// @return quoteAmount the swapped quote amount
function querySellBase(
address baseToken,
uint256 baseAmount
) external view returns (uint256 quoteAmount);
}
contract MixinWOOFi{
using LibERC20TokenV06 for IERC20TokenV06;
using LibERC20TokenV06 for IEtherTokenV06;
using LibSafeMathV06 for uint256;
address constant rebateAddress = 0xBfdcBB4C05843163F491C24f9c0019c510786304;
// /// @dev Swaps an exact amount of input tokens for as many output tokens as possible.
// /// @param _amountIn Amount of input tokens to send
// /// @param _minAmountOut The minimum amount of output tokens that must be received for the transaction not to revert.
// /// @param _tokenIn Input token
// /// @param _tokenOut Output token
// /// @param _to recipient of tokens
// /// @param pool WOOFi pool where the swap will happen
function _tradeWOOFi(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
public
returns (uint256 boughtAmount)
{
(IWooPP _pool) = abi.decode(bridgeData, (IWooPP));
uint256 beforeBalance = buyToken.balanceOf(address(this));
sellToken.approveIfBelow(address(_pool), sellAmount);
_swap(
sellAmount,
address(sellToken),
address(buyToken),
_pool
);
boughtAmount = buyToken.balanceOf(address(this)).safeSub(beforeBalance);
}
function _swap(
uint _amountIn,
address _tokenIn,
address _tokenOut,
IWooPP pool
) internal {
address quoteToken = pool.quoteToken();
if (_tokenIn == quoteToken) {
pool.sellQuote(
_tokenOut,
_amountIn,
1,
address(this),
rebateAddress
);
} else if (_tokenOut == quoteToken) {
pool.sellBase(
_tokenIn,
_amountIn,
1,
address(this),
rebateAddress
);
} else {
uint256 quoteAmount = pool.sellBase(
_tokenIn,
_amountIn,
0,
address(this),
rebateAddress
);
IERC20TokenV06(pool.quoteToken()).approveIfBelow(address(pool), quoteAmount);
pool.sellQuote(
_tokenOut,
quoteAmount,
1,
address(this),
rebateAddress
);
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.35.0",
"version": "0.36.1",
"engines": {
"node": ">=6.12"
},
@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
"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/@(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|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinVelodrome|MixinWOOFi|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"
},
"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.19.0",
"@0x/contracts-erc20": "^3.3.34",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/contracts-test-utils": "^5.4.25",
"@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.16.1",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",

View File

@@ -124,10 +124,12 @@ import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
import * as MixinPlatypus from '../test/generated-artifacts/MixinPlatypus.json';
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
import * as MixinSynthetix from '../test/generated-artifacts/MixinSynthetix.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 MixinWOOFi from '../test/generated-artifacts/MixinWOOFi.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';
@@ -347,10 +349,12 @@ export const artifacts = {
MixinNerve: MixinNerve as ContractArtifact,
MixinPlatypus: MixinPlatypus as ContractArtifact,
MixinShell: MixinShell as ContractArtifact,
MixinSynthetix: MixinSynthetix as ContractArtifact,
MixinUniswap: MixinUniswap as ContractArtifact,
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
MixinVelodrome: MixinVelodrome as ContractArtifact,
MixinWOOFi: MixinWOOFi as ContractArtifact,
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
IERC1155Token: IERC1155Token as ContractArtifact,
IERC721Token: IERC721Token as ContractArtifact,

View File

@@ -122,10 +122,12 @@ export * from '../test/generated-wrappers/mixin_mooniswap';
export * from '../test/generated-wrappers/mixin_nerve';
export * from '../test/generated-wrappers/mixin_platypus';
export * from '../test/generated-wrappers/mixin_shell';
export * from '../test/generated-wrappers/mixin_synthetix';
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_w_o_o_fi';
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
export * from '../test/generated-wrappers/mooniswap_liquidity_provider';
export * from '../test/generated-wrappers/multiplex_feature';

View File

@@ -161,10 +161,12 @@
"test/generated-artifacts/MixinNerve.json",
"test/generated-artifacts/MixinPlatypus.json",
"test/generated-artifacts/MixinShell.json",
"test/generated-artifacts/MixinSynthetix.json",
"test/generated-artifacts/MixinUniswap.json",
"test/generated-artifacts/MixinUniswapV2.json",
"test/generated-artifacts/MixinUniswapV3.json",
"test/generated-artifacts/MixinVelodrome.json",
"test/generated-artifacts/MixinWOOFi.json",
"test/generated-artifacts/MixinZeroExBridge.json",
"test/generated-artifacts/MooniswapLiquidityProvider.json",
"test/generated-artifacts/MultiplexFeature.json",

View File

@@ -1,4 +1,46 @@
[
{
"version": "16.66.0",
"changes": [
{
"note": "Add WOOFi support",
"pr": 513
}
],
"timestamp": 1659750766
},
{
"version": "16.65.0",
"changes": [
{
"note": "Use 0x gas api instead of eth gas station api",
"pr": 532
}
],
"timestamp": 1659391840
},
{
"version": "16.64.0",
"changes": [
{
"note": "Refactor `TokenAdjacency` and `TokenAdjacencyBuilder`",
"pr": 517
},
{
"note": "Add Synthetix support`",
"pr": 518
},
{
"note": "Replace Beethoven X subgraph URL",
"pr": 519
},
{
"note": "Remove Mooniswap on Ethereum mainnet",
"pr": 529
}
],
"timestamp": 1658950329
},
{
"version": "16.63.1",
"changes": [

View File

@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v16.66.0 - _August 6, 2022_
* Add WOOFi support (#513)
## v16.65.0 - _August 1, 2022_
* Use 0x gas api instead of eth gas station api (#532)
## v16.64.0 - _July 27, 2022_
* Refactor `TokenAdjacency` and `TokenAdjacencyBuilder` (#517)
* Add Synthetix support` (#518)
* Replace Beethoven X subgraph URL (#519)
* Remove Mooniswap on Ethereum mainnet (#529)
## v16.63.1 - _July 12, 2022_
* Better error handling for balancer cache (#515)

View File

@@ -39,11 +39,13 @@ import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./PlatypusSampler.sol";
import "./ShellSampler.sol";
import "./SynthetixSampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
import "./UniswapV3Sampler.sol";
import "./VelodromeSampler.sol";
import "./WooPPSampler.sol";
import "./UtilitySampler.sol";
@@ -67,11 +69,13 @@ contract ERC20BridgeSampler is
NativeOrderSampler,
PlatypusSampler,
ShellSampler,
SynthetixSampler,
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,
UniswapV3Sampler,
VelodromeSampler,
WooPPSampler,
UtilitySampler
{

View File

@@ -0,0 +1,173 @@
// 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;
interface IReadProxyAddressResolver {
function target() external view returns (address);
}
interface IAddressResolver {
function getAddress(bytes32 name) external view returns (address);
}
interface IExchanger {
// Ethereum Mainnet
function getAmountsForAtomicExchange(
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint256 amountReceived,
uint256 fee,
uint256 exchangeFeeRate
);
// Optimism
function getAmountsForExchange(
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
)
external
view
returns (
uint256 amountReceived,
uint256 fee,
uint256 exchangeFeeRate
);
}
contract SynthetixSampler {
/// @dev Sample sell quotes from Synthetix Atomic Swap.
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order).
/// @return synthetix Synthetix address.
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
function sampleSellsFromSynthetix(
IReadProxyAddressResolver readProxy,
bytes32 takerTokenSymbol,
bytes32 makerTokenSymbol,
uint256[] memory takerTokenAmounts
) public view returns (address synthetix, uint256[] memory makerTokenAmounts) {
synthetix = getSynthetixAddress(readProxy);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (numSamples == 0) {
return (synthetix, makerTokenAmounts);
}
makerTokenAmounts[0] = exchange(
readProxy,
takerTokenAmounts[0],
takerTokenSymbol,
makerTokenSymbol
);
// Synthetix atomic swap has a fixed rate. Calculate the rest based on the first value (and save gas).
for (uint256 i = 1; i < numSamples; i++) {
makerTokenAmounts[i] =
(makerTokenAmounts[0] * takerTokenAmounts[i]) /
takerTokenAmounts[0];
}
}
/// @dev Sample buy quotes from Synthetix Atomic Swap.
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample (sorted in ascending order).
/// @return synthetix Synthetix address.
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
function sampleBuysFromSynthetix(
IReadProxyAddressResolver readProxy,
bytes32 takerTokenSymbol,
bytes32 makerTokenSymbol,
uint256[] memory makerTokenAmounts
) public view returns (address synthetix, uint256[] memory takerTokenAmounts) {
synthetix = getSynthetixAddress(readProxy);
// Since Synthetix atomic have a fixed rate, we can pick any reasonablely size takerTokenAmount (fixed to 1 ether here) and calculate the rest.
uint256 amountReceivedForEther = exchange(
readProxy,
1 ether,
takerTokenSymbol,
makerTokenSymbol
);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] =
(1 ether * makerTokenAmounts[i]) /
amountReceivedForEther;
}
}
function exchange(
IReadProxyAddressResolver readProxy,
uint256 sourceAmount,
bytes32 sourceCurrencyKey,
bytes32 destinationCurrencyKey
) private view returns (uint256 amountReceived) {
IExchanger exchanger = getExchanger(readProxy);
uint256 chainId;
assembly {
chainId := chainid()
}
if (chainId == 1) {
(amountReceived, , ) = exchanger.getAmountsForAtomicExchange(
sourceAmount,
sourceCurrencyKey,
destinationCurrencyKey
);
} else {
(amountReceived, , ) = exchanger.getAmountsForExchange(
sourceAmount,
sourceCurrencyKey,
destinationCurrencyKey
);
}
}
function getSynthetixAddress(IReadProxyAddressResolver readProxy)
private
view
returns (address)
{
return IAddressResolver(readProxy.target()).getAddress("Synthetix");
}
function getExchanger(IReadProxyAddressResolver readProxy)
private
view
returns (IExchanger)
{
return
IExchanger(
IAddressResolver(readProxy.target()).getAddress("Exchanger")
);
}
}

View File

@@ -0,0 +1,121 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./SamplerUtils.sol";
import "./ApproximateBuys.sol";
interface IWooPP {
/// @dev get the quote token address (immutable)
/// @return address of quote token
function quoteToken() external view returns (address);
/// @dev Query the amount for selling the base token amount.
/// @param baseToken the base token to sell
/// @param baseAmount the amount to sell
/// @return quoteAmount the swapped quote amount
function querySellBase(address baseToken, uint256 baseAmount) external view returns (uint256 quoteAmount);
/// @dev Query the amount for selling the quote token.
/// @param baseToken the base token to receive (buy)
/// @param quoteAmount the amount to sell
/// @return baseAmount the swapped base token amount
function querySellQuote(address baseToken, uint256 quoteAmount) external view returns (uint256 baseAmount);
}
contract WooPPSampler is SamplerUtils, ApproximateBuys{
function query(
uint amountIn,
address tokenIn,
address tokenOut,
address pool
) internal view returns (uint256 amountOut) {
if (amountIn == 0) {
return 0;
}
address quoteToken = IWooPP(pool).quoteToken();
if (tokenIn == quoteToken) {
amountOut = IWooPP(pool).querySellQuote(tokenOut, amountIn);
} else if (tokenOut == quoteToken) {
amountOut = IWooPP(pool).querySellBase(tokenIn, amountIn);
} else {
uint quoteAmount = IWooPP(pool).querySellBase(tokenIn, amountIn);
amountOut = IWooPP(pool).querySellQuote(tokenOut, quoteAmount);
}
}
/// @dev Sample sell quotes from WooFI.
/// @param pool Address of the pool we are sampling from
/// @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 makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromWooPP(
address pool,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = query(takerTokenAmounts[i], takerToken, makerToken, pool);
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from WooFI.
/// @param pool Address of the pool we are sampling from
/// @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 (sorted in ascending order).
/// @return takerTokenAmounts Taker amounts bought at each taker token
/// amount.
function sampleBuysFromWooPP(
address pool,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
takerTokenData: abi.encode(pool,takerToken, makerToken),
makerTokenData: abi.encode(pool, makerToken, takerToken),
getSellQuoteCallback: _sampleSellForApproximateBuyFromWoofi
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromWoofi(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
) internal view returns (uint256) {
(address _pool, address _takerToken, address _makerToken) = abi.decode(takerTokenData, (address, address, address));
(bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromWooPP.selector,
_pool,
_takerToken,
_makerToken,
_toSingleValueArray(sellAmount)
));
if(!success) {
return 0;
}
return abi.decode(resultData, (uint256[]))[0];
}
}

View File

@@ -1897,7 +1897,7 @@ ___
# Interface: SwapQuoteRequestOpts
slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.01 (1%).
gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.63.1",
"version": "16.66.0",
"engines": {
"node": ">=6.12"
},
@@ -40,7 +40,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"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|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).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|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler|WooPPSampler).json",
"postpublish": {
"assets": []
}
@@ -61,17 +61,16 @@
"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.19.0",
"@0x/contract-wrappers": "^13.20.6",
"@0x/contracts-erc20": "^3.3.34",
"@0x/contracts-zero-ex": "^0.36.1",
"@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/quote-server": "^6.0.6",
"@0x/protocol-utils": "^11.16.1",
"@0x/quote-server": "^8.0.0",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"@balancer-labs/sdk": "0.1.6",
@@ -85,30 +84,23 @@
"axios-mock-adapter": "^1.19.0",
"balancer-labs-sor-v1": "npm:@balancer-labs/sor@0.3.2",
"cream-sor": "^0.3.3",
"decimal.js": "^10.2.0",
"ethereum-types": "^3.7.0",
"ethereumjs-util": "^7.0.10",
"fast-abi": "^0.0.4",
"graphql": "^15.4.0",
"graphql-request": "^3.4.0",
"heartbeats": "^5.0.1",
"lodash": "^4.17.11"
"lodash": "^4.17.15",
"msw": "^0.44.2"
},
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-libs": "^4.3.37",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/contracts-utils": "^4.8.13",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/contracts-test-utils": "^5.4.25",
"@0x/sol-compiler": "^4.8.1",
"@0x/subproviders": "^6.6.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.6",
"@types/lodash": "4.14.104",
"@types/lodash": "4.14.137",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
"chai": "^4.0.1",
@@ -116,12 +108,11 @@
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"gitpkg": "https://github.com/0xProject/gitpkg.git",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"nyc": "^11.0.1",
"shx": "^0.2.2",
"tslint": "5.11.0",
"tslint": "^6.1.3",
"typedoc": "~0.16.11",
"typemoq": "^2.1.0",
"typescript": "4.6.3"

View File

@@ -19,7 +19,7 @@ import {
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';
const ZERO_EX_GAS_API_URL = 'https://gas.api.0x.org/source/median';
const NULL_BYTES = '0x';
const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
@@ -48,7 +48,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
orderRefreshIntervalMs: 10000, // 10 seconds
...DEFAULT_ORDER_PRUNER_OPTS,
samplerGasLimit: 500e6,
ethGasStationUrl: ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: ZERO_EX_GAS_API_URL,
rfqt: {
integratorsWhitelist: [],
makerAssetOfferings: {},
@@ -95,11 +95,10 @@ export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_opera
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
// tslint:disable-next-line: custom-no-magic-numbers
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
export const constants = {
ETH_GAS_STATION_API_URL,
ZERO_EX_GAS_API_URL,
PROTOCOL_FEE_MULTIPLIER,
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
NULL_BYTES,

View File

@@ -157,8 +157,6 @@ export {
GetMarketOrdersRfqOpts,
LiquidityProviderFillData,
LiquidityProviderRegistry,
MarketDepth,
MarketDepthSide,
MooniswapFillData,
MultiHopFillData,
NativeRfqOrderFillData,
@@ -166,9 +164,10 @@ export {
NativeFillData,
OptimizedMarketOrder,
SourceQuoteOperation,
TokenAdjacencyGraph,
UniswapV2FillData,
} from './utils/market_operation_utils/types';
export { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from './utils/token_adjacency_graph';
export { IdentityFillAdjustor } from './utils/market_operation_utils/identity_fill_adjustor';
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
export {

View File

@@ -32,16 +32,13 @@ import {
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,
FinalUniswapV3FillData,
LiquidityProviderFillData,
MooniswapFillData,
NativeRfqOrderFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
@@ -64,7 +61,6 @@ import {
requiresTransformERC20,
} from './quote_consumer_utils';
// tslint:disable-next-line:custom-no-magic-numbers
const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
const { NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;
@@ -307,30 +303,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const fillData = slippedOrders[0].fillData as MooniswapFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
NULL_ADDRESS,
sellAmount,
minBuyAmount,
poolEncoder.encode([fillData.poolAddress]),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxy,
gasOverhead: ZERO_AMOUNT,
};
}
// RFQT VIP
if (
[ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&

View File

@@ -36,9 +36,6 @@ import {
FillData,
GasSchedule,
GetMarketOrdersOpts,
MarketDepth,
MarketDepthSide,
MarketSideLiquidity,
OptimizedMarketOrder,
OptimizerResultWithReport,
} from './utils/market_operation_utils/types';
@@ -112,7 +109,7 @@ export class SwapQuoter {
};
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
options.ethGasStationUrl,
options.zeroExGasApiUrl,
);
// Allow the sampler bytecode to be overwritten using geths override functionality
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
@@ -228,67 +225,6 @@ export class SwapQuoter {
return batchSwapQuotes.filter(x => x !== undefined) as MarketBuySwapQuote[];
}
/**
* Returns the bids and asks liquidity for the entire market.
* For certain sources (like AMM's) it is recommended to provide a practical maximum takerAssetAmount.
* @param makerTokenAddress The address of the maker asset
* @param takerTokenAddress The address of the taker asset
* @param takerAssetAmount The amount to sell and buy for the bids and asks.
*
* @return An object that conforms to MarketDepth that contains all of the samples and liquidity
* information for the source.
*/
public async getBidAskLiquidityForMakerTakerAssetPairAsync(
makerToken: string,
takerToken: string,
takerAssetAmount: BigNumber,
options: Partial<SwapQuoteRequestOpts> = {},
): Promise<MarketDepth> {
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,
};
}
/**
* Returns the recommended gas price for a fast transaction
*/

View File

@@ -17,11 +17,13 @@ import {
GetMarketOrdersOpts,
LiquidityProviderRegistry,
OptimizedMarketOrder,
TokenAdjacencyGraph,
} 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';
import { TokenAdjacencyGraph } from './utils/token_adjacency_graph';
export { SamplerMetrics } from './utils/market_operation_utils/types';
export type Address = string;
/**
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
@@ -335,7 +337,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
contractAddresses?: AssetSwapperContractAddresses;
samplerGasLimit?: number;
multiBridgeAddress?: string;
ethGasStationUrl?: string;
zeroExGasApiUrl?: string;
rfqt?: SwapQuoterRfqOpts;
samplerOverrides?: SamplerOverrides;
tokenAdjacencyGraph?: TokenAdjacencyGraph;

View File

@@ -104,11 +104,9 @@ function parseIndicativeQuoteResponseFromAltMM(
takerAmount,
// HACK: alt implementation does not return an expiration with indicative quotes
// return now + { IMPUTED EXPIRY SECONDS } to have it included after order checks
expiry:
// tslint:disable-next-line:custom-no-magic-numbers
new BigNumber(Date.now() / 1000)
.integerValue(BigNumber.ROUND_DOWN)
.plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS),
expiry: new BigNumber(Date.now() / 1000)
.integerValue(BigNumber.ROUND_DOWN)
.plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS),
};
}
@@ -243,7 +241,6 @@ export async function returnQuoteFromAltMMAsync<ResponseT>(
// empty response will get filtered out in validation
const emptyResponse = {};
// tslint:disable-next-line:custom-no-magic-numbers
if (response.status !== SUCCESS_CODE) {
const rejectedRequestInfo = {
status: response.status,

View File

@@ -40,7 +40,6 @@ interface Cache {
[key: string]: AaveReserve[];
}
// tslint:disable-next-line:custom-no-magic-numbers
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**

View File

@@ -33,8 +33,8 @@ import {
MSTABLE_POOLS_BY_CHAIN_ID,
NERVE_BSC_INFOS,
NULL_ADDRESS,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
PANGOLIN_ROUTER_BY_CHAIN_ID,
PLATYPUS_AVALANCHE_INFOS,
QUICKSWAP_ROUTER_BY_CHAIN_ID,

View File

@@ -19,7 +19,6 @@ interface Cache {
[key: string]: CToken;
}
// tslint:disable-next-line:custom-no-magic-numbers
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**

View File

@@ -1,9 +1,9 @@
import { ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { formatBytes32String } from '@ethersproject/strings';
import { formatBytes32String, parseBytes32String } from '@ethersproject/strings';
import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
import { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from '../token_adjacency_graph';
import { IdentityFillAdjustor } from './identity_fill_adjustor';
import { SourceFilters } from './source_filters';
@@ -32,12 +32,13 @@ import {
MultiHopFillData,
PlatypusInfo,
PsmInfo,
TokenAdjacencyGraph,
SynthetixFillData,
UniswapV2FillData,
UniswapV3FillData,
WOOFiFillData,
} from './types';
// tslint:disable: custom-no-magic-numbers no-bitwise
// tslint:disable: no-bitwise
export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04';
@@ -60,6 +61,7 @@ function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue:
[ChainId.Mainnet]: defaultValue,
[ChainId.Ropsten]: defaultValue,
[ChainId.Rinkeby]: defaultValue,
[ChainId.Goerli]: defaultValue,
[ChainId.Kovan]: defaultValue,
[ChainId.Ganache]: defaultValue,
[ChainId.BSC]: defaultValue,
@@ -88,7 +90,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Bancor,
ERC20BridgeSource.BancorV3,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.MultiHop,
@@ -107,6 +108,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.Synthetix,
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound,
@@ -118,10 +120,17 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Mooniswap,
]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Goerli]: new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.PolygonMumbai]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.BSC]: new SourceFilters([
ERC20BridgeSource.BakerySwap,
@@ -146,6 +155,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.BiSwap,
ERC20BridgeSource.MDex,
ERC20BridgeSource.KnightSwap,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Polygon]: new SourceFilters([
ERC20BridgeSource.SushiSwap,
@@ -168,6 +178,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.MeshSwap,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -181,6 +192,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Synapse,
ERC20BridgeSource.GMX,
ERC20BridgeSource.Platypus,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -194,6 +206,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.Yoshi,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Celo]: new SourceFilters([
ERC20BridgeSource.UbeSwap,
@@ -208,6 +221,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Velodrome,
ERC20BridgeSource.Synthetix,
]),
},
new SourceFilters([]),
@@ -228,7 +242,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
ERC20BridgeSource.BancorV3,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Shell,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
@@ -247,6 +260,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.Synthetix,
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound,
@@ -258,8 +272,15 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Mooniswap,
]),
[ChainId.Goerli]: new SourceFilters([
ERC20BridgeSource.Native,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.PolygonMumbai]: new SourceFilters([ERC20BridgeSource.Native, ERC20BridgeSource.UniswapV3]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Ganache]: new SourceFilters([ERC20BridgeSource.Native]),
@@ -286,6 +307,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.BiSwap,
ERC20BridgeSource.MDex,
ERC20BridgeSource.KnightSwap,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Polygon]: new SourceFilters([
ERC20BridgeSource.SushiSwap,
@@ -308,6 +330,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.MeshSwap,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -321,6 +344,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Synapse,
ERC20BridgeSource.GMX,
ERC20BridgeSource.Platypus,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -334,6 +358,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Synapse,
ERC20BridgeSource.Yoshi,
ERC20BridgeSource.WOOFi,
]),
[ChainId.Celo]: new SourceFilters([
ERC20BridgeSource.UbeSwap,
@@ -348,6 +373,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Velodrome,
ERC20BridgeSource.Synthetix,
]),
},
new SourceFilters([]),
@@ -366,6 +392,8 @@ export const FEE_QUOTE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>
[ChainId.Mainnet]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3],
[ChainId.BSC]: [ERC20BridgeSource.PancakeSwap, ERC20BridgeSource.Mooniswap, ERC20BridgeSource.SushiSwap],
[ChainId.Ropsten]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap],
[ChainId.Goerli]: [ERC20BridgeSource.UniswapV2, ERC20BridgeSource.SushiSwap],
[ChainId.PolygonMumbai]: [ERC20BridgeSource.UniswapV3],
[ChainId.Polygon]: [ERC20BridgeSource.QuickSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3],
[ChainId.Avalanche]: [ERC20BridgeSource.Pangolin, ERC20BridgeSource.TraderJoe, ERC20BridgeSource.SushiSwap],
[ChainId.Fantom]: [ERC20BridgeSource.SpiritSwap, ERC20BridgeSource.SpookySwap, ERC20BridgeSource.SushiSwap],
@@ -442,6 +470,11 @@ export const MAINNET_TOKENS = {
EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8',
sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620',
sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb',
sJPY: '0xf6b1c627e95bfc3c1b4c9b825a032ff0fbf3e07d',
sGBP: '0x97fe22e7341a0cd8db6f6c021a24dc8f4dad855f',
sAUD: '0xf48e200eaf9906362bb1442fca31e0835773b8b4',
sKRW: '0x269895a3df4d73b077fc823dd6da1b95f72aaf9b',
sCHF: '0x0f83287ff768d1c1e17a42f44d644d7f22e8ee1d',
stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84',
wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0',
LINK: '0x514910771af9ca656af840dff83e8264ecf986ca',
@@ -518,6 +551,7 @@ export const BSC_TOKENS = {
pBTC: '0xed28a457a5a76596ac48d87c0f577020f6ea1c4c',
nUSD: '0x23b891e5c62e0955ae2bd185990103928ab817b3',
BSW: '0x965F527D9159dCe6288a2219DB51fc6Eef120dD1',
WOO: '0x4691937a7508860f876c9c0a2a617e7d9e945d4b',
};
export const POLYGON_TOKENS = {
@@ -537,6 +571,7 @@ export const POLYGON_TOKENS = {
WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb',
nUSD: '0xb6c473756050de474286bed418b77aeac39b02af',
ANY: '0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8',
WOO: '0x1b815d120b3ef02039ee11dc2d33de7aa4a8c603',
};
export const AVALANCHE_TOKENS = {
@@ -563,6 +598,7 @@ export const AVALANCHE_TOKENS = {
UST: '0xb599c3590f42f8f995ecfa0f85d2980b76862fc1',
FRAX: '0xd24c2ad096400b6fbcd2ad8b24e7acbc21a1da64',
YUSD: '0x111111111111ed1d73f860f57b2798b683f2d325',
WOO: '0xabc9547b534519ff73921b1fba6e672b5f58d083',
};
export const CELO_TOKENS = {
@@ -622,10 +658,7 @@ export const FANTOM_TOKENS = {
gWBTC: '0x38aca5484b8603373acc6961ecd57a6a594510a3',
gCRV: '0x690754a168b022331caa2467207c61919b3f8a98',
gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe',
};
export const GEIST_FANTOM_POOLS = {
lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068',
WOO: '0x6626c47c00f1d87902fc13eecfac3ed06d5e8d8a',
};
export const OPTIMISM_TOKENS = {
@@ -636,6 +669,21 @@ export const OPTIMISM_TOKENS = {
WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095',
nETH: '0x809dc529f07651bd43a172e8db6f4a7a0d771036',
sWETH: '0x121ab82b49b2bc4c7901ca46b8277962b4350204',
// Synthetix synths:
sAAVE: '0x00b8d5a5e1ac97cb4341c4bc4367443c8776e8d9',
sAVAX: '0xb2b42b231c68cbb0b4bf2ffebf57782fd97d3da4',
sBTC: '0x298b9b95708152ff6968aafd889c6586e9169f1d',
sETH: '0xe405de8f52ba7559f9df3c368500b6e6ae6cee49',
sEUR: '0xfbc4198702e81ae77c06d58f81b629bdf36f0a71',
sLINK: '0xc5db22719a06418028a40a9b5e9a7c02959d0d08',
sMATIC: '0x81ddfac111913d3d5218dea999216323b7cd6356',
sSOL: '0x8b2f7ae8ca8ee8428b6d76de88326bb413db2766',
sUNI: '0xf5a6115aa582fd1beea22bc93b7dc7a785f60d03',
sUSD: '0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9',
};
export const GEIST_FANTOM_POOLS = {
lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068',
};
export const CURVE_POOLS = {
@@ -829,6 +877,16 @@ export const PLATYPUS_AVALANCHE_POOLS = {
sAVAX: '0x4658ea7e9960d6158a261104aaa160cc953bb6ba',
};
export const WOOFI_POOL_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.BSC]: '0xbf365ce9cfcb2d5855521985e351ba3bcf77fd3f',
[ChainId.Fantom]: '0x9503e7517d3c5bc4f9e4a1c6ae4f8b33ac2546f2',
[ChainId.Avalanche]: '0x1df3009c57a8b143c6246149f00b090bce3b8f88',
[ChainId.Polygon]: '0x7400b665c8f4f3a951a99f1ee9872efb8778723d',
},
NULL_ADDRESS,
);
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
{
[ChainId.Mainnet]: [
@@ -852,6 +910,15 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
'0xad6d458402f60fd3bd25163575031acdce07538d', // DAI
'0x07865c6e87b9f70255377e024ace6630c1eaa37f', // USDC
],
[ChainId.Goerli]: [
getContractAddressesForChainOrThrow(ChainId.Goerli).etherToken,
'0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844', // DAI
'0x07865c6E87B9F70255377e024ace6630C1Eaa37F', // USDC
],
[ChainId.PolygonMumbai]: [
getContractAddressesForChainOrThrow(ChainId.PolygonMumbai).etherToken,
'0xe6b8a5CF854791412c1f6EFC7CAf629f5Df1c747', // USDC
],
[ChainId.Polygon]: [
POLYGON_TOKENS.WMATIC,
POLYGON_TOKENS.WETH,
@@ -903,65 +970,74 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
// attaching to a default intermediary token (stables or ETH etc) can have a large impact
export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdjacencyGraph>(
{
[ChainId.Mainnet]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Mainnet],
})
[ChainId.Mainnet]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Mainnet])
.tap(builder => {
// Mirror Protocol
builder.add(MAINNET_TOKENS.MIR, MAINNET_TOKENS.UST);
// Convex and Curve
builder.add(MAINNET_TOKENS.cvxCRV, MAINNET_TOKENS.CRV).add(MAINNET_TOKENS.CRV, MAINNET_TOKENS.cvxCRV);
builder.addBidirectional(MAINNET_TOKENS.cvxCRV, MAINNET_TOKENS.CRV);
// Convex and FXS
builder.add(MAINNET_TOKENS.cvxFXS, MAINNET_TOKENS.FXS).add(MAINNET_TOKENS.FXS, MAINNET_TOKENS.cvxFXS);
builder.addBidirectional(MAINNET_TOKENS.cvxFXS, MAINNET_TOKENS.FXS);
// FEI TRIBE liquid in UniV2
builder.add(MAINNET_TOKENS.FEI, MAINNET_TOKENS.TRIBE).add(MAINNET_TOKENS.TRIBE, MAINNET_TOKENS.FEI);
builder.addBidirectional(MAINNET_TOKENS.FEI, MAINNET_TOKENS.TRIBE);
// FRAX ecosystem
builder.add(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.FXS).add(MAINNET_TOKENS.FXS, MAINNET_TOKENS.FRAX);
builder.add(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.OHM).add(MAINNET_TOKENS.OHM, MAINNET_TOKENS.FRAX);
builder.addBidirectional(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.FXS);
builder.addBidirectional(MAINNET_TOKENS.FRAX, MAINNET_TOKENS.OHM);
// REDACTED CARTEL
builder
.add(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY)
.add(MAINNET_TOKENS.BTRFLY, MAINNET_TOKENS.OHMV2);
builder.addBidirectional(MAINNET_TOKENS.OHMV2, MAINNET_TOKENS.BTRFLY);
// Lido
builder
.add(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH)
.add(MAINNET_TOKENS.wstETH, MAINNET_TOKENS.stETH);
builder.addBidirectional(MAINNET_TOKENS.stETH, MAINNET_TOKENS.wstETH);
// Synthetix Atomic Swap
builder.addCompleteSubgraph([
MAINNET_TOKENS.sBTC,
MAINNET_TOKENS.sETH,
MAINNET_TOKENS.sUSD,
MAINNET_TOKENS.sEUR,
MAINNET_TOKENS.sJPY,
MAINNET_TOKENS.sGBP,
MAINNET_TOKENS.sAUD,
MAINNET_TOKENS.sKRW,
MAINNET_TOKENS.sCHF,
]);
})
// Build
.build(),
[ChainId.BSC]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC],
}).build(),
[ChainId.Polygon]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon],
})
[ChainId.BSC]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.BSC]).build(),
[ChainId.Polygon]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon])
.tap(builder => {
builder.add(POLYGON_TOKENS.QUICK, POLYGON_TOKENS.ANY).add(POLYGON_TOKENS.ANY, POLYGON_TOKENS.QUICK);
builder.addBidirectional(POLYGON_TOKENS.QUICK, POLYGON_TOKENS.ANY);
})
.build(),
[ChainId.Avalanche]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Avalanche],
})
[ChainId.Avalanche]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Avalanche])
.tap(builder => {
// Synapse nETH/aWETH pool
builder
.add(AVALANCHE_TOKENS.aWETH, AVALANCHE_TOKENS.nETH)
.add(AVALANCHE_TOKENS.nETH, AVALANCHE_TOKENS.aWETH);
builder.addBidirectional(AVALANCHE_TOKENS.aWETH, AVALANCHE_TOKENS.nETH);
// Trader Joe MAG/MIM pool
builder.add(AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.MAG).add(AVALANCHE_TOKENS.MAG, AVALANCHE_TOKENS.MIM);
builder.addBidirectional(AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.MAG);
})
.build(),
[ChainId.Fantom]: new TokenAdjacencyGraphBuilder(
DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Fantom],
).build(),
[ChainId.Celo]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Celo]).build(),
[ChainId.Optimism]: new TokenAdjacencyGraphBuilder(DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Optimism])
.tap(builder => {
// Synthetix Atomic Swap
builder.addCompleteSubgraph([
OPTIMISM_TOKENS.sAAVE,
OPTIMISM_TOKENS.sAVAX,
OPTIMISM_TOKENS.sBTC,
OPTIMISM_TOKENS.sETH,
OPTIMISM_TOKENS.sEUR,
OPTIMISM_TOKENS.sLINK,
OPTIMISM_TOKENS.sMATIC,
OPTIMISM_TOKENS.sSOL,
OPTIMISM_TOKENS.sUNI,
OPTIMISM_TOKENS.sUSD,
]);
})
.build(),
[ChainId.Fantom]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Fantom],
}).build(),
[ChainId.Celo]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Celo],
}).build(),
[ChainId.Optimism]: new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Optimism],
}).build(),
},
new TokenAdjacencyGraphBuilder({ default: [] }).build(),
TokenAdjacencyGraph.getEmptyGraph(),
);
export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
@@ -970,6 +1046,8 @@ export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
[ChainId.BSC]: getContractAddressesForChainOrThrow(ChainId.BSC).etherToken,
[ChainId.Ganache]: getContractAddressesForChainOrThrow(ChainId.Ganache).etherToken,
[ChainId.Ropsten]: getContractAddressesForChainOrThrow(ChainId.Ropsten).etherToken,
[ChainId.Goerli]: getContractAddressesForChainOrThrow(ChainId.Goerli).etherToken,
[ChainId.PolygonMumbai]: getContractAddressesForChainOrThrow(ChainId.PolygonMumbai).etherToken,
[ChainId.Rinkeby]: getContractAddressesForChainOrThrow(ChainId.Rinkeby).etherToken,
[ChainId.Kovan]: getContractAddressesForChainOrThrow(ChainId.Kovan).etherToken,
[ChainId.Polygon]: getContractAddressesForChainOrThrow(ChainId.Polygon).etherToken,
@@ -1833,6 +1911,7 @@ export const UNISWAPV1_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xc0a47dfe034b400b47bdad5fecda2621de6c4d95',
[ChainId.Ropsten]: '0x9c83dce8ca20e9aaf9d3efc003b2ea62abc08351',
[ChainId.Goerli]: '0x6Ce570d02D73d4c384b46135E87f8C592A8c86dA',
},
NULL_ADDRESS,
);
@@ -1841,6 +1920,7 @@ export const UNISWAPV2_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
[ChainId.Ropsten]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
[ChainId.Goerli]: '0xf164fc0ec4e93095b804a4795bbe1e041497b92a',
},
NULL_ADDRESS,
);
@@ -1850,6 +1930,7 @@ export const SUSHISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
[ChainId.Mainnet]: '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f',
[ChainId.BSC]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Ropsten]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Goerli]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Polygon]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Avalanche]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
[ChainId.Fantom]: '0x1b02da8cb0d097eb8d57a175b88c7d8b47997506',
@@ -1941,7 +2022,6 @@ export const KNIGHTSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId(
{
[ChainId.Mainnet]: ['0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643'],
[ChainId.BSC]: ['0xd41b24bba51fac0e4827b6f94c0d6ddeb183cd64'],
},
[] as string[],
@@ -2011,14 +2091,6 @@ export const MAKER_PSM_INFO_BY_CHAIN_ID = valueByChainId<PsmInfo>(
},
);
export const MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xa2033d6ba88756ce6a87584d69dc87bda9a4f889',
[ChainId.Ropsten]: '0x87e0393aee0fb8c10b8653c6507c182264fe5a34',
},
NULL_ADDRESS,
);
export const BANCOR_REGISTRY_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x52Ae12ABe5D8BD778BD5397F99cA900624CfADD4',
@@ -2127,7 +2199,6 @@ export const LIDO_INFO_BY_CHAIN = valueByChainId<LidoInfo>(
},
);
export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
@@ -2139,13 +2210,6 @@ export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId(
null,
);
export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
{
[ChainId.Fantom]: 'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx',
},
'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx',
);
export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
{
[ChainId.Mainnet]: {
@@ -2156,6 +2220,14 @@ export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
[ChainId.Goerli]: {
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
[ChainId.PolygonMumbai]: {
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
[ChainId.Polygon]: {
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
@@ -2341,6 +2413,47 @@ export const VELODROME_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
NULL_ADDRESS,
);
export const SYNTHETIX_READ_PROXY_BY_CHAIN_ID = valueByChainId<string>(
{
[ChainId.Mainnet]: '0x4e3b31eb0e5cb73641ee1e65e7dcefe520ba3ef2',
[ChainId.Optimism]: '0x1cb059b7e74fd21665968c908806143e744d5f30',
},
NULL_ADDRESS,
);
export const SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID = valueByChainId<Map<string, string>>(
{
// There is no easy way to find out what synths are supported on mainnet.
// The below list is based on https://sips.synthetix.io/sccp/sccp-190.
[ChainId.Mainnet]: new Map([
[MAINNET_TOKENS.sAUD, 'sAUD'],
[MAINNET_TOKENS.sBTC, 'sBTC'],
[MAINNET_TOKENS.sCHF, 'sCHF'],
[MAINNET_TOKENS.sETH, 'sETH'],
[MAINNET_TOKENS.sEUR, 'sEUR'],
[MAINNET_TOKENS.sGBP, 'sGBP'],
[MAINNET_TOKENS.sJPY, 'sJPY'],
[MAINNET_TOKENS.sKRW, 'sKRW'],
[MAINNET_TOKENS.sUSD, 'sUSD'],
]),
// Supported assets can be find through SynthUtil::synthsRates.
// Low liquidity tokens can be excluded.
[ChainId.Optimism]: new Map([
[OPTIMISM_TOKENS.sAAVE, 'sAAVE'],
[OPTIMISM_TOKENS.sAVAX, 'sAVAX'],
[OPTIMISM_TOKENS.sBTC, 'sBTC'],
[OPTIMISM_TOKENS.sETH, 'sETH'],
[OPTIMISM_TOKENS.sEUR, 'sEUR'],
[OPTIMISM_TOKENS.sLINK, 'sLINK'],
[OPTIMISM_TOKENS.sMATIC, 'sMATIC'],
[OPTIMISM_TOKENS.sSOL, 'sSOL'],
[OPTIMISM_TOKENS.sUNI, 'sUNI'],
[OPTIMISM_TOKENS.sUSD, 'sUSD'],
]),
},
new Map(),
);
export const VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID = valueByChainId<ERC20BridgeSource[]>(
{
[ChainId.Mainnet]: [
@@ -2382,7 +2495,6 @@ const uniswapV2CloneGasSchedule = (fillData?: FillData) => {
* I.e remove the overhead cost of ExchangeProxy (130k) and
* the ethereum transaction cost (21k)
*/
// tslint:disable:custom-no-magic-numbers
export const DEFAULT_GAS_SCHEDULE: Required<GasSchedule> = {
[ERC20BridgeSource.Native]: fillData => {
// TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder
@@ -2524,7 +2636,28 @@ export const DEFAULT_GAS_SCHEDULE: Required<GasSchedule> = {
return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3;
}
},
[ERC20BridgeSource.Synthetix]: (fillData?: FillData) => {
const { chainId, makerTokenSymbolBytes32, takerTokenSymbolBytes32 } = fillData as SynthetixFillData;
const makerTokenSymbol = parseBytes32String(makerTokenSymbolBytes32);
const takerTokenSymbol = parseBytes32String(takerTokenSymbolBytes32);
// Gas cost widely varies by token on mainnet.
if (chainId === ChainId.Mainnet) {
if (takerTokenSymbol === 'sBTC' || makerTokenSymbol === 'sBTC') {
return 800e3;
}
if (takerTokenSymbol === 'sETH' || makerTokenSymbol === 'sETH') {
return 700e3;
}
return 580e3;
}
// Optimism
if (takerTokenSymbol === 'sUSD' || makerTokenSymbol === 'sUSD') {
return 480e3;
}
return 580e3;
},
//
// BSC
//
@@ -2535,7 +2668,19 @@ export const DEFAULT_GAS_SCHEDULE: Required<GasSchedule> = {
[ERC20BridgeSource.CheeseSwap]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.WaultSwap]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.ACryptos]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.WOOFi]: (fillData?: FillData) => {
const woofiFillData = fillData as WOOFiFillData;
const quoteTokenAddresses = [BSC_TOKENS.USDT, AVALANCHE_TOKENS.nUSDC, FANTOM_TOKENS.USDC, POLYGON_TOKENS.USDC];
if (
quoteTokenAddresses.includes(woofiFillData.takerToken) ||
quoteTokenAddresses.includes(woofiFillData.makerToken)
) {
return 500e3;
} else {
return 100e4;
}
},
//
// Polygon
//
@@ -2586,10 +2731,7 @@ export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
export const DEFAULT_FEE_ESTIMATE = { gas: 0, fee: ZERO_AMOUNT };
// tslint:enable:custom-no-magic-numbers
export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice'> = {
// tslint:disable-next-line: custom-no-magic-numbers
runLimit: 2 ** 15,
excludedSources: [],
excludedFeeSources: [],
@@ -2604,7 +2746,7 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice
allowFallback: true,
shouldGenerateQuoteReport: true,
shouldIncludePriceComparisonsReport: false,
tokenAdjacencyGraph: { default: [] },
tokenAdjacencyGraph: TokenAdjacencyGraph.getEmptyGraph(),
neonRouterNumSamples: 14,
fillAdjustor: new IdentityFillAdjustor(),
};

View File

@@ -29,7 +29,6 @@ import {
PriceComparisonsReport,
QuoteReport,
} from './../quote_report_generator';
import { getComparisonPrices } from './comparison_price';
import {
BUY_SOURCE_FILTER_BY_CHAIN_ID,
@@ -862,14 +861,9 @@ export class MarketOperationUtils {
}
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
void Promise.all(
Object.values(this._sampler.poolsCaches).map(async cache => {
if (!cache || cache.isFresh(takerToken, makerToken)) {
return Promise.resolve([]);
}
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
}),
);
_.values(this._sampler.poolsCaches)
.filter(cache => cache !== undefined && !cache.isFresh(takerToken, makerToken))
.forEach(cache => cache?.getFreshPoolsForPairAsync(takerToken, makerToken));
}
}

View File

@@ -12,26 +12,8 @@ import {
FillAdjustor,
MarketSideLiquidity,
MultiHopFillData,
TokenAdjacencyGraph,
} from './types';
/**
* Given a token pair, returns the intermediate tokens to consider for two-hop routes.
*/
export function getIntermediateTokens(
makerToken: string,
takerToken: string,
tokenAdjacencyGraph: TokenAdjacencyGraph,
): string[] {
const intermediateTokens = _.union(
_.get(tokenAdjacencyGraph, takerToken, tokenAdjacencyGraph.default),
_.get(tokenAdjacencyGraph, makerToken, tokenAdjacencyGraph.default),
);
return _.uniqBy(intermediateTokens, a => a.toLowerCase()).filter(
token => token.toLowerCase() !== makerToken.toLowerCase() && token.toLowerCase() !== takerToken.toLowerCase(),
);
}
/**
* Returns the best two-hop quote and the fee-adjusted rate of that quote.
*/

View File

@@ -38,10 +38,12 @@ import {
OrderDomain,
PlatypusFillData,
ShellFillData,
SynthetixFillData,
UniswapV2FillData,
UniswapV3FillData,
UniswapV3PathAmount,
VelodromeFillData,
WOOFiFillData,
} from './types';
// tslint:disable completed-docs
@@ -210,6 +212,10 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.BancorV3, 'BancorV3');
case ERC20BridgeSource.Velodrome:
return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome');
case ERC20BridgeSource.Synthetix:
return encodeBridgeSourceId(BridgeProtocol.Synthetix, 'Synthetix');
case ERC20BridgeSource.WOOFi:
return encodeBridgeSourceId(BridgeProtocol.WOOFi, 'WOOFi');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -391,6 +397,18 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const velodromeFillData = (order as OptimizedMarketBridgeOrder<VelodromeFillData>).fillData;
bridgeData = encoder.encode([velodromeFillData.router, velodromeFillData.stable]);
break;
case ERC20BridgeSource.Synthetix:
const fillData = (order as OptimizedMarketBridgeOrder<SynthetixFillData>).fillData;
bridgeData = encoder.encode([
fillData.synthetix,
fillData.takerTokenSymbolBytes32,
fillData.makerTokenSymbolBytes32,
]);
break;
case ERC20BridgeSource.WOOFi:
const woofiFillData = (order as OptimizedMarketBridgeOrder<WOOFiFillData>).fillData;
bridgeData = encoder.encode([woofiFillData.poolAddress]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -517,6 +535,8 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'),
[ERC20BridgeSource.Synthetix]: AbiEncoder.create('(address,bytes32,bytes32)'),
[ERC20BridgeSource.WOOFi]: AbiEncoder.create('(address)'),
};
function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -14,7 +14,7 @@ import { dexSampleToFill, ethToOutputAmount, nativeOrderToFill } from './fills';
import { Path, PathPenaltyOpts } from './path';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillAdjustor, FillData, SamplerMetrics } from './types';
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
// tslint:disable: prefer-for-of completed-docs no-bitwise
// NOTE: The Rust router will panic with less than 3 samples
const MIN_NUM_SAMPLE_INPUTS = 3;

View File

@@ -1,16 +1,19 @@
import { ChainId } from '@0x/contract-addresses';
import { getPoolsWithTokens, parsePoolData } from 'balancer-labs-sor-v1';
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { gql, request } from 'graphql-request';
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
import { LogFunction } from '../../../types';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
import { NoOpPoolsCache } from './no_op_pools_cache';
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
// tslint:disable:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
// tslint:enable:custom-no-magic-numbers
// tslint:disable: member-ordering
const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
interface BalancerPoolResponse {
id: string;
@@ -20,10 +23,18 @@ interface BalancerPoolResponse {
totalWeight: string;
}
export class BalancerPoolsCache extends PoolsCache {
constructor(
export class BalancerPoolsCache extends AbstractPoolsCache {
public static create(chainId: ChainId): PoolsCache {
if (chainId !== ChainId.Mainnet) {
return new NoOpPoolsCache();
}
return new BalancerPoolsCache();
}
private constructor(
private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL,
cache: { [key: string]: CacheValue } = {},
cache: Map<string, CacheValue> = new Map(),
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,

View File

@@ -6,16 +6,18 @@ import { gql, request } from 'graphql-request';
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
import { LogFunction } from '../../../types';
import {
BALANCER_MAX_POOLS_FETCHED,
BALANCER_TOP_POOLS_FETCHED,
BALANCER_V2_SUBGRAPH_URL_BY_CHAIN,
} from '../constants';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
import { parsePoolData } from './balancer_sor_v2';
import { CacheValue, PoolsCache } from './pools_cache';
import { NoOpPoolsCache } from './no_op_pools_cache';
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
// tslint:disable: member-ordering
const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = new Map<ChainId, string>([
[ChainId.Fantom, 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx'],
]);
// tslint:disable-next-line:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
interface BalancerPoolResponse {
@@ -28,7 +30,16 @@ interface BalancerPoolResponse {
amp: string | null;
}
export class BalancerV2PoolsCache extends PoolsCache {
export class BalancerV2PoolsCache extends AbstractPoolsCache {
public static createBeethovenXPoolCache(chainId: ChainId): PoolsCache {
const subgraphUrl = BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN.get(chainId);
if (subgraphUrl === undefined) {
return new NoOpPoolsCache();
}
return new BalancerV2PoolsCache(subgraphUrl);
}
private static _parseSubgraphPoolData(pool: any, takerToken: string, makerToken: string): Pool {
const tToken = pool.tokens.find((t: any) => t.address === takerToken);
const mToken = pool.tokens.find((t: any) => t.address === makerToken);
@@ -49,13 +60,12 @@ export class BalancerV2PoolsCache extends PoolsCache {
};
}
constructor(
chainId: ChainId,
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId]!,
private constructor(
private readonly subgraphUrl: string,
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
cache: { [key: string]: CacheValue } = {},
cache: Map<string, CacheValue> = new Map(),
) {
super(cache);
void this._loadTopPoolsAsync();
@@ -63,19 +73,6 @@ export class BalancerV2PoolsCache extends PoolsCache {
setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2);
}
// protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
// try {
// const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
// // Sort by maker token balance (descending)
// const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) =>
// b.balanceOut.minus(a.balanceOut).toNumber(),
// );
// return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools;
// } catch (err) {
// return [];
// }
// }
protected async _fetchTopPoolsAsync(): Promise<BalancerPoolResponse[]> {
const query = gql`
query fetchTopPools($topPoolsFetched: Int!) {

View File

@@ -20,7 +20,6 @@ import { BalancerSwapInfo, BalancerSwaps } from '../types';
import { CacheValue, EMPTY_BALANCER_SWAPS, SwapInfoCache } from './pair_swaps_cache';
import { SubgraphPoolDataService } from './sgPoolDataService';
// tslint:disable-next-line:custom-no-magic-numbers
const ONE_DAY_MS = 24 * 60 * 60 * ONE_SECOND_MS;
export interface BalancerPoolResponse {

View File

@@ -1,18 +1,20 @@
import { ChainId } from '@0x/contract-addresses';
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
import { NoOpPoolsCache } from './no_op_pools_cache';
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
export class CreamPoolsCache extends PoolsCache {
constructor(
_cache: { [key: string]: CacheValue } = {},
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {
super(_cache);
export class CreamPoolsCache extends AbstractPoolsCache {
public static create(chainId: ChainId): PoolsCache {
if (chainId !== ChainId.Mainnet) {
return new NoOpPoolsCache();
}
return new CreamPoolsCache();
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
try {
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
@@ -25,4 +27,10 @@ export class CreamPoolsCache extends PoolsCache {
return [];
}
}
private constructor(
_cache: Map<string, CacheValue> = new Map(),
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {
super(_cache);
}
}

View File

@@ -1,4 +1,4 @@
export { BalancerPoolsCache } from './balancer_utils';
export { BalancerV2PoolsCache } from './balancer_v2_utils';
export { CreamPoolsCache } from './cream_utils';
export { PoolsCache } from './pools_cache';
export { BalancerPoolsCache } from './balancer_pools_cache';
export { BalancerV2PoolsCache } from './balancer_v2_pools_cache';
export { CreamPoolsCache } from './cream_pools_cache';
export { AbstractPoolsCache, PoolsCache } from './pools_cache';

View File

@@ -0,0 +1,21 @@
import { Pool, PoolsCache } from './pools_cache';
// tslint:disable:prefer-function-over-method
export class NoOpPoolsCache implements PoolsCache {
public async getFreshPoolsForPairAsync(
_takerToken: string,
_makerToken: string,
_timeoutMs?: number | undefined,
): Promise<Pool[]> {
return [];
}
public getPoolAddressesForPair(_takerToken: string, _makerToken: string): string[] {
return [];
}
public isFresh(_takerToken: string, _makerToken: string): boolean {
return true;
}
}

View File

@@ -1,18 +1,15 @@
import { BalancerSwaps } from '../types';
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
import { BalancerSwaps } from '../types';
export interface CacheValue {
expiresAt: number;
balancerSwaps: BalancerSwaps;
}
// tslint:disable:custom-no-magic-numbers
// Cache results for 30mins
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
const DEFAULT_TIMEOUT_MS = ONE_SECOND_MS;
export const EMPTY_BALANCER_SWAPS = { swapInfoExactIn: [], swapInfoExactOut: [] };
// tslint:enable:custom-no-magic-numbers
/**
* Caches SwapInfo for a pair of tokens.

View File

@@ -7,18 +7,30 @@ export interface CacheValue {
pools: Pool[];
}
// tslint:disable:custom-no-magic-numbers
// Cache results for 30mins
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
const DEFAULT_TIMEOUT_MS = 1000;
// tslint:enable:custom-no-magic-numbers
const DEFAULT_TIMEOUT_MS = 3000;
export abstract class PoolsCache {
protected static _isExpired(value: CacheValue): boolean {
export interface PoolsCache {
getFreshPoolsForPairAsync(takerToken: string, makerToken: string, timeoutMs?: number): Promise<Pool[]>;
getPoolAddressesForPair(takerToken: string, makerToken: string): string[];
isFresh(takerToken: string, makerToken: string): boolean;
}
export abstract class AbstractPoolsCache implements PoolsCache {
protected static _getKey(takerToken: string, makerToken: string): string {
return `${takerToken}-${makerToken}`;
}
protected static _isExpired(value: CacheValue | undefined): boolean {
if (value === undefined) {
return true;
}
return Date.now() >= value.expiresAt;
}
constructor(
protected readonly _cache: { [key: string]: CacheValue },
protected readonly _cache: Map<string, CacheValue>,
protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS,
) {}
@@ -31,47 +43,42 @@ export abstract class PoolsCache {
return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]);
}
public getCachedPoolAddressesForPair(
takerToken: string,
makerToken: string,
ignoreExpired: boolean = true,
): string[] | undefined {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (ignoreExpired) {
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
if (!value) {
return undefined;
}
if (PoolsCache._isExpired(value)) {
return undefined;
}
return (value || []).pools.map(pool => pool.id);
/**
* Returns pool addresses (can be stale) for a pair.
*
* An empty array will be returned if cache does not exist.
*/
public getPoolAddressesForPair(takerToken: string, makerToken: string): string[] {
const value = this._getValue(takerToken, makerToken);
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
public isFresh(takerToken: string, makerToken: string): boolean {
const cached = this.getCachedPoolAddressesForPair(takerToken, makerToken, false);
return cached !== undefined;
const value = this._getValue(takerToken, makerToken);
return !AbstractPoolsCache._isExpired(value);
}
protected _getValue(takerToken: string, makerToken: string): CacheValue | undefined {
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
return this._cache.get(key);
}
protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (value === undefined || value.expiresAt >= Date.now()) {
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
const expiresAt = Date.now() + this._cacheTimeMs;
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
const value = this._cache.get(key);
if (!AbstractPoolsCache._isExpired(value)) {
return value!.pools;
}
return this._cache[key].pools;
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
const expiresAt = Date.now() + this._cacheTimeMs;
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
return pools;
}
protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void {
const key = JSON.stringify([takerToken, makerToken]);
this._cache[key] = {
pools,
expiresAt,
};
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
this._cache.set(key, { pools, expiresAt });
}
protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]>;

View File

@@ -3,10 +3,11 @@ import { BigNumber, NULL_BYTES } from '@0x/utils';
import { SamplerOverrides } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { TokenAdjacencyGraph } from '../token_adjacency_graph';
import { BancorService } from './bancor_service';
import { PoolsCacheMap, SamplerOperations } from './sampler_operations';
import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
import { BatchedOperation, LiquidityProviderRegistry } from './types';
/**
* Generate sample amounts up to `maxFillAmount`.

View File

@@ -1,12 +1,14 @@
import { ChainId } from '@0x/contract-addresses';
import { LimitOrderFields } from '@0x/protocol-utils';
import { BigNumber, logUtils } from '@0x/utils';
import { formatBytes32String } from '@ethersproject/strings';
import * as _ from 'lodash';
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
import { GeistSampler } from '../../noop_samplers/GeistSampler';
import { SamplerCallResult, SignedNativeOrder } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { TokenAdjacencyGraph } from '../token_adjacency_graph';
import { AaveV2ReservesCache } from './aave_reserves_cache';
import { BancorService } from './bancor_service';
@@ -24,10 +26,9 @@ import {
AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID,
AVALANCHE_TOKENS,
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
BANCORV3_NETWORK_BY_CHAIN_ID,
BANCORV3_NETWORK_INFO_BY_CHAIN_ID,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN,
COMPOUND_API_URL_BY_CHAIN_ID,
DODOV1_CONFIG_BY_CHAIN_ID,
@@ -46,16 +47,18 @@ import {
NULL_ADDRESS,
PLATYPUS_ROUTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID,
SYNTHETIX_READ_PROXY_BY_CHAIN_ID,
UNISWAPV1_ROUTER_BY_CHAIN_ID,
UNISWAPV3_CONFIG_BY_CHAIN_ID,
VELODROME_ROUTER_BY_CHAIN_ID,
WOOFI_POOL_BY_CHAIN_ID,
ZERO_AMOUNT,
} from './constants';
import { getGeistInfoForPair } from './geist_utils';
import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { getIntermediateTokens } from './multihop_utils';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache';
import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_utils_new';
import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_swap_info_cache';
import { SamplerContractOperation } from './sampler_contract_operation';
import { SamplerNoOperation } from './sampler_no_operation';
import { SourceFilters } from './source_filters';
@@ -93,11 +96,11 @@ import {
PsmInfo,
ShellFillData,
SourceQuoteOperation,
SourcesWithPoolsCache,
TokenAdjacencyGraph,
SynthetixFillData,
UniswapV2FillData,
UniswapV3FillData,
VelodromeFillData,
WOOFiFillData,
} from './types';
/**
@@ -112,9 +115,12 @@ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([
*/
export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]);
export type PoolsCacheMap = { [key in Exclude<SourcesWithPoolsCache, ERC20BridgeSource.BalancerV2>]: PoolsCache } & {
export interface PoolsCacheMap {
[ERC20BridgeSource.Balancer]: PoolsCache;
[ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache | undefined;
};
[ERC20BridgeSource.Beethovenx]: PoolsCache;
[ERC20BridgeSource.Cream]: PoolsCache;
}
// tslint:disable:no-inferred-empty-object-type no-unbound-method
@@ -140,7 +146,7 @@ export class SamplerOperations {
public readonly chainId: ChainId,
protected readonly _samplerContract: ERC20BridgeSamplerContract,
poolsCaches?: PoolsCacheMap,
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = TokenAdjacencyGraph.getEmptyGraph(),
liquidityProviderRegistry: LiquidityProviderRegistry = {},
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
) {
@@ -151,12 +157,9 @@ export class SamplerOperations {
this.poolsCaches = poolsCaches
? poolsCaches
: {
[ERC20BridgeSource.Beethovenx]: new BalancerV2PoolsCache(
chainId,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN[chainId],
),
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
[ERC20BridgeSource.Beethovenx]: BalancerV2PoolsCache.createBeethovenXPoolCache(chainId),
[ERC20BridgeSource.Balancer]: BalancerPoolsCache.create(chainId),
[ERC20BridgeSource.Cream]: CreamPoolsCache.create(chainId),
[ERC20BridgeSource.BalancerV2]:
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[chainId] === NULL_ADDRESS
? undefined
@@ -810,7 +813,7 @@ export class SamplerOperations {
if (_sources.length === 0) {
return SamplerOperations.constant([]);
}
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken);
const subOps = intermediateTokens.map(intermediateToken => {
const firstHopOps = this._getSellQuoteOperations(_sources, intermediateToken, takerToken, [ZERO_AMOUNT]);
const secondHopOps = this._getSellQuoteOperations(_sources, makerToken, intermediateToken, [ZERO_AMOUNT]);
@@ -865,7 +868,7 @@ export class SamplerOperations {
if (_sources.length === 0) {
return SamplerOperations.constant([]);
}
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken);
const subOps = intermediateTokens.map(intermediateToken => {
const firstHopOps = this._getBuyQuoteOperations(_sources, intermediateToken, takerToken, [
new BigNumber(0),
@@ -1310,6 +1313,89 @@ export class SamplerOperations {
});
}
public getSynthetixSellQuotes(
readProxy: string,
takerTokenSymbol: string,
makerTokenSymbol: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<SynthetixFillData> {
const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol);
const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol);
return new SamplerContractOperation({
source: ERC20BridgeSource.Synthetix,
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromSynthetix,
params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, takerFillAmounts],
callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => {
const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleSellsFromSynthetix',
callResults,
);
fillData.synthetix = synthetix;
fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32;
fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32;
fillData.chainId = this.chainId;
return samples;
},
});
}
public getSynthetixBuyQuotes(
readProxy: string,
takerTokenSymbol: string,
makerTokenSymbol: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<SynthetixFillData> {
const takerTokenSymbolBytes32 = formatBytes32String(takerTokenSymbol);
const makerTokenSymbolBytes32 = formatBytes32String(makerTokenSymbol);
return new SamplerContractOperation({
source: ERC20BridgeSource.Synthetix,
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromSynthetix,
params: [readProxy, takerTokenSymbolBytes32, makerTokenSymbolBytes32, makerFillAmounts],
callback: (callResults: string, fillData: SynthetixFillData): BigNumber[] => {
const [synthetix, samples] = this._samplerContract.getABIDecodedReturnData<[string, BigNumber[]]>(
'sampleBuysFromSynthetix',
callResults,
);
fillData.synthetix = synthetix;
fillData.takerTokenSymbolBytes32 = takerTokenSymbolBytes32;
fillData.makerTokenSymbolBytes32 = makerTokenSymbolBytes32;
fillData.chainId = this.chainId;
return samples;
},
});
}
public getWOOFiSellQuotes(
poolAddress: string,
takerToken: string,
makerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<WOOFiFillData> {
return new SamplerContractOperation({
fillData: { poolAddress, takerToken, makerToken },
source: ERC20BridgeSource.WOOFi,
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromWooPP,
params: [poolAddress, takerToken, makerToken, makerFillAmounts],
});
}
public getWOOFiBuyQuotes(
poolAddress: string,
takerToken: string,
makerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<WOOFiFillData> {
return new SamplerContractOperation({
fillData: { poolAddress, takerToken, makerToken },
source: ERC20BridgeSource.WOOFi,
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromWooPP,
params: [poolAddress, takerToken, makerToken, makerFillAmounts],
});
}
/**
* Returns the best price for the native token
* Best is calculated according to the fee schedule, so the price of the
@@ -1325,9 +1411,13 @@ export class SamplerOperations {
if (makerToken.toLowerCase() === nativeToken.toLowerCase()) {
return SamplerOperations.constant(new BigNumber(1));
}
const subOps = this._getSellQuoteOperations(sources, makerToken, nativeToken, [nativeFillAmount], {
default: [],
});
const subOps = this._getSellQuoteOperations(
sources,
makerToken,
nativeToken,
[nativeFillAmount],
TokenAdjacencyGraph.getEmptyGraph(),
);
return this._createBatch(
subOps,
(samples: BigNumber[][]) => {
@@ -1423,7 +1513,7 @@ export class SamplerOperations {
): SourceQuoteOperation[] {
// Find the adjacent tokens in the provided token adjacency graph,
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph);
const intermediateTokens = tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken);
// Drop out MultiHop and Native as we do not query those here.
const _sources = SELL_SOURCE_FILTER_BY_CHAIN_ID[this.chainId]
.exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native])
@@ -1533,20 +1623,17 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(balancerPool =>
this.getBalancerSellQuotes(
balancerPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
return this.poolsCaches[ERC20BridgeSource.Balancer]
.getPoolAddressesForPair(takerToken, makerToken)
.map(balancerPool =>
this.getBalancerSellQuotes(
balancerPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
case ERC20BridgeSource.BalancerV2: {
const cache = this.poolsCaches[source];
if (!cache) {
@@ -1564,16 +1651,15 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Beethovenx: {
const poolIds =
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const cache = this.poolsCaches[source];
const poolAddresses = cache.getPoolAddressesForPair(takerToken, makerToken);
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
return poolIds.map(poolId =>
return poolAddresses.map(poolAddress =>
this.getBalancerV2SellQuotes(
{ poolId, vault },
{ poolId: poolAddress, vault },
makerToken,
takerToken,
takerFillAmounts,
@@ -1582,20 +1668,17 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Cream:
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(creamPool =>
this.getBalancerSellQuotes(
creamPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return this.poolsCaches[ERC20BridgeSource.Cream]
.getPoolAddressesForPair(takerToken, makerToken)
.map(creamPool =>
this.getBalancerSellQuotes(
creamPool,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1695,8 +1778,8 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.GMX: {
// low liquidity mim pool dont quote
if (takerToken === AVALANCHE_TOKENS.MIM || makerToken === 'AVALANCHE_TOKENS.MIM') {
// MIM has no liquidity.
if (takerToken === AVALANCHE_TOKENS.MIM || makerToken === AVALANCHE_TOKENS.MIM) {
return [];
}
return this.getGMXSellQuotes(
@@ -1733,6 +1816,29 @@ export class SamplerOperations {
takerFillAmounts,
);
}
case ERC20BridgeSource.Synthetix: {
const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId];
const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId];
const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase());
const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase());
if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) {
return [];
}
return this.getSynthetixSellQuotes(
readProxy,
takerTokenSymbol,
makerTokenSymbol,
takerFillAmounts,
);
}
case ERC20BridgeSource.WOOFi: {
return this.getWOOFiSellQuotes(
WOOFI_POOL_BY_CHAIN_ID[this.chainId],
takerToken,
makerToken,
takerFillAmounts,
);
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
@@ -1767,7 +1873,7 @@ export class SamplerOperations {
): SourceQuoteOperation[] {
// Find the adjacent tokens in the provided token adjacency graph,
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
const intermediateTokens = this.tokenAdjacencyGraph.getIntermediateTokens(makerToken, takerToken);
const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources);
return _.flatten(
_sources.map((source): SourceQuoteOperation | SourceQuoteOperation[] => {
@@ -1871,20 +1977,17 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
return this.poolsCaches[ERC20BridgeSource.Balancer]
.getPoolAddressesForPair(takerToken, makerToken)
.map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
case ERC20BridgeSource.BalancerV2: {
const cache = this.poolsCaches[source];
if (!cache) {
@@ -1908,9 +2011,8 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Beethovenx: {
const poolIds =
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const cache = this.poolsCaches[source];
const poolIds = cache.getPoolAddressesForPair(takerToken, makerToken) || [];
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
@@ -1926,20 +2028,17 @@ export class SamplerOperations {
);
}
case ERC20BridgeSource.Cream:
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return this.poolsCaches[ERC20BridgeSource.Cream]
.getPoolAddressesForPair(takerToken, makerToken)
.map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODOV1_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -2027,8 +2126,8 @@ export class SamplerOperations {
return this.getCompoundBuyQuotes(cToken.tokenAddress, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.GMX: {
// bad mim pool dont quote
if (takerToken === 'AVALANCHE_TOKENS.MIM' || makerToken === 'AVALANCHE_TOKENS.MIM') {
// MIM has no liquidity.
if (takerToken === AVALANCHE_TOKENS.MIM || makerToken === AVALANCHE_TOKENS.MIM) {
return [];
}
return this.getGMXBuyQuotes(
@@ -2065,6 +2164,29 @@ export class SamplerOperations {
makerFillAmounts,
);
}
case ERC20BridgeSource.Synthetix: {
const readProxy = SYNTHETIX_READ_PROXY_BY_CHAIN_ID[this.chainId];
const currencyKeyMap = SYNTHETIX_CURRENCY_KEYS_BY_CHAIN_ID[this.chainId];
const takerTokenSymbol = currencyKeyMap.get(takerToken.toLowerCase());
const makerTokenSymbol = currencyKeyMap.get(makerToken.toLowerCase());
if (takerTokenSymbol === undefined || makerTokenSymbol === undefined) {
return [];
}
return this.getSynthetixBuyQuotes(
readProxy,
takerTokenSymbol,
makerTokenSymbol,
makerFillAmounts,
);
}
case ERC20BridgeSource.WOOFi: {
return this.getWOOFiBuyQuotes(
WOOFI_POOL_BY_CHAIN_ID[this.chainId],
takerToken,
makerToken,
makerFillAmounts,
);
}
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -1,3 +1,4 @@
import { ChainId } from '@0x/contract-addresses';
import {
FillQuoteTransformerLimitOrderInfo,
FillQuoteTransformerOrderType,
@@ -10,6 +11,7 @@ import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts }
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor';
import { IRfqClient } from '../irfq_client';
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
import { TokenAdjacencyGraph } from '../token_adjacency_graph';
import { SourceFilters } from './source_filters';
@@ -66,6 +68,8 @@ export enum ERC20BridgeSource {
Compound = 'Compound',
Synapse = 'Synapse',
BancorV3 = 'BancorV3',
Synthetix = 'Synthetix',
WOOFi = 'WOOFi',
// BSC only
PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2',
@@ -105,11 +109,6 @@ export enum ERC20BridgeSource {
// Optimism
Velodrome = 'Velodrome',
}
export type SourcesWithPoolsCache =
| ERC20BridgeSource.Balancer
| ERC20BridgeSource.BalancerV2
| ERC20BridgeSource.Beethovenx
| ERC20BridgeSource.Cream;
// tslint:disable: enum-naming
/**
@@ -375,11 +374,25 @@ export interface PlatypusFillData extends FillData {
tokenAddressPath: string[];
}
export interface WOOFiFillData extends FillData {
poolAddress: string;
takerToken: string;
makerToken: string;
}
export interface VelodromeFillData extends FillData {
router: string;
stable: boolean;
}
export interface SynthetixFillData extends FillData {
synthetix: string;
takerTokenSymbolBytes32: string;
makerTokenSymbolBytes32: string;
// Only needed for gas estimation.
chainId: ChainId;
}
/**
* Represents a node on a fill path.
*/
@@ -611,15 +624,6 @@ export interface OptimizerResultWithReport extends OptimizerResult {
priceComparisonsReport?: PriceComparisonsReport;
}
export type MarketDepthSide = Array<Array<DexSample<FillData>>>;
export interface MarketDepth {
bids: MarketDepthSide;
asks: MarketDepthSide;
makerTokenDecimals: number;
takerTokenDecimals: number;
}
export interface MarketSideLiquidity {
side: MarketOperation;
inputAmount: BigNumber;
@@ -642,11 +646,6 @@ export interface RawQuotes {
dexQuotes: Array<Array<DexSample<FillData>>>;
}
export interface TokenAdjacencyGraph {
[token: string]: string[];
default: string[];
}
export interface LiquidityProviderRegistry {
[address: string]: {
tokens: string[];

View File

@@ -6,28 +6,36 @@ import { SwapQuoterError } from '../types';
const MAX_ERROR_COUNT = 5;
interface GasOracleResponse {
result: {
// gas price in wei
fast: number;
};
}
export class ProtocolFeeUtils {
private static _instance: ProtocolFeeUtils;
private readonly _ethGasStationUrl!: string;
private readonly _zeroExGasApiUrl: string;
private readonly _gasPriceHeart: any;
private _gasPriceEstimation: BigNumber = constants.ZERO_AMOUNT;
private _errorCount: number = 0;
public static getInstance(
gasPricePollingIntervalInMs: number,
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
): ProtocolFeeUtils {
if (!ProtocolFeeUtils._instance) {
ProtocolFeeUtils._instance = new ProtocolFeeUtils(
gasPricePollingIntervalInMs,
ethGasStationUrl,
zeroExGasApiUrl,
initialGasPrice,
);
}
return ProtocolFeeUtils._instance;
}
/** @returns gas price (in wei) */
public async getGasPriceEstimationOrThrowAsync(shouldHardRefresh?: boolean): Promise<BigNumber> {
if (this._gasPriceEstimation.eq(constants.ZERO_AMOUNT)) {
return this._getGasPriceFromGasStationOrThrowAsync();
@@ -48,27 +56,21 @@ export class ProtocolFeeUtils {
private constructor(
gasPricePollingIntervalInMs: number,
ethGasStationUrl: string = constants.ETH_GAS_STATION_API_URL,
zeroExGasApiUrl: string = constants.ZERO_EX_GAS_API_URL,
initialGasPrice: BigNumber = constants.ZERO_AMOUNT,
) {
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
this._gasPriceEstimation = initialGasPrice;
this._ethGasStationUrl = ethGasStationUrl;
this._zeroExGasApiUrl = zeroExGasApiUrl;
this._initializeHeartBeat();
}
// tslint:disable-next-line: prefer-function-over-method
private async _getGasPriceFromGasStationOrThrowAsync(): Promise<BigNumber> {
try {
const res = await fetch(this._ethGasStationUrl);
const gasInfo = await res.json();
// Eth Gas Station result is gwei * 10
// tslint:disable-next-line:custom-no-magic-numbers
const BASE_TEN = 10;
const gasPriceGwei = new BigNumber(gasInfo.fast / BASE_TEN);
// tslint:disable-next-line:custom-no-magic-numbers
const unit = new BigNumber(BASE_TEN).pow(9);
const gasPriceWei = unit.times(gasPriceGwei);
const res = await fetch(this._zeroExGasApiUrl);
const gasInfo: GasOracleResponse = await res.json();
const gasPriceWei = new BigNumber(gasInfo.result.fast);
// Reset the error count to 0 once we have a successful response
this._errorCount = 0;
return gasPriceWei;

View File

@@ -0,0 +1,84 @@
import * as _ from 'lodash';
import { Address } from '../types';
export class TokenAdjacencyGraph {
private readonly _graph: Map<Address, Address[]>;
private readonly _defaultTokens: readonly Address[];
public static getEmptyGraph(): TokenAdjacencyGraph {
return new TokenAdjacencyGraphBuilder().build();
}
/** Prefer using {@link TokenAdjacencyGraphBuilder}. */
constructor(graph: Map<Address, Address[]>, defaultTokens: readonly Address[]) {
this._graph = graph;
this._defaultTokens = defaultTokens;
}
public getAdjacentTokens(fromToken: Address): readonly Address[] {
return this._graph.get(fromToken.toLowerCase()) || this._defaultTokens;
}
/** Given a token pair, returns the intermediate tokens to consider for two-hop routes. */
public getIntermediateTokens(takerToken: Address, makerToken: Address): Address[] {
// NOTE: it seems it should be a union of `to` tokens of `takerToken` and `from` tokens of `makerToken`,
// leaving it as same as the initial implementation for now.
return _.union(this.getAdjacentTokens(takerToken), this.getAdjacentTokens(makerToken)).filter(
token => token !== takerToken.toLowerCase() && token !== makerToken.toLowerCase(),
);
}
}
// tslint:disable-next-line: max-classes-per-file
export class TokenAdjacencyGraphBuilder {
private readonly _graph: Map<Address, Address[]>;
private readonly _defaultTokens: readonly Address[];
constructor(defaultTokens: readonly string[] = []) {
this._graph = new Map();
this._defaultTokens = defaultTokens.map(addr => addr.toLowerCase());
}
public add(fromToken: Address, toToken: Address): TokenAdjacencyGraphBuilder {
const fromLower = fromToken.toLowerCase();
const toLower = toToken.toLowerCase();
if (fromLower === toLower) {
throw new Error(`from token (${fromToken}) must be different from to token (${toToken})`);
}
if (!this._graph.has(fromLower)) {
this._graph.set(fromLower, [...this._defaultTokens]);
}
const toTokens = this._graph.get(fromLower)!;
if (!toTokens.includes(toLower)) {
toTokens.push(toLower);
}
return this;
}
public addBidirectional(tokenA: Address, tokenB: Address): TokenAdjacencyGraphBuilder {
return this.add(tokenA, tokenB).add(tokenB, tokenA);
}
public addCompleteSubgraph(tokens: Address[]): TokenAdjacencyGraphBuilder {
for (let i = 0; i < tokens.length; i++) {
for (let j = i + 1; j < tokens.length; j++) {
this.addBidirectional(tokens[i], tokens[j]);
}
}
return this;
}
public tap(cb: (graph: TokenAdjacencyGraphBuilder) => void): TokenAdjacencyGraphBuilder {
cb(this);
return this;
}
public build(): TokenAdjacencyGraph {
return new TokenAdjacencyGraph(this._graph, this._defaultTokens);
}
}

View File

@@ -1,25 +0,0 @@
import _ = require('lodash');
import { TokenAdjacencyGraph } from './market_operation_utils/types';
export class TokenAdjacencyGraphBuilder {
constructor(private readonly tokenAdjacency: TokenAdjacencyGraph) {}
public add(from: string, to: string | string[]): TokenAdjacencyGraphBuilder {
if (!this.tokenAdjacency[from]) {
this.tokenAdjacency[from] = [...this.tokenAdjacency.default];
}
this.tokenAdjacency[from] = [...(Array.isArray(to) ? to : [to]), ...this.tokenAdjacency[from]];
this.tokenAdjacency[from] = _.uniqBy(this.tokenAdjacency[from], a => a.toLowerCase());
return this;
}
public tap(cb: (builder: TokenAdjacencyGraphBuilder) => void): TokenAdjacencyGraphBuilder {
cb(this);
return this;
}
public build(): TokenAdjacencyGraph {
return this.tokenAdjacency;
}
}

View File

@@ -43,6 +43,7 @@ import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSamp
import * as PlatypusSampler from '../test/generated-artifacts/PlatypusSampler.json';
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
import * as SynthetixSampler from '../test/generated-artifacts/SynthetixSampler.json';
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
@@ -50,6 +51,7 @@ import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.
import * as UniswapV3Sampler from '../test/generated-artifacts/UniswapV3Sampler.json';
import * as UtilitySampler from '../test/generated-artifacts/UtilitySampler.json';
import * as VelodromeSampler from '../test/generated-artifacts/VelodromeSampler.json';
import * as WooPPSampler from '../test/generated-artifacts/WooPPSampler.json';
export const artifacts = {
ApproximateBuys: ApproximateBuys as ContractArtifact,
BalanceChecker: BalanceChecker as ContractArtifact,
@@ -76,12 +78,14 @@ export const artifacts = {
PlatypusSampler: PlatypusSampler as ContractArtifact,
SamplerUtils: SamplerUtils as ContractArtifact,
ShellSampler: ShellSampler as ContractArtifact,
SynthetixSampler: SynthetixSampler as ContractArtifact,
TwoHopSampler: TwoHopSampler as ContractArtifact,
UniswapSampler: UniswapSampler as ContractArtifact,
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
UniswapV3Sampler: UniswapV3Sampler as ContractArtifact,
UtilitySampler: UtilitySampler as ContractArtifact,
VelodromeSampler: VelodromeSampler as ContractArtifact,
WooPPSampler: WooPPSampler as ContractArtifact,
IBalancer: IBalancer as ContractArtifact,
IBalancerV2Vault: IBalancerV2Vault as ContractArtifact,
IBancor: IBancor as ContractArtifact,

View File

@@ -13,7 +13,8 @@ import * as _ from 'lodash';
import { SignedOrder } from '../src/types';
import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler';
import { ERC20BridgeSource, TokenAdjacencyGraph } from '../src/utils/market_operation_utils/types';
import { ERC20BridgeSource } from '../src/utils/market_operation_utils/types';
import { TokenAdjacencyGraphBuilder } from '../src/utils/token_adjacency_graph';
import { MockSamplerContract } from './utils/mock_sampler_contract';
import { generatePseudoRandomSalt } from './utils/utils';
@@ -29,7 +30,7 @@ describe('DexSampler tests', () => {
const wethAddress = getContractAddressesForChainOrThrow(CHAIN_ID).etherToken;
const exchangeProxyAddress = getContractAddressesForChainOrThrow(CHAIN_ID).exchangeProxy;
const tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [wethAddress] };
const tokenAdjacencyGraph = new TokenAdjacencyGraphBuilder([wethAddress]).build();
describe('getSampleAmounts()', () => {
const FILL_AMOUNT = getRandomInteger(1, 1e18);

View File

@@ -15,7 +15,7 @@ import { Pool } from 'balancer-labs-sor-v1/dist/types';
import * as _ from 'lodash';
import * as TypeMoq from 'typemoq';
import { MarketOperation, QuoteRequestor, RfqRequestOpts, SignedNativeOrder } from '../src';
import { MarketOperation, QuoteRequestor, RfqRequestOpts, SignedNativeOrder, TokenAdjacencyGraph } from '../src';
import { Integrator } from '../src/types';
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
import {
@@ -24,7 +24,7 @@ import {
SOURCE_FLAGS,
ZERO_AMOUNT,
} from '../src/utils/market_operation_utils/constants';
import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache';
import { AbstractPoolsCache } from '../src/utils/market_operation_utils/pools_cache';
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations';
import { SourceFilters } from '../src/utils/market_operation_utils/source_filters';
@@ -39,7 +39,6 @@ import {
MarketSideLiquidity,
OptimizedMarketBridgeOrder,
OptimizerResultWithReport,
TokenAdjacencyGraph,
} from '../src/utils/market_operation_utils/types';
const MAKER_TOKEN = randomAddress();
@@ -57,7 +56,7 @@ const DEFAULT_EXCLUDED = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources
);
const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
const TOKEN_ADJACENCY_GRAPH = TokenAdjacencyGraph.getEmptyGraph();
const SIGNATURE = { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign };
const FOO_INTEGRATOR: Integrator = {
@@ -99,9 +98,9 @@ async function getMarketBuyOrdersAsync(
return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts);
}
class MockPoolsCache extends PoolsCache {
class MockPoolsCache extends AbstractPoolsCache {
constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) {
super({});
super(new Map());
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
return this._handler(takerToken, makerToken);

View File

@@ -17,8 +17,6 @@ const expect = chai.expect;
const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f';
const wethAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const wbtcAddress = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599';
const balAddress = '0xba100000625a3754423978a60c9317c58a424e3d';
const creamAddress = '0x2ba592f78db6436527729929aaf6c908497cb200';
const timeoutMs = 5000;
@@ -30,12 +28,12 @@ describe('Pools Caches for Balancer-based sampling', () => {
expect(pools.length).greaterThan(0, `Failed to find any pools for ${takerToken} and ${makerToken}`);
expect(pools[0]).not.undefined();
expect(Object.keys(pools[0])).to.include.members(poolKeys);
const cachedPoolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken);
const cachedPoolIds = cache.getPoolAddressesForPair(takerToken, makerToken);
expect(cachedPoolIds).to.deep.equal(pools.map(p => p.id));
}
describe('BalancerPoolsCache', () => {
const cache = new BalancerPoolsCache();
const cache = BalancerPoolsCache.create(ChainId.Mainnet);
it('fetches pools', async () => {
const pairs = [
[usdcAddress, daiAddress],
@@ -43,36 +41,38 @@ describe('Pools Caches for Balancer-based sampling', () => {
[daiAddress, wethAddress],
];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
describe('BalancerV2PoolsCache', () => {
const cache = new BalancerV2PoolsCache(ChainId.Mainnet);
it('fetches pools', async () => {
it('fetches pools (Beethoven X - Fantom)', async () => {
const cache = BalancerV2PoolsCache.createBeethovenXPoolCache(ChainId.Fantom);
const wftmAddress = '0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83';
const beetsAddress = '0xf24bcf4d1e507740041c9cfd2dddb29585adce1e';
const fantomWethAddress = '0x74b23882a30290451a17c44f4f05243b6b58c76d';
const pairs = [
[wethAddress, wbtcAddress],
[wethAddress, balAddress],
[wftmAddress, beetsAddress],
[wftmAddress, fantomWethAddress],
];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
describe('CreamPoolsCache', () => {
const cache = new CreamPoolsCache();
const cache = CreamPoolsCache.create(ChainId.Mainnet);
it('fetches pools', async () => {
const pairs = [
[usdcAddress, creamAddress],
[creamAddress, wethAddress],
];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
pairs.map(async ([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});

View File

@@ -0,0 +1,45 @@
import * as chai from 'chai';
import 'mocha';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import { ProtocolFeeUtils } from '../src';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
const server = setupServer(
rest.get('https://mock-0x-gas-api.org/median', (_req, res, ctx) => {
return res(
ctx.json({
result: {
source: 'MEDIAN',
timestamp: 1659386474,
instant: 22000000000,
fast: 18848500000,
standard: 14765010000,
low: 13265000000,
},
}),
);
}),
);
describe('ProtocolFeeUtils', () => {
describe('getGasPriceEstimationOrThrowAsync', () => {
beforeEach(() => {
server.listen();
});
afterEach(() => {
server.close();
});
it('parses fast gas price response correctly', async () => {
const utils = ProtocolFeeUtils.getInstance(420000, 'https://mock-0x-gas-api.org/median');
const gasPrice = await utils.getGasPriceEstimationOrThrowAsync();
expect(gasPrice.toNumber()).to.eq(18848500000);
});
});
});

View File

@@ -0,0 +1,108 @@
import * as chai from 'chai';
import 'mocha';
import { TokenAdjacencyGraphBuilder } from '../src/utils/token_adjacency_graph';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
describe('TokenAdjacencyGraphBuilder and TokenAdjacencyGraph', () => {
describe('constructor', () => {
it('sanitizes passed default tokens to lower case', async () => {
const graph = new TokenAdjacencyGraphBuilder(['DEFAULT_1', 'DEFAULT_2']).build();
expect(graph.getAdjacentTokens('random_token')).to.deep.eq(['default_1', 'default_2']);
});
});
describe('add', () => {
it('adds a new token path to the graph', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1', 'default_2']).add('token_a', 'token_b').build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'default_2', 'token_b']);
});
it('adds lower-cased token path to the graph', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1', 'default_2']).add('TOKEN_A', 'TOKEN_B').build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'default_2', 'token_b']);
});
it('ignores an existing to token', async () => {
const graph = new TokenAdjacencyGraphBuilder()
.add('token_a', 'token_b')
.add('token_a', 'token_b')
.build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['token_b']);
});
});
describe('addBidirectional', () => {
it('adds a bidirectional path to the graph', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1']).addBidirectional('token_a', 'token_b').build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b']);
expect(graph.getAdjacentTokens('token_b')).to.deep.eq(['default_1', 'token_a']);
});
});
describe('addCompleteSubgraph', () => {
it('adds a complete subgraph to the graph', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1'])
.addCompleteSubgraph(['token_a', 'token_b', 'token_c', 'token_d'])
.build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b', 'token_c', 'token_d']);
expect(graph.getAdjacentTokens('token_b')).to.deep.eq(['default_1', 'token_a', 'token_c', 'token_d']);
expect(graph.getAdjacentTokens('token_c')).to.deep.eq(['default_1', 'token_a', 'token_b', 'token_d']);
expect(graph.getAdjacentTokens('token_d')).to.deep.eq(['default_1', 'token_a', 'token_b', 'token_c']);
});
});
describe('tap', () => {
it('applies callback correctly', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1'])
.tap(g => {
g.add('token_a', 'token_b');
g.add('token_c', 'token_d');
})
.build();
expect(graph.getAdjacentTokens('token_a')).to.deep.eq(['default_1', 'token_b']);
expect(graph.getAdjacentTokens('token_c')).to.deep.eq(['default_1', 'token_d']);
});
});
describe('getIntermediateTokens', () => {
it('returns intermediate tokens without a duplicate ', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1'])
.add('token_a', 'token_b')
.add('token_c', 'token_b')
.build();
expect(graph.getIntermediateTokens('token_a', 'token_c')).to.deep.eq(['default_1', 'token_b']);
});
it('returns intermediate tokens after lower-casing taker and maker tokens', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1'])
.add('token_a', 'token_b')
.add('token_c', 'token_d')
.build();
expect(graph.getIntermediateTokens('TOKEN_a', 'token_C')).to.deep.eq(['default_1', 'token_b', 'token_d']);
});
it('returns intermediate tokens excluding taker token or maker token ', async () => {
const graph = new TokenAdjacencyGraphBuilder(['default_1'])
.addBidirectional('token_a', 'token_b')
.addBidirectional('token_b', 'token_c')
.addBidirectional('token_c', 'token_a')
.build();
expect(graph.getIntermediateTokens('token_a', 'token_c')).to.deep.eq(['default_1', 'token_b']);
});
});
});

View File

@@ -41,6 +41,7 @@ export * from '../test/generated-wrappers/native_order_sampler';
export * from '../test/generated-wrappers/platypus_sampler';
export * from '../test/generated-wrappers/sampler_utils';
export * from '../test/generated-wrappers/shell_sampler';
export * from '../test/generated-wrappers/synthetix_sampler';
export * from '../test/generated-wrappers/test_native_order_sampler';
export * from '../test/generated-wrappers/two_hop_sampler';
export * from '../test/generated-wrappers/uniswap_sampler';
@@ -48,3 +49,4 @@ export * from '../test/generated-wrappers/uniswap_v2_sampler';
export * from '../test/generated-wrappers/uniswap_v3_sampler';
export * from '../test/generated-wrappers/utility_sampler';
export * from '../test/generated-wrappers/velodrome_sampler';
export * from '../test/generated-wrappers/woo_p_p_sampler';

View File

@@ -44,12 +44,14 @@
"test/generated-artifacts/PlatypusSampler.json",
"test/generated-artifacts/SamplerUtils.json",
"test/generated-artifacts/ShellSampler.json",
"test/generated-artifacts/SynthetixSampler.json",
"test/generated-artifacts/TestNativeOrderSampler.json",
"test/generated-artifacts/TwoHopSampler.json",
"test/generated-artifacts/UniswapSampler.json",
"test/generated-artifacts/UniswapV2Sampler.json",
"test/generated-artifacts/UniswapV3Sampler.json",
"test/generated-artifacts/UtilitySampler.json",
"test/generated-artifacts/VelodromeSampler.json"
"test/generated-artifacts/VelodromeSampler.json",
"test/generated-artifacts/WooPPSampler.json"
]
}

View File

@@ -1,6 +1,8 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"array-type": false,
"custom-no-magic-numbers": false,
"max-file-line-count": false,
"binary-expression-operand-order": false
},

View File

@@ -1,4 +1,27 @@
[
{
"version": "6.19.0",
"changes": [
{
"note": "Goerli and Mumbai updated verified contracts addresses",
"pr": 537
}
],
"timestamp": 1659750766
},
{
"version": "6.17.0",
"changes": [
{
"note": "Goerli and Mumbai"
},
{
"note": "Redeploy FQT on Mainnet and Optimism",
"pr": 530
}
],
"timestamp": 1658950329
},
{
"version": "6.16.0",
"changes": [

View File

@@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.19.0 - _August 6, 2022_
* Goerli and Mumbai updated verified contracts addresses (#537)
## v6.17.0 - _July 27, 2022_
* Goerli and Mumbai
* Redeploy FQT on Mainnet and Optimism (#530)
## v6.16.0 - _June 14, 2022_
* Redeploy FQT on Mainnet and Optimism

View File

@@ -37,7 +37,7 @@
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
"payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e",
"affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f",
"fillQuoteTransformer": "0x26b2d9ea76f24206805d17565a5e0efcf787e0ae",
"fillQuoteTransformer": "0xd75a9019a2a1782ea670e4f4a55f04b43514ed53",
"positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd"
}
},
@@ -125,6 +125,48 @@
"positiveSlippageFeeTransformer": "0x0000000000000000000000000000000000000000"
}
},
"5": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0xf289f8a9d26f9a32ecc8602e92e634d71a91d490",
"exchangeProxy": "0xf91bb752490473b8342a3e964e855b9f9a2a668e",
"exchangeProxyTransformerDeployer": "0x7b4f0063cc0097c19c6b8cc74ecaf630621e2be6",
"exchangeProxyFlashWallet": "0xf15469c80a1965f5f90be5651fcb6c6f3392b2a1",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x5e485f1ab8aa2a71d533f2bc15af64dd10fa44e9",
"payTakerTransformer": "0x0c59ced2ffce5fceb40f665def2146f76c576f34",
"affiliateFeeTransformer": "0x6f4d30df1ecaed54239af95efa2964f9252fd0a3",
"fillQuoteTransformer": "0x841b18c263a290c41ac41b6afbf4c1d31b0c6b36",
"positiveSlippageFeeTransformer": "0x5d6ab5729a08775c69035730bcfbad65759c4f4d"
}
},
"42": {
"erc20Proxy": "0xaa460127562482faa5df42f2c39a025cd4a1cc0a",
"erc721Proxy": "0x7b70a148e20b348c320208df84fdd642aab49fd0",
@@ -321,18 +363,18 @@
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x4cf19577bcb5e784f315e952f97a6dc247f03140",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyTransformerDeployer": "0xa8220408bcb5b327875fd82145d379a83dfd7d61",
"exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498",
"exchangeProxyLiquidityProviderSandbox": "0xe6f76f5090f8d64015113841a0c9bc5d14755d6f",
"exchangeProxyGovernor": "0x30186b2e187aeddabf019089f9375a8dc53138e4",
"exchangeProxy": "0xf471d32cb40837bf24529fcf17418fc1a4807626",
"exchangeProxyTransformerDeployer": "0x05481589f447a0767def2b0ed98a04ea5f5eba50",
"exchangeProxyFlashWallet": "0x64254cf2f3abd765bee46f8445b76e2bb0af5a2c",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x44a65ee6b33f70eda7b854abe8d81e925984c932",
"payTakerTransformer": "0x2f4868ed9cae9a4cdba063818dce19f411be4e75",
"affiliateFeeTransformer": "0x843c0ac5b1b373be51800c8f1caffe54cc29dd22",
"fillQuoteTransformer": "0x750cb81ee6d64e29e1e358ba155925000bf044d4",
"positiveSlippageFeeTransformer": "0x30aebc4c68effa70e21612b39b94299a8778d0cb"
"wethTransformer": "0x445af2e5791cc9a72f81c49a3dc90cf3b03d2a62",
"payTakerTransformer": "0xe3e8652fb306873f9dc87222423ffd51b967f014",
"affiliateFeeTransformer": "0xbd901aff2ce18355537594a17ebb0a38ca44d8b6",
"fillQuoteTransformer": "0xbfac39aea3c0a6222266cef674ec39c3b5387852",
"positiveSlippageFeeTransformer": "0x33ab86b46d84d30538a9b35c7ece4d5673caa778"
}
},
"43114": {
@@ -499,7 +541,7 @@
"wethTransformer": "0x02ce7af6520e2862f961f5d7eda746642865179c",
"payTakerTransformer": "0x085d10a34f14f6a631ea8ff7d016782ee3ffaa11",
"affiliateFeeTransformer": "0x55cf1d7535250db75bf0190493f55781ee583553",
"fillQuoteTransformer": "0xfae0ce3841afbf5625a15f0c73e03ba6660e575f",
"fillQuoteTransformer": "0x96499c097efc56ba5cf6b2a474392db17790ce96",
"positiveSlippageFeeTransformer": "0xb11e14565dfbeb702dea9bc0cb47f1a8b32f4783"
}
}

View File

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

View File

@@ -47,6 +47,7 @@ export enum ChainId {
Mainnet = 1,
Ropsten = 3,
Rinkeby = 4,
Goerli = 5,
Kovan = 42,
Ganache = 1337,
BSC = 56,
@@ -55,6 +56,7 @@ export enum ChainId {
Avalanche = 43114,
Fantom = 250,
Celo = 42220,
// Arbitrum = 42161,
Optimism = 10,
}

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1659750766,
"version": "13.20.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1658950329,
"version": "13.20.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1655244958,
"version": "13.20.4",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v13.20.6 - _August 6, 2022_
* Dependencies updated
## v13.20.5 - _July 27, 2022_
* Dependencies updated
## v13.20.4 - _June 14, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers",
"version": "13.20.4",
"version": "13.20.6",
"engines": {
"node": ">=6.12"
},
@@ -57,7 +57,7 @@
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/base-contract": "^6.5.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/contract-addresses": "^6.19.0",
"@0x/json-schemas": "^6.4.4",
"@0x/types": "^3.3.6",
"@0x/utils": "^6.5.3",

View File

@@ -1,4 +1,23 @@
[
{
"timestamp": 1659750766,
"version": "11.16.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "11.16.0",
"changes": [
{
"note": "Add Synthetix support`",
"pr": 518
}
],
"timestamp": 1658950329
},
{
"version": "11.15.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v11.16.1 - _August 6, 2022_
* Dependencies updated
## v11.16.0 - _July 27, 2022_
* Add Synthetix support` (#518)
## v11.15.0 - _June 14, 2022_
* Add Velodrome support (#494)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "11.15.0",
"version": "11.16.1",
"engines": {
"node": ">=6.12"
},
@@ -63,8 +63,8 @@
},
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/contract-addresses": "^6.16.0",
"@0x/contract-wrappers": "^13.20.4",
"@0x/contract-addresses": "^6.19.0",
"@0x/contract-wrappers": "^13.20.6",
"@0x/json-schemas": "^6.4.4",
"@0x/subproviders": "^6.6.5",
"@0x/utils": "^6.5.3",

View File

@@ -140,6 +140,8 @@ export enum BridgeProtocol {
Platypus,
BancorV3,
Velodrome,
Synthetix,
WOOFi,
}
// tslint:enable: enum-naming

1058
yarn.lock

File diff suppressed because it is too large Load Diff