Curve ERC20Bridge (#2480)
* Curve ERC20Bridge * ERC20BridgeSampler Curve (#2483) * ERC20Sampler Curve * Use Bridge Sources for each Curve * Support multiple versions of the Curve contract * CHANGELOG and redeployed Curve (mainnet) * Fix Market ops utils test * Added Curve DAI USDC USDT TUSD * Bump sampler gas limit default * Decode the Curve in tests * Disable Curve in Buy tests * blockchainTests.fork.resets Curve and Sampler
This commit is contained in:
parent
dcce8276b8
commit
e05a03a842
108
contracts/asset-proxy/contracts/src/bridges/CurveBridge.sol
Normal file
108
contracts/asset-proxy/contracts/src/bridges/CurveBridge.sol
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/DeploymentConstants.sol";
|
||||
import "../interfaces/IERC20Bridge.sol";
|
||||
import "../interfaces/ICurve.sol";
|
||||
|
||||
|
||||
// solhint-disable not-rely-on-time
|
||||
// solhint-disable space-after-comma
|
||||
contract CurveBridge is
|
||||
IERC20Bridge,
|
||||
IWallet,
|
||||
DeploymentConstants
|
||||
{
|
||||
/// @dev Callback for `ICurve`. Tries to buy `amount` of
|
||||
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||
/// (DAI, USDC) to the Curve contract, then transfers the bought
|
||||
/// tokens to `to`.
|
||||
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
|
||||
/// @param to The recipient of the bought tokens.
|
||||
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||
/// @param bridgeData The abi-encoeded "from" token address.
|
||||
/// @return success The magic bytes if successful.
|
||||
function bridgeTransferFrom(
|
||||
address toTokenAddress,
|
||||
address /* from */,
|
||||
address to,
|
||||
uint256 amount,
|
||||
bytes calldata bridgeData
|
||||
)
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
|
||||
ICurve exchange = ICurve(curveAddress);
|
||||
|
||||
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
|
||||
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||
|
||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||
if (version == 0) {
|
||||
exchange.exchange_underlying(
|
||||
fromCoinIdx,
|
||||
toCoinIdx,
|
||||
// dx
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
// min dy
|
||||
amount,
|
||||
// expires
|
||||
block.timestamp + 1
|
||||
);
|
||||
} else {
|
||||
exchange.exchange_underlying(
|
||||
fromCoinIdx,
|
||||
toCoinIdx,
|
||||
// dx
|
||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||
// min dy
|
||||
amount
|
||||
);
|
||||
}
|
||||
|
||||
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
|
||||
// Transfer the converted `toToken`s to `to`.
|
||||
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
|
||||
return BRIDGE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||
/// and sign for itself in orders. Always succeeds.
|
||||
/// @return magicValue Magic success bytes, always.
|
||||
function isValidSignature(
|
||||
bytes32,
|
||||
bytes calldata
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bytes4 magicValue)
|
||||
{
|
||||
return LEGACY_WALLET_MAGIC_VALUE;
|
||||
}
|
||||
}
|
87
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
87
contracts/asset-proxy/contracts/src/interfaces/ICurve.sol
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
interface ICurve {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on early versions of Curve (USDC/DAI)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
/// @param deadline The time in seconds when this operation should expire.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
uint256 deadline
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dy);
|
||||
|
||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param buyAmount The amount of token being bought.
|
||||
function get_dx_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dx);
|
||||
|
||||
/// @dev Get the underlying token address from the token index
|
||||
/// @param i The token index.
|
||||
function underlying_coins(
|
||||
int128 i
|
||||
)
|
||||
external
|
||||
returns (address tokenAddress);
|
||||
}
|
@ -38,7 +38,7 @@
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./test/generated-artifacts/@(ChaiBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis": "./test/generated-artifacts/@(ChaiBridge|CurveBridge|DydxBridge|ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IChai|ICurve|IDydx|IDydxBridge|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestChaiBridge|TestDydxBridge|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ChaiBridge from '../generated-artifacts/ChaiBridge.json';
|
||||
import * as CurveBridge from '../generated-artifacts/CurveBridge.json';
|
||||
import * as DydxBridge from '../generated-artifacts/DydxBridge.json';
|
||||
import * as ERC1155Proxy from '../generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20BridgeProxy from '../generated-artifacts/ERC20BridgeProxy.json';
|
||||
@ -17,6 +18,7 @@ import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||
import * as IAssetProxyDispatcher from '../generated-artifacts/IAssetProxyDispatcher.json';
|
||||
import * as IAuthorizable from '../generated-artifacts/IAuthorizable.json';
|
||||
import * as IChai from '../generated-artifacts/IChai.json';
|
||||
import * as ICurve from '../generated-artifacts/ICurve.json';
|
||||
import * as IDydx from '../generated-artifacts/IDydx.json';
|
||||
import * as IDydxBridge from '../generated-artifacts/IDydxBridge.json';
|
||||
import * as IERC20Bridge from '../generated-artifacts/IERC20Bridge.json';
|
||||
@ -49,6 +51,7 @@ export const artifacts = {
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||
CurveBridge: CurveBridge as ContractArtifact,
|
||||
DydxBridge: DydxBridge as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
KyberBridge: KyberBridge as ContractArtifact,
|
||||
@ -58,6 +61,7 @@ export const artifacts = {
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IChai: IChai as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDydx: IDydx as ContractArtifact,
|
||||
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
|
@ -4,6 +4,7 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../generated-wrappers/chai_bridge';
|
||||
export * from '../generated-wrappers/curve_bridge';
|
||||
export * from '../generated-wrappers/dydx_bridge';
|
||||
export * from '../generated-wrappers/erc1155_proxy';
|
||||
export * from '../generated-wrappers/erc20_bridge_proxy';
|
||||
@ -15,6 +16,7 @@ export * from '../generated-wrappers/i_asset_proxy';
|
||||
export * from '../generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/i_authorizable';
|
||||
export * from '../generated-wrappers/i_chai';
|
||||
export * from '../generated-wrappers/i_curve';
|
||||
export * from '../generated-wrappers/i_dydx';
|
||||
export * from '../generated-wrappers/i_dydx_bridge';
|
||||
export * from '../generated-wrappers/i_erc20_bridge';
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ChaiBridge from '../test/generated-artifacts/ChaiBridge.json';
|
||||
import * as CurveBridge from '../test/generated-artifacts/CurveBridge.json';
|
||||
import * as DydxBridge from '../test/generated-artifacts/DydxBridge.json';
|
||||
import * as ERC1155Proxy from '../test/generated-artifacts/ERC1155Proxy.json';
|
||||
import * as ERC20BridgeProxy from '../test/generated-artifacts/ERC20BridgeProxy.json';
|
||||
@ -17,6 +18,7 @@ import * as IAssetProxy from '../test/generated-artifacts/IAssetProxy.json';
|
||||
import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyDispatcher.json';
|
||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
||||
import * as IChai from '../test/generated-artifacts/IChai.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
import * as IDydx from '../test/generated-artifacts/IDydx.json';
|
||||
import * as IDydxBridge from '../test/generated-artifacts/IDydxBridge.json';
|
||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||
@ -49,6 +51,7 @@ export const artifacts = {
|
||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||
ChaiBridge: ChaiBridge as ContractArtifact,
|
||||
CurveBridge: CurveBridge as ContractArtifact,
|
||||
DydxBridge: DydxBridge as ContractArtifact,
|
||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||
KyberBridge: KyberBridge as ContractArtifact,
|
||||
@ -58,6 +61,7 @@ export const artifacts = {
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||
IChai: IChai as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDydx: IDydx as ContractArtifact,
|
||||
IDydxBridge: IDydxBridge as ContractArtifact,
|
||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||
|
@ -4,6 +4,7 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/chai_bridge';
|
||||
export * from '../test/generated-wrappers/curve_bridge';
|
||||
export * from '../test/generated-wrappers/dydx_bridge';
|
||||
export * from '../test/generated-wrappers/erc1155_proxy';
|
||||
export * from '../test/generated-wrappers/erc20_bridge_proxy';
|
||||
@ -15,6 +16,7 @@ export * from '../test/generated-wrappers/i_asset_proxy';
|
||||
export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
|
||||
export * from '../test/generated-wrappers/i_authorizable';
|
||||
export * from '../test/generated-wrappers/i_chai';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
export * from '../test/generated-wrappers/i_dydx';
|
||||
export * from '../test/generated-wrappers/i_dydx_bridge';
|
||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||
|
@ -4,6 +4,7 @@
|
||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
|
||||
"files": [
|
||||
"generated-artifacts/ChaiBridge.json",
|
||||
"generated-artifacts/CurveBridge.json",
|
||||
"generated-artifacts/DydxBridge.json",
|
||||
"generated-artifacts/ERC1155Proxy.json",
|
||||
"generated-artifacts/ERC20BridgeProxy.json",
|
||||
@ -15,6 +16,7 @@
|
||||
"generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"generated-artifacts/IAuthorizable.json",
|
||||
"generated-artifacts/IChai.json",
|
||||
"generated-artifacts/ICurve.json",
|
||||
"generated-artifacts/IDydx.json",
|
||||
"generated-artifacts/IDydxBridge.json",
|
||||
"generated-artifacts/IERC20Bridge.json",
|
||||
@ -37,6 +39,7 @@
|
||||
"generated-artifacts/TestUniswapBridge.json",
|
||||
"generated-artifacts/UniswapBridge.json",
|
||||
"test/generated-artifacts/ChaiBridge.json",
|
||||
"test/generated-artifacts/CurveBridge.json",
|
||||
"test/generated-artifacts/DydxBridge.json",
|
||||
"test/generated-artifacts/ERC1155Proxy.json",
|
||||
"test/generated-artifacts/ERC20BridgeProxy.json",
|
||||
@ -48,6 +51,7 @@
|
||||
"test/generated-artifacts/IAssetProxyDispatcher.json",
|
||||
"test/generated-artifacts/IAuthorizable.json",
|
||||
"test/generated-artifacts/IChai.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IDydx.json",
|
||||
"test/generated-artifacts/IDydxBridge.json",
|
||||
"test/generated-artifacts/IERC20Bridge.json",
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "1.4.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added Curve contract sampling",
|
||||
"pr": 2483
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.3.0",
|
||||
"changes": [
|
||||
|
@ -29,6 +29,7 @@ import "./IERC20BridgeSampler.sol";
|
||||
import "./IEth2Dai.sol";
|
||||
import "./IKyberNetwork.sol";
|
||||
import "./IUniswapExchangeQuotes.sol";
|
||||
import "./ICurve.sol";
|
||||
|
||||
|
||||
contract ERC20BridgeSampler is
|
||||
@ -43,6 +44,9 @@ contract ERC20BridgeSampler is
|
||||
uint256 constant internal UNISWAP_CALL_GAS = 150e3; // 150k
|
||||
/// @dev Base gas limit for Eth2Dai calls.
|
||||
uint256 constant internal ETH2DAI_CALL_GAS = 1000e3; // 1m
|
||||
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
|
||||
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
|
||||
uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
|
||||
|
||||
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||
/// @param callDatas ABI-encoded call data for each function call.
|
||||
@ -389,6 +393,44 @@ contract ERC20BridgeSampler is
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sample sell quotes from Curve.
|
||||
/// @param curveAddress Address of the Curve contract.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromCurve(
|
||||
address curveAddress,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
curveAddress.staticcall.gas(CURVE_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
ICurve(0).get_dy_underlying.selector,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Overridable way to get token decimals.
|
||||
/// @param tokenAddress Address of the token.
|
||||
/// @return decimals The decimal places for the token.
|
||||
|
87
contracts/erc20-bridge-sampler/contracts/src/ICurve.sol
Normal file
87
contracts/erc20-bridge-sampler/contracts/src/ICurve.sol
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
|
||||
Copyright 2019 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
|
||||
// solhint-disable func-name-mixedcase
|
||||
interface ICurve {
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on early versions of Curve (USDC/DAI)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
/// @param deadline The time in seconds when this operation should expire.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
uint256 deadline
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Sell `sellAmount` of `fromToken` token and receive `toToken` token.
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
/// @param minBuyAmount The minimum buy amount of the token being bought.
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Get the amount of `toToken` by selling `sellAmount` of `fromToken`
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param sellAmount The amount of token being bought.
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dy);
|
||||
|
||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
||||
/// This function exists on later versions of Curve (USDC/DAI/USDT)
|
||||
/// @param i The token index being sold.
|
||||
/// @param j The token index being bought.
|
||||
/// @param buyAmount The amount of token being bought.
|
||||
function get_dx_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 dx);
|
||||
|
||||
/// @dev Get the underlying token address from the token index
|
||||
/// @param i The token index.
|
||||
function underlying_coins(
|
||||
int128 i
|
||||
)
|
||||
external
|
||||
returns (address tokenAddress);
|
||||
}
|
@ -132,4 +132,21 @@ interface IERC20BridgeSampler {
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory takerTokenAmounts);
|
||||
|
||||
/// @dev Sample sell quotes from Curve.
|
||||
/// @param curveAddress Address of the Curve contract.
|
||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromCurve(
|
||||
address curveAddress,
|
||||
int128 fromTokenIdx,
|
||||
int128 toTokenIdx,
|
||||
uint256[] calldata takerTokenAmounts
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts);
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { ContractArtifact } from 'ethereum-types';
|
||||
|
||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
|
||||
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
|
||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||
@ -14,6 +15,7 @@ import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExc
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
export const artifacts = {
|
||||
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IDevUtils: IDevUtils as ContractArtifact,
|
||||
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
|
||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||
|
@ -4,6 +4,7 @@
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
export * from '../test/generated-wrappers/i_dev_utils';
|
||||
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||
|
@ -6,6 +6,7 @@
|
||||
"generated-artifacts/ERC20BridgeSampler.json",
|
||||
"generated-artifacts/IERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IDevUtils.json",
|
||||
"test/generated-artifacts/IERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/IEth2Dai.json",
|
||||
|
@ -11,8 +11,12 @@
|
||||
"pr": 2479
|
||||
},
|
||||
{
|
||||
"note": "Addeded decoders for stop-limit data",
|
||||
"note": "Addded decoders for stop-limit data",
|
||||
"pr": 2484
|
||||
},
|
||||
{
|
||||
"note": "Added ERC20Sampler and Curve Mainnet test",
|
||||
"pr": 2483
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -57,6 +57,7 @@
|
||||
"@0x/contracts-broker": "^1.0.2",
|
||||
"@0x/contracts-coordinator": "^3.1.0",
|
||||
"@0x/contracts-dev-utils": "^1.1.0",
|
||||
"@0x/contracts-erc20-bridge-sampler": "^1.3.0",
|
||||
"@0x/contracts-exchange-forwarder": "^4.2.0",
|
||||
"@0x/contracts-exchange-libs": "^4.3.0",
|
||||
"@0x/contracts-extensions": "^6.1.0",
|
||||
|
@ -0,0 +1,37 @@
|
||||
import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridge-sampler';
|
||||
import { blockchainTests, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
||||
let testContract: ERC20BridgeSamplerContract;
|
||||
before(async () => {
|
||||
testContract = await ERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||
artifacts.ERC20BridgeSampler,
|
||||
env.provider,
|
||||
{ ...env.txDefaults, from: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b' },
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
describe('sampleSellsFromCurve()', () => {
|
||||
const curveAddress = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
|
||||
const daiTokenIdx = new BigNumber(0);
|
||||
const usdcTokenIdx = new BigNumber(1);
|
||||
|
||||
it('samples sells from Curve DAI->USDC', async () => {
|
||||
const samples = await testContract
|
||||
.sampleSellsFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1)])
|
||||
.callAsync();
|
||||
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||
});
|
||||
|
||||
it('samples sells from Curve USDC->DAI', async () => {
|
||||
const samples = await testContract
|
||||
.sampleSellsFromCurve(curveAddress, usdcTokenIdx, daiTokenIdx, [toBaseUnitAmount(1, 6)])
|
||||
.callAsync();
|
||||
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
111
contracts/integrations/test/bridges/curve_bridge_mainnet_test.ts
Normal file
111
contracts/integrations/test/bridges/curve_bridge_mainnet_test.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { artifacts as assetProxyArtifacts } from '@0x/contracts-asset-proxy';
|
||||
import { CurveBridgeContract } from '@0x/contracts-asset-proxy/lib/src/wrappers';
|
||||
import { ERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import { blockchainTests, constants, describe, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||
import { AbiEncoder } from '@0x/utils';
|
||||
|
||||
blockchainTests.fork.resets('Mainnet curve bridge tests', env => {
|
||||
let testContract: CurveBridgeContract;
|
||||
const receiver = '0x986ccf5234d9cfbb25246f1a5bfa51f4ccfcb308';
|
||||
const usdcWallet = '0xF977814e90dA44bFA03b6295A0616a897441aceC';
|
||||
const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
|
||||
const daiWallet = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
||||
const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
|
||||
const curveAddressUsdcDai = '0x2e60CF74d81ac34eB21eEff58Db4D385920ef419';
|
||||
const curveAddressUsdcDaiUsdt = '0x52EA46506B9CC5Ef470C5bf89f17Dc28bB35D85C';
|
||||
const daiTokenIdx = 0;
|
||||
const usdcTokenIdx = 1;
|
||||
const bridgeDataEncoder = AbiEncoder.create([
|
||||
{ name: 'curveAddress', type: 'address' },
|
||||
{ name: 'fromTokenIdx', type: 'int128' },
|
||||
{ name: 'toTokenIdx', type: 'int128' },
|
||||
{ name: 'version', type: 'int128' },
|
||||
]);
|
||||
before(async () => {
|
||||
testContract = await CurveBridgeContract.deployFrom0xArtifactAsync(
|
||||
assetProxyArtifacts.CurveBridge,
|
||||
env.provider,
|
||||
{ ...env.txDefaults, from: daiWallet },
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
describe('bridgeTransferFrom()', () => {
|
||||
describe('Version 0', () => {
|
||||
const version = 0;
|
||||
it('succeeds exchanges DAI for USDC', async () => {
|
||||
const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, daiTokenIdx, usdcTokenIdx, version]);
|
||||
// Fund the Bridge
|
||||
const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
|
||||
await dai
|
||||
.transfer(testContract.address, toBaseUnitAmount(1))
|
||||
.awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
|
||||
// Exchange via Curve
|
||||
await testContract
|
||||
.bridgeTransferFrom(
|
||||
usdcAddress,
|
||||
constants.NULL_ADDRESS,
|
||||
receiver,
|
||||
constants.ZERO_AMOUNT,
|
||||
bridgeData,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
|
||||
});
|
||||
it('succeeds exchanges USDC for DAI', async () => {
|
||||
const bridgeData = bridgeDataEncoder.encode([curveAddressUsdcDai, usdcTokenIdx, daiTokenIdx, version]);
|
||||
// Fund the Bridge
|
||||
const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
|
||||
await usdc
|
||||
.transfer(testContract.address, toBaseUnitAmount(1, 6))
|
||||
.awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
|
||||
// Exchange via Curve
|
||||
await testContract
|
||||
.bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
|
||||
.awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
|
||||
});
|
||||
});
|
||||
describe('Version 1', () => {
|
||||
const version = 1;
|
||||
it('succeeds exchanges DAI for USDC', async () => {
|
||||
const bridgeData = bridgeDataEncoder.encode([
|
||||
curveAddressUsdcDaiUsdt,
|
||||
daiTokenIdx,
|
||||
usdcTokenIdx,
|
||||
version,
|
||||
]);
|
||||
// Fund the Bridge
|
||||
const dai = new ERC20TokenContract(daiAddress, env.provider, { ...env.txDefaults, from: daiWallet });
|
||||
await dai
|
||||
.transfer(testContract.address, toBaseUnitAmount(1))
|
||||
.awaitTransactionSuccessAsync({ from: daiWallet }, { shouldValidate: false });
|
||||
// Exchange via Curve
|
||||
await testContract
|
||||
.bridgeTransferFrom(
|
||||
usdcAddress,
|
||||
constants.NULL_ADDRESS,
|
||||
receiver,
|
||||
constants.ZERO_AMOUNT,
|
||||
bridgeData,
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: daiWallet, gasPrice: 1 }, { shouldValidate: false });
|
||||
});
|
||||
it('succeeds exchanges USDC for DAI', async () => {
|
||||
const bridgeData = bridgeDataEncoder.encode([
|
||||
curveAddressUsdcDaiUsdt,
|
||||
usdcTokenIdx,
|
||||
daiTokenIdx,
|
||||
version,
|
||||
]);
|
||||
// Fund the Bridge
|
||||
const usdc = new ERC20TokenContract(usdcAddress, env.provider, { ...env.txDefaults, from: usdcWallet });
|
||||
await usdc
|
||||
.transfer(testContract.address, toBaseUnitAmount(1, 6))
|
||||
.awaitTransactionSuccessAsync({ from: usdcWallet }, { shouldValidate: false });
|
||||
// Exchange via Curve
|
||||
await testContract
|
||||
.bridgeTransferFrom(daiAddress, constants.NULL_ADDRESS, receiver, constants.ZERO_AMOUNT, bridgeData)
|
||||
.awaitTransactionSuccessAsync({ from: usdcWallet, gasPrice: 1 }, { shouldValidate: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -24,6 +24,7 @@ export let providerConfigs: Web3Config = {
|
||||
'0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
|
||||
'0x55dc8f21d20d4c6ed3c82916a438a413ca68e335',
|
||||
'0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0', // ERC20BridgeProxy
|
||||
'0xf977814e90da44bfa03b6295a0616a897441acec', // Binance: USDC, TUSD
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -5,6 +5,10 @@
|
||||
{
|
||||
"note": "Use `batchCall()` version of the `ERC20BridgeSampler` contract",
|
||||
"pr": 2477
|
||||
},
|
||||
{
|
||||
"note": "Support for sampling Curve contracts",
|
||||
"pr": 2483
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
} from './types';
|
||||
|
||||
import { constants as marketOperationUtilConstants } from './utils/market_operation_utils/constants';
|
||||
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
|
||||
|
||||
const ETH_GAS_STATION_API_BASE_URL = 'https://ethgasstation.info';
|
||||
const NULL_BYTES = '0x';
|
||||
@ -42,7 +43,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||
},
|
||||
...DEFAULT_ORDER_PRUNER_OPTS,
|
||||
samplerGasLimit: 36e6,
|
||||
samplerGasLimit: 59e6,
|
||||
};
|
||||
|
||||
const DEFAULT_FORWARDER_EXTENSION_CONTRACT_OPTS: ForwarderExtensionContractOpts = {
|
||||
@ -64,6 +65,34 @@ const DEFAULT_SWAP_QUOTE_REQUEST_OPTS: SwapQuoteRequestOpts = {
|
||||
...marketOperationUtilConstants.DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||
};
|
||||
|
||||
// Mainnet Curve configuration
|
||||
const DEFAULT_CURVE_OPTS: { [source: string]: { version: number; curveAddress: string; tokens: string[] } } = {
|
||||
[ERC20BridgeSource.CurveUsdcDai]: {
|
||||
version: 0,
|
||||
curveAddress: '0x2e60cf74d81ac34eb21eeff58db4d385920ef419',
|
||||
tokens: ['0x6b175474e89094c44da98b954eedeac495271d0f', '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'],
|
||||
},
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdt]: {
|
||||
version: 1,
|
||||
curveAddress: '0x52ea46506b9cc5ef470c5bf89f17dc28bb35d85c',
|
||||
tokens: [
|
||||
'0x6b175474e89094c44da98b954eedeac495271d0f',
|
||||
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
'0xdac17f958d2ee523a2206206994597c13d831ec7',
|
||||
],
|
||||
},
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: {
|
||||
version: 1,
|
||||
curveAddress: '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51',
|
||||
tokens: [
|
||||
'0x6b175474e89094c44da98b954eedeac495271d0f',
|
||||
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
||||
'0xdac17f958d2ee523a2206206994597c13d831ec7',
|
||||
'0x0000000000085d4780b73119b644ae5ecd22b376',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const constants = {
|
||||
ETH_GAS_STATION_API_BASE_URL,
|
||||
PROTOCOL_FEE_MULTIPLIER,
|
||||
@ -84,4 +113,5 @@ export const constants = {
|
||||
PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||
MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE,
|
||||
BRIDGE_ASSET_DATA_PREFIX: '0xdc1600f3',
|
||||
DEFAULT_CURVE_OPTS,
|
||||
};
|
||||
|
@ -7,7 +7,14 @@ const INFINITE_TIMESTAMP_SEC = new BigNumber(2524604400);
|
||||
/**
|
||||
* Valid sources for market sell.
|
||||
*/
|
||||
export const SELL_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai, ERC20BridgeSource.Kyber];
|
||||
export const SELL_SOURCES = [
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.CurveUsdcDai,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdt,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
|
||||
];
|
||||
|
||||
/**
|
||||
* Valid sources for market buy.
|
||||
|
@ -90,6 +90,10 @@ export class CreateOrderUtils {
|
||||
return this._contractAddress.kyberBridge;
|
||||
case ERC20BridgeSource.Uniswap:
|
||||
return this._contractAddress.uniswapBridge;
|
||||
case ERC20BridgeSource.CurveUsdcDai:
|
||||
case ERC20BridgeSource.CurveUsdcDaiUsdt:
|
||||
case ERC20BridgeSource.CurveUsdcDaiUsdtTusd:
|
||||
return this._contractAddress.curveBridge;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -106,13 +110,30 @@ function createBridgeOrder(
|
||||
slippage: number,
|
||||
isBuy: boolean = false,
|
||||
): OptimizedMarketOrder {
|
||||
return {
|
||||
makerAddress: bridgeAddress,
|
||||
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
|
||||
let makerAssetData;
|
||||
if (
|
||||
fill.source === ERC20BridgeSource.CurveUsdcDai ||
|
||||
fill.source === ERC20BridgeSource.CurveUsdcDaiUsdt ||
|
||||
fill.source === ERC20BridgeSource.CurveUsdcDaiUsdtTusd
|
||||
) {
|
||||
const { curveAddress, tokens, version } = constants.DEFAULT_CURVE_OPTS[fill.source];
|
||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
||||
const toTokenIdx = tokens.indexOf(makerToken);
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
bridgeAddress,
|
||||
createCurveBridgeData(curveAddress, fromTokenIdx, toTokenIdx, version),
|
||||
);
|
||||
} else {
|
||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||
makerToken,
|
||||
bridgeAddress,
|
||||
createBridgeData(takerToken),
|
||||
),
|
||||
);
|
||||
}
|
||||
return {
|
||||
makerAddress: bridgeAddress,
|
||||
makerAssetData,
|
||||
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken),
|
||||
...createCommonOrderFields(orderDomain, fill, slippage, isBuy),
|
||||
};
|
||||
@ -123,6 +144,21 @@ function createBridgeData(tokenAddress: string): string {
|
||||
return encoder.encode({ tokenAddress });
|
||||
}
|
||||
|
||||
function createCurveBridgeData(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
version: number,
|
||||
): string {
|
||||
const curveBridgeDataEncoder = AbiEncoder.create([
|
||||
{ name: 'curveAddress', type: 'address' },
|
||||
{ name: 'fromTokenIdx', type: 'int128' },
|
||||
{ name: 'toTokenIdx', type: 'int128' },
|
||||
{ name: 'version', type: 'int128' },
|
||||
]);
|
||||
return curveBridgeDataEncoder.encode([curveAddress, fromTokenIdx, toTokenIdx, version]);
|
||||
}
|
||||
|
||||
type CommonOrderFields = Pick<
|
||||
OptimizedMarketOrder,
|
||||
Exclude<keyof OptimizedMarketOrder, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
|
||||
|
@ -2,6 +2,8 @@ import { IERC20BridgeSamplerContract } from '@0x/contract-wrappers';
|
||||
import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { constants } from '../../constants';
|
||||
|
||||
import { DexSample, ERC20BridgeSource } from './types';
|
||||
|
||||
/**
|
||||
@ -89,6 +91,28 @@ const samplerOperations = {
|
||||
},
|
||||
};
|
||||
},
|
||||
getCurveSellQuotes(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: number,
|
||||
toTokenIdx: number,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<BigNumber[]> {
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
return contract
|
||||
.sampleSellsFromCurve(
|
||||
curveAddress,
|
||||
new BigNumber(fromTokenIdx),
|
||||
new BigNumber(toTokenIdx),
|
||||
takerFillAmounts,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleSellsFromCurve', callResults);
|
||||
},
|
||||
};
|
||||
},
|
||||
getUniswapBuyQuotes(
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
@ -127,30 +151,55 @@ const samplerOperations = {
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): BatchedOperation<DexSample[][]> {
|
||||
const subOps = sources.map(source => {
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
return samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
return samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Kyber) {
|
||||
return samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else {
|
||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||
}
|
||||
});
|
||||
const subOps = sources
|
||||
.map(source => {
|
||||
let batchedOperation;
|
||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||
batchedOperation = samplerOperations.getEth2DaiSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||
batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (source === ERC20BridgeSource.Kyber) {
|
||||
batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||
} else if (
|
||||
source === ERC20BridgeSource.CurveUsdcDai ||
|
||||
source === ERC20BridgeSource.CurveUsdcDaiUsdt ||
|
||||
source === ERC20BridgeSource.CurveUsdcDaiUsdtTusd
|
||||
) {
|
||||
const { curveAddress, tokens } = constants.DEFAULT_CURVE_OPTS[source];
|
||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
||||
const toTokenIdx = tokens.indexOf(makerToken);
|
||||
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
||||
batchedOperation = samplerOperations.getCurveSellQuotes(
|
||||
curveAddress,
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerFillAmounts,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Unsupported sell sample source: ${source}`);
|
||||
}
|
||||
return { batchedOperation, source };
|
||||
})
|
||||
.filter(op => op.batchedOperation) as Array<{
|
||||
batchedOperation: BatchedOperation<BigNumber[]>;
|
||||
source: ERC20BridgeSource;
|
||||
}>;
|
||||
return {
|
||||
encodeCall: contract => {
|
||||
const subCalls = subOps.map(op => op.encodeCall(contract));
|
||||
const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract));
|
||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||
},
|
||||
handleCallResultsAsync: async (contract, callResults) => {
|
||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||
const samples = await Promise.all(
|
||||
subOps.map(async (op, i) => op.handleCallResultsAsync(contract, rawSubCallResults[i])),
|
||||
subOps.map(async (op, i) =>
|
||||
op.batchedOperation.handleCallResultsAsync(contract, rawSubCallResults[i]),
|
||||
),
|
||||
);
|
||||
return sources.map((source, i) => {
|
||||
return subOps.map((op, i) => {
|
||||
return samples[i].map((output, j) => ({
|
||||
source,
|
||||
source: op.source,
|
||||
output,
|
||||
input: takerFillAmounts[j],
|
||||
}));
|
||||
|
@ -28,6 +28,9 @@ export enum ERC20BridgeSource {
|
||||
Uniswap = 'Uniswap',
|
||||
Eth2Dai = 'Eth2Dai',
|
||||
Kyber = 'Kyber',
|
||||
CurveUsdcDai = 'Curve_USDC_DAI',
|
||||
CurveUsdcDaiUsdt = 'Curve_USDC_DAI_USDT',
|
||||
CurveUsdcDaiUsdtTusd = 'Curve_USDC_DAI_USDT_TUSD',
|
||||
}
|
||||
|
||||
// Internal `fillData` field for `Fill` objects.
|
||||
|
@ -14,6 +14,7 @@ import { SignedOrder } from '@0x/types';
|
||||
import { BigNumber, hexUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants as assetSwapperConstants } from '../src/constants';
|
||||
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
|
||||
import { constants as marketOperationUtilConstants } from '../src/utils/market_operation_utils/constants';
|
||||
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
|
||||
@ -28,6 +29,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const ETH2DAI_BRIDGE_ADDRESS = contractAddresses.eth2DaiBridge;
|
||||
const KYBER_BRIDGE_ADDRESS = contractAddresses.kyberBridge;
|
||||
const UNISWAP_BRIDGE_ADDRESS = contractAddresses.uniswapBridge;
|
||||
const CURVE_BRIDGE_ADDRESS = contractAddresses.curveBridge;
|
||||
|
||||
const MAKER_TOKEN = randomAddress();
|
||||
const TAKER_TOKEN = randomAddress();
|
||||
@ -78,6 +80,11 @@ describe('MarketOperationUtils tests', () => {
|
||||
return ERC20BridgeSource.Eth2Dai;
|
||||
case UNISWAP_BRIDGE_ADDRESS.toLowerCase():
|
||||
return ERC20BridgeSource.Uniswap;
|
||||
case CURVE_BRIDGE_ADDRESS.toLowerCase():
|
||||
const curveSource = Object.keys(assetSwapperConstants.DEFAULT_CURVE_OPTS).filter(
|
||||
k => assetData.indexOf(assetSwapperConstants.DEFAULT_CURVE_OPTS[k].curveAddress.slice(2)) !== -1,
|
||||
);
|
||||
return curveSource[0] as ERC20BridgeSource;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -116,13 +123,15 @@ describe('MarketOperationUtils tests', () => {
|
||||
type GetQuotesOperation = (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => BigNumber[];
|
||||
|
||||
function createGetSellQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
|
||||
return (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return (...args) => {
|
||||
const fillAmounts = args.pop() as BigNumber[];
|
||||
return fillAmounts.map((a, i) => a.times(rates[i]).integerValue());
|
||||
};
|
||||
}
|
||||
|
||||
function createGetBuyQuotesOperationFromRates(rates: Numberish[]): GetQuotesOperation {
|
||||
return (makerToken: string, takerToken: string, fillAmounts: BigNumber[]) => {
|
||||
return (...args) => {
|
||||
const fillAmounts = args.pop() as BigNumber[];
|
||||
return fillAmounts.map((a, i) => a.div(rates[i]).integerValue());
|
||||
};
|
||||
}
|
||||
@ -179,6 +188,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.CurveUsdcDai]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdt]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.CurveUsdcDaiUsdtTusd]: createDecreasingRates(NUM_SAMPLES),
|
||||
};
|
||||
|
||||
function findSourceWithMaxOutput(rates: RatesBySource): ERC20BridgeSource {
|
||||
@ -209,6 +221,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
getEth2DaiSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
|
||||
getUniswapBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Uniswap]),
|
||||
getEth2DaiBuyQuotes: createGetBuyQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.Eth2Dai]),
|
||||
getCurveSellQuotes: createGetSellQuotesOperationFromRates(DEFAULT_RATES[ERC20BridgeSource.CurveUsdcDai]),
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES),
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES),
|
||||
};
|
||||
@ -386,6 +399,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Kyber] = [0.7, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
});
|
||||
@ -411,6 +427,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Kyber] = [0.4, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
});
|
||||
@ -436,6 +455,9 @@ describe('MarketOperationUtils tests', () => {
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.15, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.15, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Kyber] = [0.7, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.CurveUsdcDai] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdt] = [0, 0, 0, 0];
|
||||
rates[ERC20BridgeSource.CurveUsdcDaiUsdtTusd] = [0, 0, 0, 0];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
});
|
||||
@ -516,7 +538,15 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('returns the most cost-effective single source if `runLimit == 0`', async () => {
|
||||
const bestSource = findSourceWithMaxOutput(_.omit(DEFAULT_RATES, ERC20BridgeSource.Kyber));
|
||||
const bestSource = findSourceWithMaxOutput(
|
||||
_.omit(
|
||||
DEFAULT_RATES,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.CurveUsdcDai,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdt,
|
||||
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
|
||||
),
|
||||
);
|
||||
expect(bestSource).to.exist('');
|
||||
const improvedOrders = await marketOperationUtils.getMarketBuyOrdersAsync(ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
|
@ -3,8 +3,16 @@
|
||||
"version": "4.6.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added ChainlinkStopLimit addresses (mainnet, ropsten, rinkeby)",
|
||||
"note": "Added `ChainlinkStopLimit` addresses (mainnet, ropsten, rinkeby)",
|
||||
"pr": 2473
|
||||
},
|
||||
{
|
||||
"note": "Added `CurveBridge` address (mainnet)",
|
||||
"pr": 2483
|
||||
},
|
||||
{
|
||||
"note": "Update `ERC20BridgeSampler` address (mainnet, kovan)",
|
||||
"pr": 2483
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -20,14 +20,15 @@
|
||||
"devUtils": "0xb1a3d901bad1df7d710fc8d008db7cdd6bbbffe6",
|
||||
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
|
||||
"uniswapBridge": "0x533344cfdf2a3e911e2cf4c6f5ed08e791f5355f",
|
||||
"erc20BridgeSampler": "0x774c53ee7604af93cd3ed1cd25a788a9e0c06fb2",
|
||||
"erc20BridgeSampler": "0x43cfd1027bcc01d3df714d9e7bf989622e4bbec1",
|
||||
"kyberBridge": "0xf342f3a80fdc9b48713d58fe97e17f5cc764ee62",
|
||||
"eth2DaiBridge": "0xe3379a1956f4a79f39eb2e87bb441419e167538e",
|
||||
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
|
||||
"dydxBridge": "0x55dc8f21d20d4c6ed3c82916a438a413ca68e335",
|
||||
"godsUnchainedValidator": "0x09A379Ef7218BCFD8913fAa8B281ebc5A2E0bC04",
|
||||
"broker": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
|
||||
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53"
|
||||
"chainlinkStopLimit": "0xeb27220f95f364e1d9531992c48613f231839f53",
|
||||
"curveBridge": "0xe335bdd1fb0ee30f9a9a434f18f8b118dec32df7"
|
||||
},
|
||||
"3": {
|
||||
"erc20Proxy": "0xb1408f4c245a23c31b98d2c626777d4c0d766caa",
|
||||
@ -57,7 +58,8 @@
|
||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||
"godsUnchainedValidator": "0xd4690a51044db77D91d7Aa8f7a3a5ad5dA331Af0",
|
||||
"broker": "0x4Aa817C6f383C8e8aE77301d18Ce48efb16Fd2BE",
|
||||
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74"
|
||||
"chainlinkStopLimit": "0x67a094cf028221ffdd93fc658f963151d05e2a74",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"4": {
|
||||
"exchangeV2": "0xbff9493f92a3df4b0429b6d00743b3cfb4c85831",
|
||||
@ -87,7 +89,8 @@
|
||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
||||
"broker": "0x0000000000000000000000000000000000000000",
|
||||
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a"
|
||||
"chainlinkStopLimit": "0x407b4128e9ecad8769b2332312a9f655cb9f5f3a",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"42": {
|
||||
"erc20Proxy": "0xf1ec01d6236d3cd881a0bf0130ea25fe4234003e",
|
||||
@ -111,13 +114,14 @@
|
||||
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
||||
"uniswapBridge": "0x8224aa8fe5c9f07d5a59c735386ff6cc6aaeb568",
|
||||
"eth2DaiBridge": "0x9485d65c6a2fae0d519cced5bd830e57c41998a9",
|
||||
"erc20BridgeSampler": "0xca6485a7d0f1a42192072dff7518324513294adf",
|
||||
"erc20BridgeSampler": "0x0937795148f54f08538390d936e898163684b33f",
|
||||
"kyberBridge": "0xde7b2747624a647600fdb349184d0448ab954929",
|
||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
||||
"broker": "0x0000000000000000000000000000000000000000",
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000"
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000"
|
||||
},
|
||||
"1337": {
|
||||
"erc20Proxy": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48",
|
||||
@ -147,6 +151,7 @@
|
||||
"dydxBridge": "0x0000000000000000000000000000000000000000",
|
||||
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
|
||||
"broker": "0x0000000000000000000000000000000000000000",
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000"
|
||||
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
|
||||
"curveBridge": "0x0000000000000000000000000000000000000000"
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ export interface ContractAddresses {
|
||||
kyberBridge: string;
|
||||
chaiBridge: string;
|
||||
dydxBridge: string;
|
||||
curveBridge: string;
|
||||
}
|
||||
|
||||
export enum ChainId {
|
||||
|
@ -106,6 +106,20 @@
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{ "internalType": "address", "name": "curveAddress", "type": "address" },
|
||||
{ "internalType": "int128", "name": "fromTokenIdx", "type": "int128" },
|
||||
{ "internalType": "int128", "name": "toTokenIdx", "type": "int128" },
|
||||
{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }
|
||||
],
|
||||
"name": "sampleSellsFromCurve",
|
||||
"outputs": [{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
@ -187,6 +201,16 @@
|
||||
},
|
||||
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||
},
|
||||
"sampleSellsFromCurve(address,int128,int128,uint256[])": {
|
||||
"details": "Sample sell quotes from Curve.",
|
||||
"params": {
|
||||
"curveAddress": "Address of the Curve contract.",
|
||||
"fromTokenIdx": "Index of the taker token (what to sell).",
|
||||
"takerTokenAmounts": "Taker token sell amount for each sample.",
|
||||
"toTokenIdx": "Index of the maker token (what to buy)."
|
||||
},
|
||||
"return": "makerTokenAmounts Maker amounts bought at each taker token amount."
|
||||
},
|
||||
"sampleSellsFromEth2Dai(address,address,uint256[])": {
|
||||
"details": "Sample sell quotes from Eth2Dai/Oasis.",
|
||||
"params": {
|
||||
|
@ -396,6 +396,37 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'curveAddress',
|
||||
type: 'address',
|
||||
},
|
||||
{
|
||||
name: 'fromTokenIdx',
|
||||
type: 'int128',
|
||||
},
|
||||
{
|
||||
name: 'toTokenIdx',
|
||||
type: 'int128',
|
||||
},
|
||||
{
|
||||
name: 'takerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
name: 'sampleSellsFromCurve',
|
||||
outputs: [
|
||||
{
|
||||
name: 'makerTokenAmounts',
|
||||
type: 'uint256[]',
|
||||
},
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function',
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
@ -751,6 +782,48 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample sell quotes from Curve.
|
||||
* @param curveAddress Address of the Curve contract.
|
||||
* @param fromTokenIdx Index of the taker token (what to sell).
|
||||
* @param toTokenIdx Index of the maker token (what to buy).
|
||||
* @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
* @returns makerTokenAmounts Maker amounts bought at each taker token amount.
|
||||
*/
|
||||
public sampleSellsFromCurve(
|
||||
curveAddress: string,
|
||||
fromTokenIdx: BigNumber,
|
||||
toTokenIdx: BigNumber,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
): ContractFunctionObj<BigNumber[]> {
|
||||
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||
assert.isString('curveAddress', curveAddress);
|
||||
assert.isBigNumber('fromTokenIdx', fromTokenIdx);
|
||||
assert.isBigNumber('toTokenIdx', toTokenIdx);
|
||||
assert.isArray('takerTokenAmounts', takerTokenAmounts);
|
||||
const functionSignature = 'sampleSellsFromCurve(address,int128,int128,uint256[])';
|
||||
|
||||
return {
|
||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
||||
BaseContract._assertCallParams(callData, defaultBlock);
|
||||
const rawCallResult = await self._performCallAsync(
|
||||
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||
defaultBlock,
|
||||
);
|
||||
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||
BaseContract._throwIfUnexpectedEmptyCallResult(rawCallResult, abiEncoder);
|
||||
return abiEncoder.strictDecodeReturnValue<BigNumber[]>(rawCallResult);
|
||||
},
|
||||
getABIEncodedTransactionData(): string {
|
||||
return self._strictEncodeArguments(functionSignature, [
|
||||
curveAddress.toLowerCase(),
|
||||
fromTokenIdx,
|
||||
toTokenIdx,
|
||||
takerTokenAmounts,
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Sample sell quotes from Eth2Dai/Oasis.
|
||||
* @param takerToken Address of the taker token (what to sell).
|
||||
|
@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"version": "6.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added `CurveBridge` address (null)",
|
||||
"pr": 2483
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "6.1.0",
|
||||
"changes": [
|
||||
|
@ -302,6 +302,7 @@ export async function runMigrationsAsync(
|
||||
erc20BridgeSampler: constants.NULL_ADDRESS,
|
||||
chaiBridge: constants.NULL_ADDRESS,
|
||||
dydxBridge: constants.NULL_ADDRESS,
|
||||
curveBridge: constants.NULL_ADDRESS,
|
||||
};
|
||||
return contractAddresses;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user