Compare commits

...

40 Commits

Author SHA1 Message Date
Noah Khamliche
5ae24b5211 fixed mobius celo pools 2022-02-02 14:52:17 -05:00
Noah Khamliche
0a08a44aa1 setting up asset swapper 2022-01-31 13:52:18 -05:00
Noah Khamliche
a1b21c9b70 added initial support for celo 2022-01-24 15:47:37 -05:00
Github Actions
dd96eef3aa Publish
- @0x/asset-swapper@16.46.0
2022-01-24 15:47:26 -05:00
Github Actions
16a40d845c Updated CHANGELOGS & MD docs 2022-01-24 15:47:25 -05:00
Jacob Evans
b36b1013d1 chore: Enable Curve ETH/CVX (#394)
* chore: Enable Curve ETH/CVX

* pr number
2022-01-24 15:47:25 -05:00
Github Actions
6d3de3f981 Publish
- @0x/asset-swapper@16.45.2
2022-01-24 15:47:25 -05:00
Github Actions
6b902cf94c Updated CHANGELOGS & MD docs 2022-01-24 15:47:25 -05:00
Kim Persson
6a7573d721 fix: don't create fills for 0 output samples and negative adjusted rate orders (#387)
* fix: don't try to create fills for 0 output samples

* fix: negative adjusted output native orders causing undefined fills

* fix: make sure to use the same sourcePathId for fills from same source

* fix: should be same sourcePathId within the same DexSample[]

* fix: split native orders into 13 samples to align with interpolation

* chore: add changelog entry for asset-swapper
2022-01-24 15:47:25 -05:00
Github Actions
e5bdbb8a58 Publish
- @0x/asset-swapper@16.45.1
2022-01-24 15:47:25 -05:00
Github Actions
4f8b1adee4 Updated CHANGELOGS & MD docs 2022-01-24 15:47:25 -05:00
Jacob Evans
2696359f33 chore: Celo Update certain tokens since Optics v2 (#390)
* chore: Celo Update certain tokens since Optics v2

* Changelog
2022-01-24 15:47:22 -05:00
Oskar Paolini
278edb73af fixes axios object dumping in logs (#345) 2022-01-24 15:47:11 -05:00
Github Actions
d2a3dd371a Publish
- @0x/asset-swapper@16.45.0
2022-01-24 15:47:11 -05:00
Github Actions
9f5b35bcec Updated CHANGELOGS & MD docs 2022-01-24 15:47:11 -05:00
Jacob Evans
242df31584 feat: Capture Routing timing metrics (#388) 2022-01-24 15:47:11 -05:00
Github Actions
837f36d74c Publish
- @0x/asset-swapper@16.44.0
2022-01-24 15:47:10 -05:00
Github Actions
710e19559c Updated CHANGELOGS & MD docs 2022-01-24 15:47:10 -05:00
Kim Persson
87905fd122 feat: Udate neon-router and use router estimated output amount (#354)
* feat: use Rust router estimated output amount when possible

* fix: use strings for sample ids, and increase samples in the rust router

* fix: remove unnecessary interpolation of out of range values

* fix: don't recalculate sampled dist sum in a loop

* fix: use 14 samples for rust router to work around interpolation issues

* fix: unintentional logic change

* fix: remove local dev plotting param from route fn call

* feat: make neon-router number of samples configurable

* chore: bump to newly published neon-router version

* fix: handle insufficient liquidity at all requested sources

* chore: update asset-swapper changelog
2022-01-24 15:47:10 -05:00
Github Actions
dac22c20ce Publish
- @0x/contracts-erc20@3.3.25
 - @0x/contracts-test-utils@5.4.16
 - @0x/contracts-treasury@1.4.8
 - @0x/contracts-utils@4.8.6
 - @0x/contracts-zero-ex@0.30.1
 - @0x/asset-swapper@16.43.0
 - @0x/contract-addresses@6.11.0
 - @0x/contract-wrappers@13.18.5
 - @0x/migrations@8.1.14
 - @0x/protocol-utils@1.10.1
2022-01-24 15:47:10 -05:00
Github Actions
3b93c295d1 Updated CHANGELOGS & MD docs 2022-01-24 15:47:10 -05:00
Shawn
6e2692a576 feat/optimism [TKR-280] (#385)
* rebase and remove WIP Clipper new weth router test

* remove clipper test

* restore from clipper test

* add uniswapV3 on optimism

* modify token addresses and add EP-related addresses on optimism

* prettier

* modify CHANGLOG.json

* convert addresses to lowercase

* remove testnet addresses

Co-authored-by: Romain Butteaud <romain.butteaud@gmail.com>
2022-01-24 15:47:10 -05:00
Github Actions
dfeb3929b7 Publish
- @0x/asset-swapper@16.42.0
2022-01-24 15:47:09 -05:00
Github Actions
42de461906 Updated CHANGELOGS & MD docs 2022-01-24 15:47:09 -05:00
Jacob Evans
e872d72e8c fix: Beethovenx graphql endpoint (#383) 2022-01-24 15:47:09 -05:00
Jacob Evans
7e340c6f42 fix: PR number in CHANGELOG 2022-01-24 15:47:09 -05:00
Jacob Evans
448c33c534 feat: UniswapV3 on Polygon (#382) 2022-01-24 15:47:09 -05:00
Github Actions
87839f75c6 Publish
- @0x/asset-swapper@16.41.0
2022-01-24 15:47:09 -05:00
Github Actions
3759c4a5c6 Updated CHANGELOGS & MD docs 2022-01-24 15:47:09 -05:00
Noah Khamliche
476988c64a Sushi router celo fix (#376)
* Updated Sushiswap router on CELO

Sushi address in the docs was incorrect, this is the correct address `0x1421bDe4B10e8dd459b3BCb598810B1337D56842`.

* address to lowercase

* Update CHANGELOG.json

* fixed sushi router address, replaced celo ref with wcelo, fixed mcusd address

* celo->wcelo

* Update packages/asset-swapper/CHANGELOG.json

Co-authored-by: Jacob Evans <jacob@dekz.net>

* changelog

* version bump

Co-authored-by: Jacob Evans <jacob@dekz.net>
2022-01-24 15:47:05 -05:00
Github Actions
0205bc24f6 Publish
- @0x/contracts-erc20@3.3.24
 - @0x/contracts-test-utils@5.4.15
 - @0x/contracts-treasury@1.4.7
 - @0x/contracts-utils@4.8.5
 - @0x/contracts-zero-ex@0.30.0
 - @0x/asset-swapper@16.40.0
 - @0x/contract-addresses@6.10.0
 - @0x/contract-wrappers@13.18.4
 - @0x/migrations@8.1.13
 - @0x/protocol-utils@1.10.0
2022-01-24 15:46:31 -05:00
Github Actions
06644ca366 Updated CHANGELOGS & MD docs 2022-01-24 15:46:31 -05:00
Kim Persson
8a5881400c feat: deploy interest tokens (#321)
* feat: Aave aToken deposit/withdrawal [TKR-111] (#293)

* feat: AaveV2 deposit/withdrawal integration WIP

* feat: add basic Aave Reserves cache with data from subgraphs WIP

* feat: hook up Aave Reserves integration

* fix: set allowance before trade & use ERC20 token interface

* refactor: pass aToken to mixin to avoid lookup

* fix: migrate from swap/revert to normal sampling

* fix: Aave gas estimate & refactor to clean up code

* feat: Create a sampler no operation type and make AaveV2Sampler a no-op

* fix: Clipper merge conflict resolution issues

* fix: don't fetch unnecessary Aave pool data & clean up code

* chore: Add changelog entries

* feat: cToken deposit/withdrawal [TKR-222] (#294)

* feat: first stab at a CompoundSampler implementation

* feat: MixinCompound implementation WIP

* feat: Compound integration with cache WIP

* fix: decimals scaling in CompoundSampler

* feat: handle minting and redeeming of cETH

* fix: adjust Compound gas schedule

* refactor: clean up code and add comments in cToken cache

* fix: MixinCompound check allowance on WETH withdrawal & fix indentation

* fix: address review comments and clean up code

* chore: add changelog entries

* feat: enable AaveV2 on Avalanche

* chore: add freshly deployed FQT on Polygon, Avalanche

* fix: temporarily disable on Ethereum mainnet until we redeploy EP

* fix: address PR comments and update changelogs

* fix: correct contract-addresses changelog note
2022-01-24 15:46:31 -05:00
Jacob Evans
30ae0c56ca chore: Curve added ETH/CRV pool (#378) 2022-01-24 15:46:31 -05:00
Jacob Evans
4b402916c8 Revert "chore: Curve added ETH/CRV pool"
This reverts commit 90b441330b.
2022-01-24 15:46:30 -05:00
Jacob Evans
349766a761 chore: Curve added ETH/CRV pool 2022-01-24 15:46:30 -05:00
Github Actions
a57b66ccbd Publish
- @0x/asset-swapper@16.38.0
2022-01-24 15:46:30 -05:00
Github Actions
f49b07e76b Updated CHANGELOGS & MD docs 2022-01-24 15:46:30 -05:00
Jacob Evans
44a47dd233 chore: Add ability to capture sampler metrics (#374)
* chore: Add ability to capture sampler metrics

* Added block number metrics
2022-01-24 15:46:30 -05:00
Noah Khamliche
70a4119b89 added initial support for celo 2022-01-24 14:53:34 -05:00
65 changed files with 1917 additions and 162 deletions

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "3.3.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "3.3.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "3.3.23",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.25 - _December 24, 2021_
* Dependencies updated
## v3.3.24 - _December 1, 2021_
* Dependencies updated
## v3.3.23 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.23",
"version": "3.3.25",
"engines": {
"node": ">=6.12"
},
@@ -53,8 +53,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.14",
"@0x/contracts-utils": "^4.8.4",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/contracts-utils": "^4.8.6",
"@0x/dev-utils": "^4.2.9",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "5.4.16",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "5.4.15",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "5.4.14",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.16 - _December 24, 2021_
* Dependencies updated
## v5.4.15 - _December 1, 2021_
* Dependencies updated
## v5.4.14 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.14",
"version": "5.4.16",
"engines": {
"node": ">=6.12"
},
@@ -44,7 +44,7 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/order-utils": "^10.4.28",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "1.4.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "1.4.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "1.4.6",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.4.8 - _December 24, 2021_
* Dependencies updated
## v1.4.7 - _December 1, 2021_
* Dependencies updated
## v1.4.6 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.4.6",
"version": "1.4.8",
"engines": {
"node": ">=6.12"
},
@@ -47,12 +47,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.23",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-test-utils": "^5.4.14",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.2",
"@0x/protocol-utils": "^1.9.5",
"@0x/protocol-utils": "^1.10.1",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "4.8.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "4.8.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "4.8.4",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.8.6 - _December 24, 2021_
* Dependencies updated
## v4.8.5 - _December 1, 2021_
* Dependencies updated
## v4.8.4 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.8.4",
"version": "4.8.6",
"engines": {
"node": ">=6.12"
},
@@ -52,7 +52,7 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.14",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/dev-utils": "^4.2.9",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.7.5",

View File

@@ -1,4 +1,23 @@
[
{
"timestamp": 1640364306,
"version": "0.30.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.30.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"timestamp": 1637102971,
"version": "0.29.5",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.30.1 - _December 24, 2021_
* Dependencies updated
## v0.30.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v0.29.5 - _November 16, 2021_
* Dependencies updated

View File

@@ -22,10 +22,12 @@ pragma experimental ABIEncoderV2;
import "./IBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCompound.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinCryptoCom.sol";
@@ -47,10 +49,12 @@ import "./mixins/MixinZeroExBridge.sol";
contract BridgeAdapter is
IBridgeAdapter,
MixinAaveV2,
MixinBalancer,
MixinBalancerV2,
MixinBancor,
MixinCoFiX,
MixinCompound,
MixinCurve,
MixinCurveV2,
MixinCryptoCom,
@@ -72,10 +76,12 @@ contract BridgeAdapter is
{
constructor(IEtherTokenV06 weth)
public
MixinAaveV2()
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinCoFiX()
MixinCompound(weth)
MixinCurve(weth)
MixinCurveV2()
MixinCryptoCom()
@@ -245,6 +251,20 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.AAVEV2) {
boughtAmount = _tradeAaveV2(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.COMPOUND) {
boughtAmount = _tradeCompound(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,

View File

@@ -50,4 +50,6 @@ library BridgeProtocols {
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
uint128 internal constant AAVEV2 = 23;
uint128 internal constant COMPOUND = 24;
}

View File

@@ -0,0 +1,93 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
// Minimal Aave V2 LendingPool interface
interface ILendingPool {
/**
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
/**
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to Address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
* @return The final amount withdrawn
**/
function withdraw(
address asset,
uint256 amount,
address to
) external returns (uint256);
}
contract MixinAaveV2 {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeAaveV2(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256)
{
(ILendingPool lendingPool, address aToken) = abi.decode(bridgeData, (ILendingPool, address));
sellToken.approveIfBelow(
address(lendingPool),
sellAmount
);
if (address(buyToken) == aToken) {
lendingPool.deposit(address(sellToken), sellAmount, address(this), 0);
// 1:1 mapping token -> aToken and have the same number of decimals as the underlying token
return sellAmount;
} else if (address(sellToken) == aToken) {
return lendingPool.withdraw(address(buyToken), sellAmount, address(this));
}
revert("MixinAaveV2/UNSUPPORTED_TOKEN_PAIR");
}
}

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
/// @dev Minimal CToken interface
interface ICToken {
/// @dev deposits specified amount underlying tokens and mints cToken for the sender
/// @param mintAmountInUnderlying amount of underlying tokens to deposit to mint cTokens
/// @return status code of whether the mint was successful or not
function mint(uint256 mintAmountInUnderlying) external returns (uint256);
/// @dev redeems specified amount of cTokens and returns the underlying token to the sender
/// @param redeemTokensInCtokens amount of cTokens to redeem for underlying collateral
/// @return status code of whether the redemption was successful or not
function redeem(uint256 redeemTokensInCtokens) external returns (uint256);
}
/// @dev Minimal CEther interface
interface ICEther {
/// @dev deposits the amount of Ether sent as value and return mints cEther for the sender
function mint() payable external;
/// @dev redeems specified amount of cETH and returns the underlying ether to the sender
/// @dev redeemTokensInCEther amount of cETH to redeem for underlying ether
/// @return status code of whether the redemption was successful or not
function redeem(uint256 redeemTokensInCEther) external returns (uint256);
}
contract MixinCompound {
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
uint256 constant private COMPOUND_SUCCESS_CODE = 0;
function _tradeCompound(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256)
{
(address cTokenAddress) = abi.decode(bridgeData, (address));
uint256 beforeBalance = buyToken.balanceOf(address(this));
if (address(buyToken) == cTokenAddress) {
if (address(sellToken) == address(WETH)) {
// ETH/WETH -> cETH
ICEther cETH = ICEther(cTokenAddress);
// Compound expects ETH to be sent with mint call
WETH.withdraw(sellAmount);
// NOTE: cETH mint will revert on failure instead of returning a status code
cETH.mint{value: sellAmount}();
} else {
sellToken.approveIfBelow(
cTokenAddress,
sellAmount
);
// Token -> cToken
ICToken cToken = ICToken(cTokenAddress);
require(cToken.mint(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_MINT_CTOKEN");
}
} else if (address(sellToken) == cTokenAddress) {
if (address(buyToken) == address(WETH)) {
// cETH -> ETH/WETH
uint256 etherBalanceBefore = address(this).balance;
ICEther cETH = ICEther(cTokenAddress);
require(cETH.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CETHER");
uint256 etherBalanceAfter = address(this).balance;
uint256 receivedEtherBalance = etherBalanceAfter.safeSub(etherBalanceBefore);
WETH.deposit{value: receivedEtherBalance}();
} else {
ICToken cToken = ICToken(cTokenAddress);
require(cToken.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CTOKEN");
}
}
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
}
}

View File

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

View File

@@ -81,10 +81,12 @@ import * as LiquidityProviderFeature from '../test/generated-artifacts/Liquidity
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
@@ -272,10 +274,12 @@ export const artifacts = {
BridgeAdapter: BridgeAdapter as ContractArtifact,
BridgeProtocols: BridgeProtocols as ContractArtifact,
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
MixinAaveV2: MixinAaveV2 as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCompound: MixinCompound as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,
MixinCurveV2: MixinCurveV2 as ContractArtifact,

View File

@@ -79,10 +79,12 @@ export * from '../test/generated-wrappers/liquidity_provider_feature';
export * from '../test/generated-wrappers/liquidity_provider_sandbox';
export * from '../test/generated-wrappers/log_metadata_transformer';
export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_aave_v2';
export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_compound';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';
export * from '../test/generated-wrappers/mixin_curve_v2';

View File

@@ -112,10 +112,12 @@
"test/generated-artifacts/LiquidityProviderSandbox.json",
"test/generated-artifacts/LogMetadataTransformer.json",
"test/generated-artifacts/MetaTransactionsFeature.json",
"test/generated-artifacts/MixinAaveV2.json",
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCompound.json",
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",
"test/generated-artifacts/MixinCurveV2.json",

View File

@@ -1,4 +1,117 @@
[
{
"version": "16.46.0",
"changes": [
{
"note": "Enable `Curve` ETH/CVX pool",
"pr": 394
}
],
"timestamp": 1641863395
},
{
"version": "16.45.2",
"changes": [
{
"note": "Handle 0 output samples and negative adjusted rate native orders in routing",
"pr": 387
}
],
"timestamp": 1641827361
},
{
"version": "16.45.1",
"changes": [
{
"note": "Update `Celo` intermediate tokens",
"pr": 390
}
],
"timestamp": 1641359319
},
{
"version": "16.45.0",
"changes": [
{
"note": "Capture router timings",
"pr": 388
}
],
"timestamp": 1641308410
},
{
"version": "16.44.0",
"changes": [
{
"note": "Update neon-router and use router estimated output amount",
"pr": 354
}
],
"timestamp": 1640778328
},
{
"version": "16.43.0",
"changes": [
{
"note": "`UniswapV3` support for `Optimism`",
"pr": 385
}
],
"timestamp": 1640364306
},
{
"version": "16.42.0",
"changes": [
{
"note": "`UniswapV3` support for `Polygon`",
"pr": 382
},
{
"note": "Update `Beethoven` Graphql url",
"pr": 383
}
],
"timestamp": 1640124159
},
{
"version": "16.41.0",
"changes": [
{
"note": "Update mcusd contract address, and made celo native asset",
"pr": 376
}
],
"timestamp": 1638827302
},
{
"version": "16.40.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"version": "16.39.0",
"changes": [
{
"note": "Curve ETH/CRV pool",
"pr": 378
}
]
},
{
"version": "16.38.0",
"changes": [
{
"note": "Capture sampler metrics",
"pr": 374
}
],
"timestamp": 1638228231
},
{
"version": "16.37.0",
"changes": [

View File

@@ -5,6 +5,51 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v16.46.0 - _January 11, 2022_
* Enable `Curve` ETH/CVX pool (#394)
## v16.45.2 - _January 10, 2022_
* Handle 0 output samples and negative adjusted rate native orders in routing (#387)
## v16.45.1 - _January 5, 2022_
* Update `Celo` intermediate tokens (#390)
## v16.45.0 - _January 4, 2022_
* Capture router timings (#388)
## v16.44.0 - _December 29, 2021_
* Update neon-router and use router estimated output amount (#354)
## v16.43.0 - _December 24, 2021_
* `UniswapV3` support for `Optimism` (#385)
## v16.42.0 - _December 21, 2021_
* `UniswapV3` support for `Polygon` (#382)
* Update `Beethoven` Graphql url (#383)
## v16.41.0 - _December 6, 2021_
* Update mcusd contract address, and made celo native asset (#376)
## v16.40.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v16.39.0 - _Invalid date_
* Curve ETH/CRV pool (#378)
## v16.38.0 - _November 29, 2021_
* Capture sampler metrics (#374)
## v16.37.0 - _November 19, 2021_
* Changed Sushiswap router address (#373)

View File

@@ -0,0 +1,96 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./SamplerUtils.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
// Minimal CToken interface
interface ICToken {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function exchangeRateStored() external view returns (uint);
function decimals() external view returns (uint8);
}
contract CompoundSampler is SamplerUtils {
uint256 constant private EXCHANGE_RATE_SCALE = 1e10;
function sampleSellsFromCompound(
ICToken cToken,
IERC20TokenV06 takerToken,
IERC20TokenV06 makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
uint256 exchangeRate = cToken.exchangeRateStored();
uint256 cTokenDecimals = uint256(cToken.decimals());
if (address(makerToken) == address(cToken)) {
// mint
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = (takerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals) / exchangeRate;
}
} else if (address(takerToken) == address(cToken)) {
// redeem
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = (takerTokenAmounts[i] * exchangeRate) / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
}
}
}
function sampleBuysFromCompound(
ICToken cToken,
IERC20TokenV06 takerToken,
IERC20TokenV06 makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
uint256 exchangeRate = cToken.exchangeRateStored();
uint256 cTokenDecimals = uint256(cToken.decimals());
if (address(makerToken) == address(cToken)) {
// mint
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = makerTokenAmounts[i] * exchangeRate / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
}
} else if (address(takerToken) == address(cToken)) {
// redeem
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = (makerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals)/exchangeRate;
}
}
}
}

View File

@@ -23,6 +23,7 @@ pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol";
import "./BancorSampler.sol";
import "./CompoundSampler.sol";
import "./CurveSampler.sol";
import "./DODOSampler.sol";
import "./DODOV2Sampler.sol";
@@ -48,6 +49,7 @@ contract ERC20BridgeSampler is
BalancerSampler,
BalancerV2Sampler,
BancorSampler,
CompoundSampler,
CurveSampler,
DODOSampler,
DODOV2Sampler,

View File

@@ -51,7 +51,7 @@ interface IUniswapV3Pool {
contract UniswapV3Sampler
{
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
uint256 constant private QUOTE_GAS = 300e3;
uint256 constant private QUOTE_GAS = 600e3;
/// @dev Sample sell quotes from UniswapV3.
/// @param quoter UniswapV3 Quoter contract.

View File

@@ -77,4 +77,19 @@ contract UtilitySampler {
assembly { size := extcodesize(account) }
return size > 0;
}
function getGasLeft()
public
returns (uint256)
{
return gasleft();
}
function getBlockNumber()
public
view
returns (uint256)
{
return block.number;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.37.0",
"version": "16.46.0",
"engines": {
"node": ">=6.12"
},
@@ -39,7 +39,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|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}
@@ -60,14 +60,14 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-wrappers": "^13.18.3",
"@0x/contracts-erc20": "^3.3.23",
"@0x/contracts-zero-ex": "^0.29.5",
"@0x/contract-addresses": "^6.11.0",
"@0x/contract-wrappers": "^13.18.5",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-zero-ex": "^0.30.1",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/neon-router": "^0.2.1",
"@0x/protocol-utils": "^1.9.5",
"@0x/neon-router": "^0.3.1",
"@0x/protocol-utils": "^1.10.1",
"@0x/quote-server": "^6.0.6",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
@@ -98,10 +98,10 @@
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-libs": "^4.3.37",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.14",
"@0x/contracts-utils": "^4.8.4",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/contracts-utils": "^4.8.6",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.1.12",
"@0x/migrations": "^8.1.14",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -28,7 +28,6 @@ const ONE_SECOND_MS = 1000;
const ONE_MINUTE_SECS = 60;
const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS;
const DEFAULT_PER_PAGE = 1000;
const ZERO_AMOUNT = new BigNumber(0);
const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180;
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
@@ -43,6 +42,7 @@ const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
// default 50% buffer for selecting native orders to be aggregated with other sources
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
export const ZERO_AMOUNT = new BigNumber(0);
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
chainId: ChainId.Mainnet,
orderRefreshIntervalMs: 10000, // 10 seconds

View File

@@ -113,6 +113,7 @@ export {
SwapQuoterError,
SwapQuoterOpts,
SwapQuoterRfqOpts,
SamplerMetrics,
} from './types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export {

View File

@@ -0,0 +1,57 @@
import { BigNumber } from '@0x/utils';
import { ZERO_AMOUNT } from '../constants';
export interface AaveInfo {
lendingPool: string;
aToken: string;
underlyingToken: string;
}
// tslint:disable-next-line:no-unnecessary-class
export class AaveV2Sampler {
public static sampleSellsFromAaveV2(
aaveInfo: AaveInfo,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
): BigNumber[] {
// Deposit/Withdrawal underlying <-> aToken is always 1:1
if (
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
) {
return takerTokenAmounts;
}
// Not matching the reserve return 0 results
const numSamples = takerTokenAmounts.length;
const makerTokenAmounts = new Array(numSamples);
makerTokenAmounts.fill(ZERO_AMOUNT);
return makerTokenAmounts;
}
public static sampleBuysFromAaveV2(
aaveInfo: AaveInfo,
takerToken: string,
makerToken: string,
makerTokenAmounts: BigNumber[],
): BigNumber[] {
// Deposit/Withdrawal underlying <-> aToken is always 1:1
if (
(takerToken.toLowerCase() === aaveInfo.aToken.toLowerCase() &&
makerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase()) ||
(takerToken.toLowerCase() === aaveInfo.underlyingToken.toLowerCase() &&
makerToken.toLowerCase() === aaveInfo.aToken.toLowerCase())
) {
return makerTokenAmounts;
}
// Not matching the reserve return 0 results
const numSamples = makerTokenAmounts.length;
const takerTokenAmounts = new Array(numSamples);
takerTokenAmounts.fill(ZERO_AMOUNT);
return takerTokenAmounts;
}
}

View File

@@ -19,6 +19,7 @@ import {
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';

View File

@@ -223,7 +223,17 @@ export async function returnQuoteFromAltMMAsync<ResponseT>(
cancelToken,
})
.catch(err => {
warningLogger(err, `Alt RFQ MM request failed`);
if (err.response) {
// request was made and market maker responded
warningLogger(
{ data: err.response.data, status: err.response.status, headers: err.response.headers },
`Alt RFQ MM request failed`,
);
} else if (err.request) {
warningLogger({}, 'Alt RFQ MM no response received');
} else {
warningLogger({ err: err.message }, 'Failed to construct Alt RFQ MM request');
}
throw new Error(`Alt RFQ MM request failed`);
});

View File

@@ -0,0 +1,106 @@
import { logUtils } from '@0x/utils';
import { gql, request } from 'graphql-request';
import { constants } from '../../constants';
const RESERVES_GQL_QUERY = gql`
{
reserves(
first: 300
where: { isActive: true, isFrozen: false }
orderBy: totalLiquidity
orderDirection: desc
) {
id
underlyingAsset
aToken {
id
}
pool {
id
lendingPool
}
}
}
`;
export interface AaveReserve {
id: string;
underlyingAsset: string;
aToken: {
id: string;
};
pool: {
id: string;
lendingPool: string;
};
}
interface Cache {
[key: string]: AaveReserve[];
}
// tslint:disable-next-line:custom-no-magic-numbers
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**
* Fetches Aave V2 reserve information from the official subgraph(s).
* The reserve information is updated every 30 minutes and cached
* so that it can be accessed with the underlying token's address
*/
export class AaveV2ReservesCache {
private _cache: Cache = {};
constructor(private readonly _subgraphUrl: string) {
const resfreshReserves = async () => this.fetchAndUpdateReservesAsync();
// tslint:disable-next-line:no-floating-promises
resfreshReserves();
setInterval(resfreshReserves, RESERVES_REFRESH_INTERVAL_MS);
}
/**
* Fetches Aave V2 reserves from the subgraph and updates the cache
*/
public async fetchAndUpdateReservesAsync(): Promise<void> {
try {
const { reserves } = await request<{ reserves: AaveReserve[] }>(this._subgraphUrl, RESERVES_GQL_QUERY);
const newCache = reserves.reduce<Cache>((memo, reserve) => {
const underlyingAsset = reserve.underlyingAsset.toLowerCase();
if (!memo[underlyingAsset]) {
memo[underlyingAsset] = [];
}
memo[underlyingAsset].push(reserve);
return memo;
}, {});
this._cache = newCache;
} catch (err) {
logUtils.warn(`Failed to update Aave V2 reserves cache: ${err.message}`);
// Empty cache just to be safe
this._cache = {};
}
}
public get(takerToken: string, makerToken: string): AaveReserve | undefined {
// Deposit takerToken into reserve
if (this._cache[takerToken.toLowerCase()]) {
const matchingReserve = this._cache[takerToken.toLowerCase()].find(
r => r.aToken.id === makerToken.toLowerCase(),
);
if (matchingReserve) {
return matchingReserve;
}
}
// Withdraw makerToken from reserve
if (this._cache[makerToken.toLowerCase()]) {
const matchingReserve = this._cache[makerToken.toLowerCase()].find(
r => r.aToken.id === takerToken.toLowerCase(),
);
if (matchingReserve) {
return matchingReserve;
}
}
// No match
return undefined;
}
}

View File

@@ -54,6 +54,7 @@ import {
UNISWAPV2_ROUTER_BY_CHAIN_ID,
WAULTSWAP_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
MOBIUS_CELO_INFOS,
} from './constants';
import { CurveInfo, ERC20BridgeSource } from './types';
@@ -247,6 +248,19 @@ export function getNerveInfosForPair(chainId: ChainId, takerToken: string, maker
);
}
export function getMobiusInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Celo) {
return [];
}
return Object.values(MOBIUS_CELO_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
}
export function getFirebirdOneSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId === ChainId.BSC) {
return Object.values(FIREBIRDONESWAP_BSC_INFOS).filter(c =>
@@ -413,7 +427,8 @@ export function getCurveLikeInfosForPair(
| ERC20BridgeSource.IronSwap
| ERC20BridgeSource.XSigma
| ERC20BridgeSource.FirebirdOneSwap
| ERC20BridgeSource.ACryptos,
| ERC20BridgeSource.ACryptos
| ERC20BridgeSource.MobiusMoney,
): CurveDetailedInfo[] {
let pools: CurveInfo[] = [];
switch (source) {
@@ -456,6 +471,9 @@ export function getCurveLikeInfosForPair(
case ERC20BridgeSource.ACryptos:
pools = getAcryptosInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.MobiusMoney:
pools = getMobiusInfoForPair(chainId, takerToken, makerToken);
break;
default:
throw new Error(`Unknown Curve like source ${source}`);
}

View File

@@ -0,0 +1,78 @@
import { logUtils } from '@0x/utils';
import axios from 'axios';
import { constants } from '../../constants';
export interface CToken {
tokenAddress: string;
underlyingAddress: string;
}
interface CTokenApiResponse {
cToken: Array<{
token_address: string;
underlying_address: string;
}>;
}
interface Cache {
[key: string]: CToken;
}
// tslint:disable-next-line:custom-no-magic-numbers
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**
* Fetches a list of CTokens from Compound's official API.
* The token information is updated every 30 minutes and cached
* so that it can be accessed with the underlying token's address.
*/
export class CompoundCTokenCache {
private _cache: Cache = {};
constructor(private readonly _apiUrl: string, private readonly _wethAddress: string) {
const refreshCTokenCache = async () => this.fetchAndUpdateCTokensAsync();
// tslint:disable-next-line:no-floating-promises
refreshCTokenCache();
setInterval(refreshCTokenCache, CTOKEN_REFRESH_INTERVAL_MS);
}
public async fetchAndUpdateCTokensAsync(): Promise<void> {
try {
const { data } = await axios.get<CTokenApiResponse>(`${this._apiUrl}/ctoken`);
const newCache = data?.cToken.reduce<Cache>((memo, cToken) => {
// NOTE: Re-map cETH with null underlying token address to WETH address (we only handle WETH internally)
const underlyingAddressClean = cToken.underlying_address
? cToken.underlying_address.toLowerCase()
: this._wethAddress;
const tokenData: CToken = {
tokenAddress: cToken.token_address.toLowerCase(),
underlyingAddress: underlyingAddressClean,
};
memo[underlyingAddressClean] = tokenData;
return memo;
}, {});
this._cache = newCache;
} catch (err) {
logUtils.warn(`Failed to update Compound cToken cache: ${err.message}`);
// NOTE: Safe to keep already cached data as tokens should only be added to the list
}
}
public get(takerToken: string, makerToken: string): CToken | undefined {
// mint cToken
let cToken = this._cache[takerToken.toLowerCase()];
if (cToken && makerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
return cToken;
}
// redeem cToken
cToken = this._cache[makerToken.toLowerCase()];
if (cToken && takerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
return cToken;
}
// No match
return undefined;
}
}

View File

@@ -7,7 +7,9 @@ import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
import { SourceFilters } from './source_filters';
import {
AaveV2FillData,
BancorFillData,
CompoundFillData,
CurveFillData,
CurveFunctionSelectors,
CurveInfo,
@@ -59,6 +61,7 @@ function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue:
[ChainId.Avalanche]: defaultValue,
[ChainId.Fantom]: defaultValue,
[ChainId.Celo]: defaultValue,
[ChainId.Optimism]: defaultValue,
...(rest || {}),
};
}
@@ -101,6 +104,9 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -159,6 +165,8 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.JetSwap,
ERC20BridgeSource.IronSwap,
ERC20BridgeSource.AaveV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -168,6 +176,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Curve,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.KyberDmm,
ERC20BridgeSource.AaveV2,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -184,7 +193,9 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UbeSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.MobiusMoney,
]),
[ChainId.Optimism]: new SourceFilters([ERC20BridgeSource.UniswapV3]),
},
new SourceFilters([]),
);
@@ -227,6 +238,9 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.UniswapV3,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.ShibaSwap,
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// ERC20BridgeSource.AaveV2,
// ERC20BridgeSource.Compound,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -285,6 +299,8 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.JetSwap,
ERC20BridgeSource.IronSwap,
ERC20BridgeSource.AaveV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Avalanche]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -294,6 +310,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Curve,
ERC20BridgeSource.CurveV2,
ERC20BridgeSource.KyberDmm,
ERC20BridgeSource.AaveV2,
]),
[ChainId.Fantom]: new SourceFilters([
ERC20BridgeSource.MultiHop,
@@ -307,10 +324,12 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.SushiSwap,
]),
[ChainId.Celo]: new SourceFilters([
ERC20BridgeSource.MobiusMoney,
ERC20BridgeSource.UbeSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
]),
[ChainId.Optimism]: new SourceFilters([ERC20BridgeSource.UniswapV3]),
},
new SourceFilters([]),
);
@@ -328,10 +347,11 @@ 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.Polygon]: [ERC20BridgeSource.QuickSwap, ERC20BridgeSource.SushiSwap],
[ChainId.Polygon]: [ERC20BridgeSource.QuickSwap, ERC20BridgeSource.SushiSwap, ERC20BridgeSource.UniswapV3],
[ChainId.Avalanche]: [ERC20BridgeSource.Pangolin, ERC20BridgeSource.TraderJoe, ERC20BridgeSource.SushiSwap],
[ChainId.Fantom]: [ERC20BridgeSource.SpiritSwap, ERC20BridgeSource.SpookySwap, ERC20BridgeSource.SushiSwap],
[ChainId.Celo]: [ERC20BridgeSource.UbeSwap, ERC20BridgeSource.SushiSwap],
[ChainId.Optimism]: [ERC20BridgeSource.UniswapV3],
},
[],
);
@@ -444,6 +464,7 @@ export const MAINNET_TOKENS = {
CRV: '0xd533a949740bb3306d119cc777fa900ba034cd52',
MIM: '0x99d8a9c45b2eca8864373a26d1459e3dff1e17f3',
EURT: '0xc581b735a1688071a1746c968e0798d642ede491',
CVX: '0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b',
};
export const BSC_TOKENS = {
@@ -492,9 +513,46 @@ export const AVALANCHE_TOKENS = {
};
export const CELO_TOKENS = {
WETH: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
WCELO: '0x471ece3750da237f93b8e339c536989b8978a438',
// Some of these tokens are Optics bridge? tokens which
// had an issue and migrated from v1 to v2
//WETHv1: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
WETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455',
WBTC: '0xbaab46e28388d2779e6e31fd00cf0e5ad95e327b',
cUSD: '0x765de816845861e75a25fca122bb6898b8b1282a',
// ??
WBTCv1: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b',
cETH: '0x2def4285787d58a2f811af24755a8150622f4361',
UBE: '0x00be915b9dcf56a3cbe739d9b9c202ca692409ec',
// Moolah
mCELO: '0x7d00cd74ff385c955ea3d79e47bf06bd7386387d',
mCUSD: '0x918146359264c492bd6934071c6bd31c854edbc3',
mCEUR: '0xe273ad7ee11dcfaa87383ad5977ee1504ac07568',
amCUSD: '0x64defa3544c695db8c535d289d843a189aa26b98',
MOO: '0x17700282592d6917f6a73d0bf8accf4d578c131e',
//Mobius Money
WETHv1: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
CELO: '0x471ece3750da237f93b8e339c536989b8978a438',
mCUSD: '0x64defa3544c695db8c535d289d843a189aa26b98',
mCUSDm: '0x64defa3544c695db8c535d289d843a189aa26b98',
wBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
wETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
wBTCO: '0xbe50a3013a1c94768a1abb78c3cb79ab28fc1ace',
pUSDCxV1: '0xcc82628f6a8defa1e2b0ad7ed448bef3647f7941',
cUSDC: '0x2a3684e9dc20b857375ea04235f2f7edbe818fa7',
cUSDC_V2: '0xef4229c8c3250c675f21bcefa42f58efbff6002a',
pUSDC_V2: '0x1bfc26ce035c368503fae319cc2596716428ca44',
wBTC_V2: '0xbaabl46e28388d2779e6e31fd00cf0e5ad95e327b',
cUSDTm: '0xcfffe0c89a779c09df3df5624f54cdf7ef5fdd5d',
cUSDCm: '0x93db49be12b864019da9cb147ba75cdc0506190e',
cBTC: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b',
asUSDC: '0xcd7d7ff64746c1909e44db8e95331f9316478817',
aaUSDC: '0xb70e0a782b058bfdb0d109a3599bec1f19328e36',
pUSDC: '0x1bfc26ce035c368503fae319cc2596716428ca44',
oWETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455',
oDAI: '0x90ca507a5d4458a4c6c6249d186b6dcb02a5bccd',
pCELO: '0x301a61d01a63c8d670c2b8a43f37d12ef181f997',
pUSD: '0xeadf4a7168a82d30ba0619e64d5bcf5b30b45226',
};
export const FANTOM_TOKENS = {
@@ -507,6 +565,14 @@ export const FANTOM_TOKENS = {
renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501',
};
export const OPTIMISM_TOKENS = {
WETH: '0x4200000000000000000000000000000000000006',
USDC: '0x7f5c764cbc14f9669b88837ca1490cca17c31607',
USDT: '0x94b008aa00579c1307b0ef2c499ad98a8ce58e58',
DAI: '0xda10009cbd5d07dd0cecc66161fc93d7c9000da1',
WBTC: '0x68f180fcce6836688e9084f035309e29bf0a2095',
};
export const CURVE_POOLS = {
compound: '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56', // 0.Compound
// 1.USDT is dead
@@ -551,6 +617,8 @@ export const CURVE_POOLS = {
cvxcrv: '0x9d0464996170c6b9e75eed71c68b99ddedf279e8',
mim: '0x5a6a4d54456819380173272a5e8e9b9904bdf41b',
eurt: '0xfd5db7463a3ab53fd211b4af195c5bccc1a03890',
ethcrv: '0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511',
ethcvx: '0xb576491f1e6e5e62f1d8f26062ee822b40b0e0d4',
};
export const CURVE_V2_POOLS = {
@@ -620,6 +688,24 @@ export const NERVE_POOLS = {
threePool: '0x1b3771a66ee31180906972580ade9b81afc5fcdc',
};
export const MOBIUS_CELO_POOLS = {
poof_cusd_v2: '0xa2f0e57d4ceacf025e81c76f28b9ad6e9fbe8735',
poof_celo_v2: '0xfc9e2c63370d8deb3521922a7b2b60f4cff7e75a',
usdc_optics_v2: '0x9906589ea8fd27504974b7e8201df5bbde986b03',
dai_optics_v2: '0xf3f65dfe0c8c8f2986da0fec159abe6fd4e700b4',
weth_optics_v2: '0x74ef28d635c6c5800dd3cd62d4c4f8752daacb09',
wbtc_optics_v2: '0xaefc4e8cf655a182e8346b24c8abce45616ee0d2',
pusdc_optics_v2: '0xcce0d62ce14fb3e4363eb92db37ff3630836c252',
usdc_allbridge_avax: '0x0986b42f5f9c42feeef66fc23eba9ea1164c916d',
usdc_allbridge_solana: '0x63c1914bf00a9b395a2bf89aada55a5615a3656e',
usdc_eth_optics: '0xa5037661989789d0310ac2b796fa78f1b01f195d',
usdc_poly_optics: '0x2080aaa167e2225e1fc9923250ba60e19a180fb2',
wbtc: '0x19260b9b573569ddb105780176547875fe9feda3',
weth: '0xe0f2cc70e52f05edb383313393d88df2937da55a',
usdt_moss: '0xdbf27fd2a702cc02ac7acf0aea376db780d53247',
usdc_moss: '0x0ff04189Ef135b6541E56f7C638489De92E9c778',
};
export const BELT_POOLS = {
vPool: '0xf16d312d119c13dd27fd0dc814b0bcdcaaa62dfd',
};
@@ -686,7 +772,35 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
AVALANCHE_TOKENS.USDC,
],
[ChainId.Fantom]: [FANTOM_TOKENS.WFTM, FANTOM_TOKENS.WETH, FANTOM_TOKENS.DAI, FANTOM_TOKENS.USDC],
[ChainId.Celo]: [CELO_TOKENS.mCUSD, CELO_TOKENS.WETH, CELO_TOKENS.CELO],
[ChainId.Celo]: [
CELO_TOKENS.WCELO,
CELO_TOKENS.mCUSD,
CELO_TOKENS.WETH,
CELO_TOKENS.amCUSD,
CELO_TOKENS.WBTC,
CELO_TOKENS.WETHv1,
CELO_TOKENS.CELO,
CELO_TOKENS.mCUSDm,
CELO_TOKENS.wBTC,
CELO_TOKENS.wETH,
CELO_TOKENS.wBTCO,
CELO_TOKENS.pUSDCxV1,
CELO_TOKENS.cUSDC,
CELO_TOKENS.cUSDC_V2,
CELO_TOKENS.pUSDC_V2,
CELO_TOKENS.wBTC_V2,
CELO_TOKENS.cUSDTm,
CELO_TOKENS.cUSDCm,
CELO_TOKENS.cBTC,
CELO_TOKENS.asUSDC,
CELO_TOKENS.aaUSDC,
CELO_TOKENS.pUSDC,
CELO_TOKENS.oWETH,
CELO_TOKENS.oDAI,
CELO_TOKENS.pCELO,
CELO_TOKENS.pUSD,
],
[ChainId.Optimism]: [OPTIMISM_TOKENS.WETH, OPTIMISM_TOKENS.DAI, OPTIMISM_TOKENS.USDC],
},
[],
);
@@ -726,6 +840,9 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
[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(),
);
@@ -742,6 +859,7 @@ export const NATIVE_FEE_TOKEN_BY_CHAIN_ID = valueByChainId<string>(
[ChainId.Avalanche]: getContractAddressesForChainOrThrow(ChainId.Avalanche).etherToken,
[ChainId.Fantom]: getContractAddressesForChainOrThrow(ChainId.Fantom).etherToken,
[ChainId.Celo]: getContractAddressesForChainOrThrow(ChainId.Celo).etherToken,
[ChainId.Optimism]: getContractAddressesForChainOrThrow(ChainId.Optimism).etherToken,
},
NULL_ADDRESS,
);
@@ -1016,6 +1134,28 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: CURVE_POOLS.cvxcrv,
gasSchedule: 105e3,
}),
[CURVE_POOLS.ethcrv]: {
...createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.CRV],
pool: CURVE_POOLS.ethcrv,
gasSchedule: 350e3,
}),
// This pool has a custom get_dy and exchange selector with uint256
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
},
[CURVE_POOLS.ethcvx]: {
...createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.CVX],
pool: CURVE_POOLS.ethcvx,
gasSchedule: 350e3,
}),
// This pool has a custom get_dy and exchange selector with uint256
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
},
};
export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = {
@@ -1273,6 +1413,144 @@ export const NERVE_BSC_INFOS: { [name: string]: CurveInfo } = {
},
};
export const MOBIUS_CELO_INFOS: { [name: string]: CurveInfo } = {
[MOBIUS_CELO_POOLS.poof_cusd_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.poof_cusd_v2,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSD],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.poof_celo_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.poof_celo_v2,
tokens: [CELO_TOKENS.CELO, CELO_TOKENS.pCELO],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_optics_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_optics_v2,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDC_V2],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.dai_optics_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.dai_optics_v2,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.oDAI],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.wbtc_optics_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.wbtc_optics_v2,
tokens: [CELO_TOKENS.cBTC, CELO_TOKENS.wBTC_V2],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.weth_optics_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.weth_optics_v2,
tokens: [CELO_TOKENS.cETH, CELO_TOKENS.oWETH],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.pusdc_optics_v2]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.pusdc_optics_v2,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSDC],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_allbridge_avax]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_allbridge_avax,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.aaUSDC],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_allbridge_solana]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_allbridge_solana,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.asUSDC],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_eth_optics]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_eth_optics,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDC],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_poly_optics]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_poly_optics,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSDCxV1],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.wbtc]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.wbtc,
tokens: [CELO_TOKENS.cBTC, CELO_TOKENS.wBTCO],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.weth]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.weth,
tokens: [CELO_TOKENS.cETH, CELO_TOKENS.WETHv1],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdc_moss]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_moss,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDCm],
metaTokens: undefined,
gasSchedule: 140e3,
},
[MOBIUS_CELO_POOLS.usdt_moss]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
poolAddress: MOBIUS_CELO_POOLS.usdc_moss,
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDTm],
metaTokens: undefined,
gasSchedule: 140e3,
},
};
export const FIREBIRDONESWAP_BSC_INFOS: { [name: string]: CurveInfo } = {
[FIREBIRDONESWAP_BSC_POOLS.oneswap]: {
exchangeFunctionSelector: CurveFunctionSelectors.swap,
@@ -1669,9 +1947,9 @@ export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
{
[ChainId.Fantom]: 'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx-v4',
[ChainId.Fantom]: 'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx',
},
'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx-v4',
'https://graph-node.beets-ftm-node.com/subgraphs/name/beethovenx',
);
export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
@@ -1684,10 +1962,36 @@ export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
quoter: '0x2f9e608fd881861b8916257b76613cb22ee0652c',
router: '0x03782388516e94fcd4c18666303601a12aa729ea',
},
[ChainId.Polygon]: {
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
[ChainId.Optimism]: {
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
},
{ quoter: NULL_ADDRESS, router: NULL_ADDRESS },
);
export const AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID = valueByChainId(
{
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// [ChainId.Mainnet]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2',
[ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/aave/aave-v2-matic',
[ChainId.Avalanche]: 'https://api.thegraph.com/subgraphs/name/aave/protocol-v2-avalanche',
},
null,
);
export const COMPOUND_API_URL_BY_CHAIN_ID = valueByChainId(
{
// TODO: enable after FQT has been redeployed on Ethereum mainnet
// [ChainId.Mainnet]: 'https://api.compound.finance/api/v2',
},
null,
);
//
// BSC
//
@@ -1893,6 +2197,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.IronSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.XSigma]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.FirebirdOneSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.MobiusMoney]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.MultiBridge]: () => 350e3,
[ERC20BridgeSource.UniswapV2]: uniswapV2CloneGasSchedule,
[ERC20BridgeSource.SushiSwap]: uniswapV2CloneGasSchedule,
@@ -1953,6 +2258,21 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
return gas;
},
[ERC20BridgeSource.Lido]: () => 226e3,
[ERC20BridgeSource.AaveV2]: (fillData?: FillData) => {
const aaveFillData = fillData as AaveV2FillData;
// NOTE: The Aave deposit method is more expensive than the withdraw
return aaveFillData.takerToken === aaveFillData.underlyingToken ? 400e3 : 300e3;
},
[ERC20BridgeSource.Compound]: (fillData?: FillData) => {
// NOTE: cETH is handled differently than other cTokens
const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet];
const compoundFillData = fillData as CompoundFillData;
if (compoundFillData.takerToken === compoundFillData.cToken) {
return compoundFillData.makerToken === wethAddress ? 120e3 : 150e3;
} else {
return compoundFillData.takerToken === wethAddress ? 210e3 : 250e3;
}
},
//
// BSC
@@ -2019,4 +2339,5 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: Omit<GetMarketOrdersOpts, 'gasPrice
shouldGenerateQuoteReport: true,
shouldIncludePriceComparisonsReport: false,
tokenAdjacencyGraph: { default: [] },
neonRouterNumSamples: 14,
};

View File

@@ -158,6 +158,8 @@ export class MarketOperationUtils {
// Call the sampler contract.
const samplerPromise = this._sampler.executeAsync(
this._sampler.getBlockNumber(),
this._sampler.getGasLeft(),
this._sampler.getTokenDecimals([makerToken, takerToken]),
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
@@ -184,6 +186,7 @@ export class MarketOperationUtils {
takerAmount,
),
this._sampler.isAddressContract(txOrigin),
this._sampler.getGasLeft(),
);
// Refresh the cached pools asynchronously if required
@@ -191,6 +194,8 @@ export class MarketOperationUtils {
const [
[
blockNumber,
gasBefore,
tokenDecimals,
orderFillableTakerAmounts,
outputAmountPerEth,
@@ -198,9 +203,14 @@ export class MarketOperationUtils {
dexQuotes,
rawTwoHopQuotes,
isTxOriginContract,
gasAfter,
],
] = await Promise.all([samplerPromise]);
// Log the gas metrics
_opts.samplerMetrics?.logGasDetails({ gasBefore, gasAfter });
_opts.samplerMetrics?.logBlockNumber(blockNumber);
// Filter out any invalid two hop quotes where we couldn't find a route
const twoHopQuotes = rawTwoHopQuotes.filter(
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
@@ -433,6 +443,7 @@ export class MarketOperationUtils {
feeSchedule: _opts.feeSchedule,
allowFallback: _opts.allowFallback,
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
},
);
return optimizerResult;
@@ -521,9 +532,18 @@ export class MarketOperationUtils {
penaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
opts.samplerMetrics,
);
} else {
optimalPath = await findOptimalPathJSAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
optimalPath = await findOptimalPathJSAsync(
side,
fills,
inputAmount,
opts.runLimit,
opts.samplerMetrics,
penaltyOpts,
);
}
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
@@ -586,6 +606,8 @@ export class MarketOperationUtils {
allowFallback: _opts.allowFallback,
exchangeProxyOverhead: _opts.exchangeProxyOverhead,
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
samplerMetrics: _opts.samplerMetrics,
};
if (nativeOrders.length === 0) {
@@ -796,6 +818,8 @@ export class MarketOperationUtils {
sturdyPenaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
);
} else {
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
@@ -804,6 +828,7 @@ export class MarketOperationUtils {
sturdyFills,
inputAmount,
opts.runLimit,
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
sturdyPenaltyOpts,
);
}

View File

@@ -5,11 +5,13 @@ import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
import {
AaveV2FillData,
AggregationError,
BalancerFillData,
BalancerV2FillData,
BancorFillData,
CollapsedFill,
CompoundFillData,
CurveFillData,
DexSample,
DODOFillData,
@@ -132,6 +134,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BakerySwap');
case ERC20BridgeSource.Nerve:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Nerve');
case ERC20BridgeSource.MobiusMoney:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
case ERC20BridgeSource.Belt:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt');
case ERC20BridgeSource.Ellipsis:
@@ -194,6 +198,10 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
case ERC20BridgeSource.MorpheusSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
case ERC20BridgeSource.AaveV2:
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
case ERC20BridgeSource.Compound:
return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -220,6 +228,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.MobiusMoney:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Smoothy:
@@ -339,6 +348,15 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
break;
case ERC20BridgeSource.AaveV2:
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
bridgeData = encoder.encode([aaveFillData.lendingPool, aaveFillData.aToken]);
break;
case ERC20BridgeSource.Compound:
const compoundFillData = (order as OptimizedMarketBridgeOrder<CompoundFillData>).fillData;
bridgeData = encoder.encode([compoundFillData.cToken]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -456,6 +474,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.FirebirdOneSwap]: curveEncoder,
[ERC20BridgeSource.IronSwap]: curveEncoder,
[ERC20BridgeSource.ACryptos]: curveEncoder,
[ERC20BridgeSource.MobiusMoney]: curveEncoder,
// UniswapV2 like, (router, address[])
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
@@ -504,6 +523,8 @@ export const BRIDGE_ENCODERS: {
]),
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -1,23 +1,21 @@
import { assert } from '@0x/assert';
import { ChainId } from '@0x/contract-addresses';
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { performance } from 'perf_hooks';
import { DEFAULT_INFO_LOGGER } from '../../constants';
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID } from '../market_operation_utils/constants';
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
import { getRate } from './rate_utils';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData } from './types';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types';
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
const RUN_LIMIT_DECAY_FACTOR = 0.5;
const RUST_ROUTER_NUM_SAMPLES = 200;
const FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD = new BigNumber(150e3);
// NOTE: The Rust router will panic with less than 3 samples
const MIN_NUM_SAMPLE_INPUTS = 3;
@@ -69,21 +67,6 @@ function calculateOuputFee(
}
}
// Use linear interpolation to approximate the output
// at a certain input somewhere between the two samples
// See https://en.wikipedia.org/wiki/Linear_interpolation
const interpolateOutputFromSamples = (
left: { input: BigNumber; output: BigNumber },
right: { input: BigNumber; output: BigNumber },
targetInput: BigNumber,
): BigNumber =>
left.output.plus(
right.output
.minus(left.output)
.dividedBy(right.input.minus(left.input))
.times(targetInput.minus(left.input)),
);
function findRoutesAndCreateOptimalPath(
side: MarketOperation,
samples: DexSample[][],
@@ -91,29 +74,27 @@ function findRoutesAndCreateOptimalPath(
input: BigNumber,
opts: PathPenaltyOpts,
fees: FeeSchedule,
neonRouterNumSamples: number,
): Path | undefined {
const createFill = (sample: DexSample) =>
dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees)[0];
// Track sample id's to integers (required by rust router)
const sampleIdLookup: { [key: string]: number } = {};
let sampleIdCounter = 0;
const sampleToId = (source: ERC20BridgeSource, index: number): number => {
const key = `${source}-${index}`;
if (sampleIdLookup[key]) {
return sampleIdLookup[key];
} else {
sampleIdLookup[key] = ++sampleIdCounter;
return sampleIdLookup[key];
const createFill = (sample: DexSample): Fill | undefined => {
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
if (fills.length === 0) {
return undefined;
}
return fills[0];
};
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
const serializedPaths: SerializedPath[] = [];
const sampleSourcePathIds: string[] = [];
for (const singleSourceSamples of samples) {
if (singleSourceSamples.length === 0) {
continue;
}
const sourcePathId = hexUtils.random();
const singleSourceSamplesWithOutput = [...singleSourceSamples];
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
if (singleSourceSamples[i].output.isZero()) {
@@ -131,7 +112,7 @@ function findRoutesAndCreateOptimalPath(
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
(memo, sample, sampleIdx) => {
memo.ids.push(sampleToId(sample.source, sampleIdx));
memo.ids.push(`${sample.source}-${serializedPaths.length}-${sampleIdx}`);
memo.inputs.push(sample.input.integerValue().toNumber());
memo.outputs.push(sample.output.integerValue().toNumber());
memo.outputFees.push(
@@ -152,8 +133,10 @@ function findRoutesAndCreateOptimalPath(
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
serializedPaths.push(serializedPath);
sampleSourcePathIds.push(sourcePathId);
}
const nativeOrdersourcePathId = hexUtils.random();
for (const [idx, nativeOrder] of nativeOrders.entries()) {
const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts(
side,
@@ -164,32 +147,25 @@ function findRoutesAndCreateOptimalPath(
if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) {
continue;
}
// HACK: the router requires at minimum 3 samples as a basis for interpolation
const inputs = [
0,
normalizedOrderInput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderInput.integerValue().toNumber(),
];
const outputs = [
0,
normalizedOrderOutput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderOutput.integerValue().toNumber(),
];
// NOTE: same fee no matter if full or partial fill
const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
.integerValue()
.toNumber();
const outputFees = [fee, fee, fee];
// NOTE: ids can be the same for all fake samples
const id = sampleToId(ERC20BridgeSource.Native, idx);
const ids = [id, id, id];
// HACK: due to an issue with the Rust router interpolation we need to create exactly 13 samples from the native order
const ids = [];
const inputs = [];
const outputs = [];
const outputFees = [];
for (let i = 1; i <= 13; i++) {
const fraction = i / 13;
const currentInput = BigNumber.min(normalizedOrderInput.times(fraction), normalizedOrderInput);
const currentOutput = BigNumber.min(normalizedOrderOutput.times(fraction), normalizedOrderOutput);
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
inputs.push(currentInput.integerValue().toNumber());
outputs.push(currentOutput.integerValue().toNumber());
outputFees.push(fee);
ids.push(id);
}
const serializedPath: SerializedPath = {
ids,
@@ -200,6 +176,7 @@ function findRoutesAndCreateOptimalPath(
samplesAndNativeOrdersWithResults.push([nativeOrder]);
serializedPaths.push(serializedPath);
sampleSourcePathIds.push(nativeOrdersourcePathId);
}
if (serializedPaths.length === 0) {
@@ -212,30 +189,33 @@ function findRoutesAndCreateOptimalPath(
pathsIn: serializedPaths,
};
const before = performance.now();
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
route(rustArgs, allSourcesRustRoute, RUST_ROUTER_NUM_SAMPLES);
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'real' },
'Rust router real routing performance',
);
const strategySourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
route(rustArgs, allSourcesRustRoute, strategySourcesOutputAmounts, neonRouterNumSamples);
assert.assert(
rustArgs.pathsIn.length === allSourcesRustRoute.length,
'different number of sources in the Router output than the input',
);
assert.assert(
rustArgs.pathsIn.length === strategySourcesOutputAmounts.length,
'different number of sources in the Router output amounts results than the input',
);
const routesAndSamples = _.zip(allSourcesRustRoute, samplesAndNativeOrdersWithResults);
const routesAndSamplesAndOutputs = _.zip(
allSourcesRustRoute,
samplesAndNativeOrdersWithResults,
strategySourcesOutputAmounts,
sampleSourcePathIds,
);
const adjustedFills: Fill[] = [];
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
const scale = input.dividedBy(totalRoutedAmount);
for (const [routeInput, routeSamplesAndNativeOrders] of routesAndSamples) {
if (!routeInput || !routeSamplesAndNativeOrders) {
for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) {
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount || !Number.isFinite(outputAmount)) {
continue;
}
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precisions loss for number/f64
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
const rustInputAdjusted = BigNumber.min(
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
@@ -251,14 +231,21 @@ function findRoutesAndCreateOptimalPath(
opts.outputAmountPerEth,
opts.inputAmountPerEth,
fees,
)[0];
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push(nativeFill);
)[0] as Fill | undefined;
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
// and nativeFill will be `undefined`
if (nativeFill) {
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
}
continue;
}
// NOTE: For DexSamples only
let fill = createFill(current);
if (!fill) {
continue;
}
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
// Descend to approach a closer fill for fillData which may not be consistent
// throughout the path (UniswapV3) and for a closer guesstimate at
@@ -267,49 +254,47 @@ function findRoutesAndCreateOptimalPath(
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
for (let k = routeSamples.length - 1; k >= 0; k--) {
if (k === 0) {
fill = createFill(routeSamples[0]);
fill = createFill(routeSamples[0]) ?? fill;
}
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
// Between here and the previous fill
// HACK: Use the midpoint between the two
const left = routeSamples[k];
const right = routeSamples[k + 1];
if (left && right) {
// Approximate how much output we get for the input with the surrounding samples
const interpolatedOutput = interpolateOutputFromSamples(
left,
right,
rustInputAdjusted,
).decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
fill = createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: interpolatedOutput,
});
fill =
createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: new BigNumber(outputAmount),
}) ?? fill;
} else {
assert.assert(Boolean(left || right), 'No valid sample to use');
fill = createFill(left || right);
fill = createFill(left || right) ?? fill;
}
break;
}
}
const scaleOutput = (output: BigNumber) =>
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
const scaleOutput = (fillInput: BigNumber, output: BigNumber) =>
output
.dividedBy(fill.input)
.dividedBy(fillInput)
.times(rustInputAdjusted)
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
adjustedFills.push({
...fill,
input: rustInputAdjusted,
output: scaleOutput(fill.output),
adjustedOutput: scaleOutput(fill.adjustedOutput),
output: scaleOutput(fill.input, fill.output),
adjustedOutput: scaleOutput(fill.input, fill.adjustedOutput),
index: 0,
parent: undefined,
sourcePathId: sourcePathId ?? hexUtils.random(),
});
}
if (adjustedFills.length === 0) {
return undefined;
}
const pathFromRustInputs = Path.create(side, adjustedFills, input);
return pathFromRustInputs;
@@ -323,15 +308,27 @@ export function findOptimalRustPathFromSamples(
opts: PathPenaltyOpts,
fees: FeeSchedule,
chainId: ChainId,
neonRouterNumSamples: number,
samplerMetrics?: SamplerMetrics,
): Path | undefined {
const before = performance.now();
const logPerformance = () =>
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'total' },
'Rust router total routing performance',
);
const allSourcesPath = findRoutesAndCreateOptimalPath(side, samples, nativeOrders, input, opts, fees);
const beforeAllTimeMs = performance.now();
let beforeTimeMs = performance.now();
const allSourcesPath = findRoutesAndCreateOptimalPath(
side,
samples,
nativeOrders,
input,
opts,
fees,
neonRouterNumSamples,
);
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'all',
timingMs: performance.now() - beforeTimeMs,
});
if (!allSourcesPath) {
return undefined;
}
@@ -341,11 +338,27 @@ export function findOptimalRustPathFromSamples(
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
// we need to try to route them in isolation and compare with the results all sources
if (vipSources.length > 0) {
beforeTimeMs = performance.now();
const vipSourcesSet = new Set(vipSources);
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
if (vipSourcesSamples.length > 0) {
const vipSourcesPath = findRoutesAndCreateOptimalPath(side, vipSourcesSamples, [], input, opts, fees);
const vipSourcesPath = findRoutesAndCreateOptimalPath(
side,
vipSourcesSamples,
[],
input,
opts,
fees,
neonRouterNumSamples,
);
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'vip',
timingMs: performance.now() - beforeTimeMs,
});
const { input: allSourcesInput, output: allSourcesOutput } = allSourcesPath.adjustedSize();
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
@@ -358,13 +371,18 @@ export function findOptimalRustPathFromSamples(
const allSourcesAdjustedRateWithFqtOverhead = getRate(side, allSourcesInput, outputWithFqtOverhead);
if (vipSourcesPath?.adjustedRate().isGreaterThan(allSourcesAdjustedRateWithFqtOverhead)) {
logPerformance();
return vipSourcesPath;
}
}
}
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'total',
timingMs: performance.now() - beforeAllTimeMs,
});
logPerformance();
return allSourcesPath;
}
@@ -377,8 +395,10 @@ export async function findOptimalPathJSAsync(
fills: Fill[][],
targetInput: BigNumber,
runLimit: number = 2 ** 8,
samplerMetrics?: SamplerMetrics,
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
): Promise<Path | undefined> {
const beforeTimeMs = performance.now();
// Sort fill arrays by descending adjusted completed rate.
// Remove any paths which cannot impact the optimal path
const sortedPaths = reducePaths(fillsToSortedPaths(fills, side, targetInput, opts), side);
@@ -392,7 +412,15 @@ export async function findOptimalPathJSAsync(
// Yield to event loop.
await Promise.resolve();
}
return optimalPath.isComplete() ? optimalPath : undefined;
const finalPath = optimalPath.isComplete() ? optimalPath : undefined;
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'js',
type: 'total',
timingMs: performance.now() - beforeTimeMs,
});
return finalPath;
}
// Sort fill arrays by descending adjusted completed rate.

View File

@@ -14,7 +14,8 @@ import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAd
*/
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
const distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
const distributionSum = BigNumber.sum(...distribution);
const stepSizes = distribution.map(d => d.div(distributionSum));
const amounts = stepSizes.map((_s, i) => {
if (i === numSamples - 1) {
return maxFillAmount;
@@ -130,6 +131,37 @@ export class DexOrderSampler extends SamplerOperations {
BatchedOperationResult<T8>
]>;
// prettier-ignore
public async executeAsync<
T1, T2, T3, T4, T5, T6, T7, T8, T9
>(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9]): Promise<[
BatchedOperationResult<T1>,
BatchedOperationResult<T2>,
BatchedOperationResult<T3>,
BatchedOperationResult<T4>,
BatchedOperationResult<T5>,
BatchedOperationResult<T6>,
BatchedOperationResult<T7>,
BatchedOperationResult<T8>,
BatchedOperationResult<T9>
]>;
// prettier-ignore
public async executeAsync<
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
>(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Promise<[
BatchedOperationResult<T1>,
BatchedOperationResult<T2>,
BatchedOperationResult<T3>,
BatchedOperationResult<T4>,
BatchedOperationResult<T5>,
BatchedOperationResult<T6>,
BatchedOperationResult<T7>,
BatchedOperationResult<T8>,
BatchedOperationResult<T9>,
BatchedOperationResult<T10>,
]>;
/**
* Run a series of operations from `DexOrderSampler.ops` in a single transaction.
*/

View File

@@ -0,0 +1,36 @@
import { BigNumber, logUtils, NULL_BYTES } from '@0x/utils';
import { ERC20BridgeSource, FillData, SourceQuoteOperation } from './types';
interface SamplerNoOperationCall {
callback: () => BigNumber[];
}
/**
* SamplerNoOperation can be used for sources where we already have all the necessary information
* required to perform the sample operations, without needing access to any on-chain data. Using a noop sample
* you can skip the eth_call, and just calculate the results directly in typescript land.
*/
export class SamplerNoOperation<TFillData extends FillData = FillData> implements SourceQuoteOperation<TFillData> {
public readonly source: ERC20BridgeSource;
public fillData: TFillData;
private readonly _callback: () => BigNumber[];
constructor(opts: { source: ERC20BridgeSource; fillData?: TFillData } & SamplerNoOperationCall) {
this.source = opts.source;
this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion
this._callback = opts.callback;
}
// tslint:disable-next-line:prefer-function-over-method
public encodeCall(): string {
return NULL_BYTES;
}
public handleCallResults(_callResults: string): BigNumber[] {
return this._callback();
}
public handleRevert(_callResults: string): BigNumber[] {
logUtils.warn(`SamplerNoOperation: ${this.source} reverted`);
return [];
}
}

View File

@@ -3,9 +3,11 @@ import { LimitOrderFields } from '@0x/protocol-utils';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
import { SamplerCallResult, SignedNativeOrder } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { AaveV2ReservesCache } from './aave_reserves_cache';
import { BancorService } from './bancor_service';
import {
getCurveLikeInfosForPair,
@@ -17,11 +19,14 @@ import {
isValidAddress,
uniswapV2LikeRouterAddress,
} from './bridge_source_utils';
import { CompoundCTokenCache } from './compound_ctoken_cache';
import {
AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID,
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN,
BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN,
COMPOUND_API_URL_BY_CHAIN_ID,
DODOV1_CONFIG_BY_CHAIN_ID,
DODOV2_FACTORIES_BY_CHAIN_ID,
KYBER_CONFIG_BY_CHAIN_ID,
@@ -45,13 +50,17 @@ import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { getIntermediateTokens } from './multihop_utils';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache';
import { SamplerContractOperation } from './sampler_contract_operation';
import { SamplerNoOperation } from './sampler_no_operation';
import { SourceFilters } from './source_filters';
import {
AaveV2FillData,
AaveV2Info,
BalancerFillData,
BalancerV2FillData,
BalancerV2PoolInfo,
BancorFillData,
BatchedOperation,
CompoundFillData,
CurveFillData,
CurveInfo,
DexSample,
@@ -99,6 +108,8 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
export class SamplerOperations {
public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
public readonly poolsCaches: { [key in SourcesWithPoolsCache]: PoolsCache };
public readonly aaveReservesCache: AaveV2ReservesCache | undefined;
public readonly compoundCTokenCache: CompoundCTokenCache | undefined;
protected _bancorService?: BancorService;
public static constant<T>(result: T): BatchedOperation<T> {
return {
@@ -131,6 +142,19 @@ export class SamplerOperations {
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
};
const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
if (aaveSubgraphUrl) {
this.aaveReservesCache = new AaveV2ReservesCache(aaveSubgraphUrl);
}
const compoundApiUrl = COMPOUND_API_URL_BY_CHAIN_ID[chainId];
if (compoundApiUrl) {
this.compoundCTokenCache = new CompoundCTokenCache(
compoundApiUrl,
NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId],
);
}
// Initialize the Bancor service, fetching paths in the background
bancorServiceFn()
.then(service => (this._bancorService = service))
@@ -158,6 +182,30 @@ export class SamplerOperations {
};
}
public getGasLeft(): BatchedOperation<BigNumber> {
return {
encodeCall: () => this._samplerContract.getGasLeft().getABIEncodedTransactionData(),
handleCallResults: (callResults: string) =>
this._samplerContract.getABIDecodedReturnData<BigNumber>('getGasLeft', callResults),
handleRevert: () => {
/* should never happen */
throw new Error('Invalid result for getGasLeft');
},
};
}
public getBlockNumber(): BatchedOperation<BigNumber> {
return {
encodeCall: () => this._samplerContract.getBlockNumber().getABIEncodedTransactionData(),
handleCallResults: (callResults: string) =>
this._samplerContract.getABIDecodedReturnData<BigNumber>('getBlockNumber', callResults),
handleRevert: () => {
/* should never happen */
throw new Error('Invalid result for getBlockNumber');
},
};
}
public getLimitOrderFillableTakerAmounts(
orders: SignedNativeOrder[],
exchangeAddress: string,
@@ -1075,6 +1123,64 @@ export class SamplerOperations {
});
}
// tslint:disable-next-line:prefer-function-over-method
public getAaveV2SellQuotes(
aaveInfo: AaveV2Info,
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<AaveV2FillData> {
return new SamplerNoOperation({
source: ERC20BridgeSource.AaveV2,
fillData: { ...aaveInfo, takerToken },
callback: () => AaveV2Sampler.sampleSellsFromAaveV2(aaveInfo, takerToken, makerToken, takerFillAmounts),
});
}
// tslint:disable-next-line:prefer-function-over-method
public getAaveV2BuyQuotes(
aaveInfo: AaveV2Info,
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<AaveV2FillData> {
return new SamplerNoOperation({
source: ERC20BridgeSource.AaveV2,
fillData: { ...aaveInfo, takerToken },
callback: () => AaveV2Sampler.sampleBuysFromAaveV2(aaveInfo, takerToken, makerToken, makerFillAmounts),
});
}
public getCompoundSellQuotes(
cToken: string,
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<CompoundFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Compound,
fillData: { cToken, takerToken, makerToken },
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromCompound,
params: [cToken, takerToken, makerToken, takerFillAmounts],
});
}
public getCompoundBuyQuotes(
cToken: string,
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<CompoundFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Compound,
fillData: { cToken, takerToken, makerToken },
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromCompound,
params: [cToken, takerToken, makerToken, makerFillAmounts],
});
}
public getMedianSellRate(
sources: ERC20BridgeSource[],
makerToken: string,
@@ -1243,6 +1349,7 @@ export class SamplerOperations {
case ERC20BridgeSource.FirebirdOneSwap:
case ERC20BridgeSource.IronSwap:
case ERC20BridgeSource.ACryptos:
case ERC20BridgeSource.MobiusMoney:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveSellQuotes(
pool,
@@ -1425,6 +1532,38 @@ export class SamplerOperations {
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts);
}
case ERC20BridgeSource.AaveV2: {
if (!this.aaveReservesCache) {
return [];
}
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
if (!reserve) {
return [];
}
const info: AaveV2Info = {
lendingPool: reserve.pool.lendingPool,
aToken: reserve.aToken.id,
underlyingToken: reserve.underlyingAsset,
};
return this.getAaveV2SellQuotes(info, makerToken, takerToken, takerFillAmounts);
}
case ERC20BridgeSource.Compound: {
if (!this.compoundCTokenCache) {
return [];
}
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
if (!cToken) {
return [];
}
return this.getCompoundSellQuotes(
cToken.tokenAddress,
makerToken,
takerToken,
takerFillAmounts,
);
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
@@ -1516,6 +1655,7 @@ export class SamplerOperations {
case ERC20BridgeSource.FirebirdOneSwap:
case ERC20BridgeSource.IronSwap:
case ERC20BridgeSource.ACryptos:
case ERC20BridgeSource.MobiusMoney:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveBuyQuotes(
pool,
@@ -1694,6 +1834,32 @@ export class SamplerOperations {
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.AaveV2: {
if (!this.aaveReservesCache) {
return [];
}
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
if (!reserve) {
return [];
}
const info: AaveV2Info = {
lendingPool: reserve.pool.lendingPool,
aToken: reserve.aToken.id,
underlyingToken: reserve.underlyingAsset,
};
return this.getAaveV2BuyQuotes(info, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.Compound: {
if (!this.compoundCTokenCache) {
return [];
}
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
if (!cToken) {
return [];
}
return this.getCompoundBuyQuotes(cToken.tokenAddress, makerToken, takerToken, makerFillAmounts);
}
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -68,6 +68,8 @@ export enum ERC20BridgeSource {
CurveV2 = 'Curve_V2',
Lido = 'Lido',
ShibaSwap = 'ShibaSwap',
AaveV2 = 'Aave_V2',
Compound = 'Compound',
// BSC only
PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2',
@@ -94,6 +96,7 @@ export enum ERC20BridgeSource {
TraderJoe = 'TraderJoe',
// Celo only
UbeSwap = 'UbeSwap',
MobiusMoney = 'Mobius',
// Fantom
SpiritSwap = 'SpiritSwap',
SpookySwap = 'SpookySwap',
@@ -116,8 +119,10 @@ export enum CurveFunctionSelectors {
exchange_underlying = '0xa6417ed6',
get_dy_underlying = '0x07211ef7',
get_dx_underlying = '0x0e71d1b9',
get_dy = '0x5e0d443f',
get_dy = '0x5e0d443f', // get_dy(int128,int128,uint256)
get_dx = '0x67df02ca',
get_dy_uint256 = '0x556d6e9f', // get_dy(uint256,uint256,uint256)
exchange_underlying_uint256 = '0x65b2489b', // exchange_underlying(uint256,uint256,uint256,uint256)
// Curve V2
exchange_v2 = '0x5b41b908',
exchange_underlying_v2 = '0x65b2489b',
@@ -126,7 +131,7 @@ export enum CurveFunctionSelectors {
// Smoothy
swap_uint256 = '0x5673b02d', // swap(uint256,uint256,uint256,uint256)
get_swap_amount = '0x45cf2ef6', // getSwapAmount(uint256,uint256,uint256)
// Nerve BSC, Saddle Mainnet
// Nerve BSC, Saddle Mainnet, Synapse all networks, Mobius Celo
swap = '0x91695586', // swap(uint8,uint8,uint256,uint256,uint256)
calculateSwap = '0xa95b089f', // calculateSwap(uint8,uint8,uint256)
}
@@ -170,6 +175,12 @@ export interface BalancerV2PoolInfo {
vault: string;
}
export interface AaveV2Info {
lendingPool: string;
aToken: string;
underlyingToken: string;
}
// Internal `fillData` field for `Fill` objects.
export interface FillData {}
@@ -277,6 +288,19 @@ export interface LidoFillData extends FillData {
takerToken: string;
}
export interface AaveV2FillData extends FillData {
lendingPool: string;
aToken: string;
underlyingToken: string;
takerToken: string;
}
export interface CompoundFillData extends FillData {
cToken: string;
takerToken: string;
makerToken: string;
}
/**
* Represents a node on a fill path.
*/
@@ -432,6 +456,10 @@ export interface GetMarketOrdersOpts {
* Default: 1.25.
*/
sampleDistributionBase: number;
/**
* Number of samples to use when creating fill curves with neon-router
*/
neonRouterNumSamples: number;
/**
* Fees for each liquidity source, expressed in gas.
*/
@@ -469,6 +497,37 @@ export interface GetMarketOrdersOpts {
* Gas price to use for quote
*/
gasPrice: BigNumber;
/**
* Sampler metrics for recording data on the sampler service and operations
*/
samplerMetrics?: SamplerMetrics;
}
export interface SamplerMetrics {
/**
* Logs the gas information performed during a sampler call.
*
* @param data.gasBefore The gas remaining measured before any operations have been performed
* @param data.gasAfter The gas remaining measured after all operations have been performed
*/
logGasDetails(data: { gasBefore: BigNumber; gasAfter: BigNumber }): void;
/**
* Logs the block number
*
* @param blockNumber block number of the sampler call
*/
logBlockNumber(blockNumber: BigNumber): void;
/**
* Logs the routing timings
*
* @param data.router The router type (neon-router or js)
* @param data.type The type of timing being recorded (e.g total timing, all sources timing or vip timing)
* @param data.timingMs The timing in milliseconds
*/
logRouterDetails(data: { router: 'neon-router' | 'js'; type: 'all' | 'vip' | 'total'; timingMs: number }): void;
}
/**
@@ -554,6 +613,8 @@ export interface GenerateOptimizedOrdersOpts {
allowFallback?: boolean;
shouldBatchBridgeOrders?: boolean;
gasPrice: BigNumber;
neonRouterNumSamples: number;
samplerMetrics?: SamplerMetrics;
}
export interface ComparisonPrice {

View File

@@ -10,6 +10,7 @@ import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
import * as BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json';
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
import * as CompoundSampler from '../test/generated-artifacts/CompoundSampler.json';
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
@@ -52,6 +53,7 @@ export const artifacts = {
BalancerSampler: BalancerSampler as ContractArtifact,
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
BancorSampler: BancorSampler as ContractArtifact,
CompoundSampler: CompoundSampler as ContractArtifact,
CurveSampler: CurveSampler as ContractArtifact,
DODOSampler: DODOSampler as ContractArtifact,
DODOV2Sampler: DODOV2Sampler as ContractArtifact,

View File

@@ -23,6 +23,7 @@ import {
POSITIVE_INF,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SOURCE_FLAGS,
ZERO_AMOUNT,
} from '../src/utils/market_operation_utils/constants';
import { createFills } from '../src/utils/market_operation_utils/fills';
import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache';
@@ -427,6 +428,8 @@ describe('MarketOperationUtils tests', () => {
getTwoHopSellQuotes: (..._params: any[]) => [],
getTwoHopBuyQuotes: (..._params: any[]) => [],
isAddressContract: (..._params: any[]) => false,
getGasLeft: () => ZERO_AMOUNT,
getBlockNumber: () => ZERO_AMOUNT,
};
const MOCK_SAMPLER = ({

View File

@@ -8,6 +8,7 @@ export * from '../test/generated-wrappers/balance_checker';
export * from '../test/generated-wrappers/balancer_sampler';
export * from '../test/generated-wrappers/balancer_v2_sampler';
export * from '../test/generated-wrappers/bancor_sampler';
export * from '../test/generated-wrappers/compound_sampler';
export * from '../test/generated-wrappers/curve_sampler';
export * from '../test/generated-wrappers/d_o_d_o_sampler';
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';

View File

@@ -11,6 +11,7 @@
"test/generated-artifacts/BalancerSampler.json",
"test/generated-artifacts/BalancerV2Sampler.json",
"test/generated-artifacts/BancorSampler.json",
"test/generated-artifacts/CompoundSampler.json",
"test/generated-artifacts/CurveSampler.json",
"test/generated-artifacts/DODOSampler.json",
"test/generated-artifacts/DODOV2Sampler.json",

View File

@@ -1,4 +1,24 @@
[
{
"version": "6.11.0",
"changes": [
{
"note": "Add Optimism addresses",
"pr": 385
}
],
"timestamp": 1640364306
},
{
"version": "6.10.0",
"changes": [
{
"note": "Add Aave supported FQT addresses for Polygon, Avalanche",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"version": "6.9.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.11.0 - _December 24, 2021_
* Add Optimism addresses (#385)
## v6.10.0 - _December 1, 2021_
* Add Aave supported FQT addresses for Polygon, Avalanche (#321)
## v6.9.0 - _November 16, 2021_
* Add Celo addresses (#368)

View File

@@ -289,7 +289,7 @@
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
"fillQuoteTransformer": "0xf708d512b8a82e2862543a630403327174410baf",
"fillQuoteTransformer": "0xd3afdf4a8ea9183e76c9c2306cda03ea4afffea5",
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
}
},
@@ -373,7 +373,7 @@
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
"payTakerTransformer": "0x898c6fde239d646c73f0a57e3570b6f86a3d62a3",
"affiliateFeeTransformer": "0x34617b855411e52fbc05899435f44cbd0503022c",
"fillQuoteTransformer": "0x8a5417dd7ffde61ec61e11b45797e16686e1d6b9",
"fillQuoteTransformer": "0xd421f50b3ae27f223aa35a04944236d257235412",
"positiveSlippageFeeTransformer": "0x470ba89da18a6db6e8a0567b3c9214b960861857"
}
},
@@ -460,5 +460,47 @@
"fillQuoteTransformer": "0xa825d4d3c4d2820c52da69fcccf269b4081871f2",
"positiveSlippageFeeTransformer": "0x9ffc7a79133ed5242777e40764777a6d5aab282c"
}
},
"10": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0x4200000000000000000000000000000000000006",
"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": "0x6d506b2847df0c6f04d2628da1adaf4d8fb2e81b",
"exchangeProxy": "0xdef1abe32c034e558cdd535791643c58a13acc10",
"exchangeProxyTransformerDeployer": "0x3a539ed6bd42de8fbaf3899fb490c792e153d647",
"exchangeProxyFlashWallet": "0xa3128d9b7cca7d5af29780a56abeec12b05a6740",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x02ce7af6520e2862f961f5d7eda746642865179c",
"payTakerTransformer": "0x085d10a34f14f6a631ea8ff7d016782ee3ffaa11",
"affiliateFeeTransformer": "0x55cf1d7535250db75bf0190493f55781ee583553",
"fillQuoteTransformer": "0x3543ef833d28b7e983c293856561f21a7f089f1d",
"positiveSlippageFeeTransformer": "0xb11e14565dfbeb702dea9bc0cb47f1a8b32f4783"
}
}
}

View File

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

View File

@@ -55,6 +55,7 @@ export enum ChainId {
Avalanche = 43114,
Fantom = 250,
Celo = 42220,
Optimism = 10,
}
/**

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "13.18.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "13.18.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "13.18.3",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v13.18.5 - _December 24, 2021_
* Dependencies updated
## v13.18.4 - _December 1, 2021_
* Dependencies updated
## v13.18.3 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers",
"version": "13.18.3",
"version": "13.18.5",
"engines": {
"node": ">=6.12"
},
@@ -57,7 +57,7 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/json-schemas": "^6.3.0",
"@0x/types": "^3.3.4",
"@0x/utils": "^6.4.4",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1640364306,
"version": "8.1.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "8.1.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "8.1.12",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v8.1.14 - _December 24, 2021_
* Dependencies updated
## v8.1.13 - _December 1, 2021_
* Dependencies updated
## v8.1.12 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
"version": "8.1.12",
"version": "8.1.14",
"engines": {
"node": ">=6.12"
},
@@ -68,20 +68,20 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-coordinator": "^3.1.38",
"@0x/contracts-dev-utils": "^1.3.36",
"@0x/contracts-erc1155": "^2.1.37",
"@0x/contracts-erc20": "^3.3.23",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-erc721": "^3.1.37",
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-forwarder": "^4.2.38",
"@0x/contracts-extensions": "^6.2.32",
"@0x/contracts-multisig": "^4.1.38",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-utils": "^4.8.4",
"@0x/contracts-zero-ex": "^0.29.5",
"@0x/contracts-utils": "^4.8.6",
"@0x/contracts-zero-ex": "^0.30.1",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/typescript-typings": "^5.2.1",

View File

@@ -1,4 +1,23 @@
[
{
"timestamp": 1640364306,
"version": "1.10.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.10.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"timestamp": 1637102971,
"version": "1.9.5",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.10.1 - _December 24, 2021_
* Dependencies updated
## v1.10.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v1.9.5 - _November 16, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "1.9.5",
"version": "1.10.1",
"engines": {
"node": ">=6.12"
},
@@ -63,8 +63,8 @@
},
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/contract-addresses": "^6.9.0",
"@0x/contract-wrappers": "^13.18.3",
"@0x/contract-addresses": "^6.11.0",
"@0x/contract-wrappers": "^13.18.5",
"@0x/json-schemas": "^6.3.0",
"@0x/subproviders": "^6.6.0",
"@0x/utils": "^6.4.4",

View File

@@ -132,6 +132,8 @@ export enum BridgeProtocol {
CurveV2,
Lido,
Clipper, // Not used: Clipper is now using PLP interface
AaveV2,
Compound,
}
// tslint:enable: enum-naming

View File

@@ -959,10 +959,10 @@
typedoc "~0.16.11"
yargs "^10.0.3"
"@0x/neon-router@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.2.1.tgz#23bb3cedc0eafd55a8ba6b6ea8a59ee4c538064b"
integrity sha512-feCCKuox4staZl8lxLY4nf5U256NcDHrgvSFra5cU/TUhoblLHb8F7eWAC9ygpukZUCVFLy13mExkFQHXlEOYw==
"@0x/neon-router@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.3.1.tgz#4ec13e750d1435357c4928d7f2521a2b4376f27e"
integrity sha512-M4ypTov9KyxsGJpYwobrld3Y2JOlR7U0XjR6BEQE2gQ1k3nie/1wNEI2J4ZjKw++RLDxdv/RCqhgA5VnINzjxA==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.5"