ERC20BridgeSampler: Additional Buy support (#2551)
* ERC20BridgeSampler: Sample Curve Buy * Fake Buy Kyber/PLP * Deploy mainnet * Add Kyber rates for buy tests * CHANGELOGs * Provide maxIterations and targetSlippage as options * Cleanup ERC20BridgeSampler for re-use * Redeploy Mainnet Kovan * Feedback fixes * Handle OOG/revert 0s * Redeploy Mainnet refactor
This commit is contained in:
parent
110e1afa8e
commit
a458e81f8d
@ -65,7 +65,6 @@ interface ICurve {
|
|||||||
returns (uint256 dy);
|
returns (uint256 dy);
|
||||||
|
|
||||||
/// @dev Get the amount of `fromToken` by buying `buyAmount` of `toToken`
|
/// @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 i The token index being sold.
|
||||||
/// @param j The token index being bought.
|
/// @param j The token index being bought.
|
||||||
/// @param buyAmount The amount of token being bought.
|
/// @param buyAmount The amount of token being bought.
|
||||||
|
@ -5,6 +5,14 @@
|
|||||||
{
|
{
|
||||||
"note": "Pass in `DevUtils` address as a constructor parameter",
|
"note": "Pass in `DevUtils` address as a constructor parameter",
|
||||||
"pr": 2531
|
"pr": 2531
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Sample `Curve` for buy amounts",
|
||||||
|
"pr": 2551
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `sampleBuysFromKyberNetwork` ",
|
||||||
|
"pr": 2551
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -213,6 +213,33 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Kyber.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromKyberNetwork(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts,
|
||||||
|
FakeBuyOptions memory opts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
return _sampleApproximateBuysFromSource(
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
makerTokenAmounts,
|
||||||
|
opts,
|
||||||
|
this.sampleSellsFromKyberNetwork.selector,
|
||||||
|
address(0) // PLP registry address
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
@ -443,6 +470,44 @@ contract ERC20BridgeSampler is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy 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 makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromCurve(
|
||||||
|
address curveAddress,
|
||||||
|
int128 fromTokenIdx,
|
||||||
|
int128 toTokenIdx,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = 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_dx_underlying.selector,
|
||||||
|
fromTokenIdx,
|
||||||
|
toTokenIdx,
|
||||||
|
makerTokenAmounts[i]
|
||||||
|
));
|
||||||
|
uint256 sellAmount = 0;
|
||||||
|
if (didSucceed) {
|
||||||
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
takerTokenAmounts[i] = sellAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
@ -500,52 +565,28 @@ contract ERC20BridgeSampler is
|
|||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleBuysFromLiquidityProviderRegistry(
|
function sampleBuysFromLiquidityProviderRegistry(
|
||||||
address registryAddress,
|
address registryAddress,
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] memory makerTokenAmounts
|
uint256[] memory makerTokenAmounts,
|
||||||
|
FakeBuyOptions memory opts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
view
|
view
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
{
|
{
|
||||||
// Initialize array of taker token amounts.
|
return _sampleApproximateBuysFromSource(
|
||||||
uint256 numSamples = makerTokenAmounts.length;
|
|
||||||
takerTokenAmounts = new uint256[](numSamples);
|
|
||||||
|
|
||||||
// Query registry for provider address.
|
|
||||||
address providerAddress = getLiquidityProviderFromRegistry(
|
|
||||||
registryAddress,
|
|
||||||
takerToken,
|
|
||||||
makerToken
|
|
||||||
);
|
|
||||||
// If provider doesn't exist, return all zeros.
|
|
||||||
if (providerAddress == address(0)) {
|
|
||||||
return takerTokenAmounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, query liquidity provider for quotes.
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
|
|
||||||
abi.encodeWithSelector(
|
|
||||||
ILiquidityProvider(0).getBuyQuote.selector,
|
|
||||||
takerToken,
|
takerToken,
|
||||||
makerToken,
|
makerToken,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts,
|
||||||
));
|
opts,
|
||||||
uint256 sellAmount = 0;
|
this.sampleSellsFromLiquidityProviderRegistry.selector,
|
||||||
if (didSucceed) {
|
registryAddress
|
||||||
sellAmount = abi.decode(resultData, (uint256));
|
);
|
||||||
} else {
|
|
||||||
// Exit early if the amount is too high for the liquidity provider to serve
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
takerTokenAmounts[i] = sellAmount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Returns the address of a liquidity provider for the given market
|
/// @dev Returns the address of a liquidity provider for the given market
|
||||||
@ -639,4 +680,131 @@ contract ERC20BridgeSampler is
|
|||||||
{
|
{
|
||||||
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuy(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256 takerTokenAmount,
|
||||||
|
bytes4 selector,
|
||||||
|
address plpRegistryAddress
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 makerTokenAmount)
|
||||||
|
{
|
||||||
|
bytes memory callData;
|
||||||
|
uint256[] memory tmpTakerAmounts = new uint256[](1);
|
||||||
|
tmpTakerAmounts[0] = takerTokenAmount;
|
||||||
|
if (selector == this.sampleSellsFromKyberNetwork.selector) {
|
||||||
|
callData = abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromKyberNetwork.selector,
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
tmpTakerAmounts
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
callData = abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromLiquidityProviderRegistry.selector,
|
||||||
|
plpRegistryAddress,
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
tmpTakerAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
(bool success, bytes memory resultData) = address(this).staticcall(callData);
|
||||||
|
if (!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable indent
|
||||||
|
makerTokenAmount = abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sampleApproximateBuysFromSource(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts,
|
||||||
|
FakeBuyOptions memory opts,
|
||||||
|
bytes4 selector,
|
||||||
|
address plpRegistryAddress
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
if (makerTokenAmounts.length == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
uint256 sellAmount;
|
||||||
|
uint256 buyAmount;
|
||||||
|
uint256 slippageFromTarget;
|
||||||
|
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||||
|
sellAmount = _sampleSellForApproximateBuy(
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
makerTokenAmounts[0],
|
||||||
|
selector,
|
||||||
|
plpRegistryAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
if (sellAmount == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
buyAmount = _sampleSellForApproximateBuy(
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
sellAmount,
|
||||||
|
selector,
|
||||||
|
plpRegistryAddress
|
||||||
|
);
|
||||||
|
if (buyAmount == 0) {
|
||||||
|
return takerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
|
||||||
|
for (uint256 iter = 0; iter < opts.maxIterations; iter++) {
|
||||||
|
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
|
||||||
|
sellAmount = LibMath.getPartialAmountCeil(
|
||||||
|
makerTokenAmounts[i],
|
||||||
|
buyAmount,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
sellAmount = LibMath.getPartialAmountCeil(
|
||||||
|
(10000 + opts.targetSlippageBps),
|
||||||
|
10000,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
uint256 _buyAmount = _sampleSellForApproximateBuy(
|
||||||
|
takerToken,
|
||||||
|
makerToken,
|
||||||
|
sellAmount,
|
||||||
|
selector,
|
||||||
|
plpRegistryAddress
|
||||||
|
);
|
||||||
|
if (_buyAmount == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// We re-use buyAmount next iteration, only assign if it is
|
||||||
|
// non zero
|
||||||
|
buyAmount = _buyAmount;
|
||||||
|
// If we've reached our goal, exit early
|
||||||
|
if (buyAmount >= makerTokenAmounts[i]) {
|
||||||
|
uint256 slippageFromTarget = (buyAmount - makerTokenAmounts[i]) * 10000 /
|
||||||
|
makerTokenAmounts[i];
|
||||||
|
if (slippageFromTarget <= opts.targetSlippageBps) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
|
||||||
|
// if we hit a max iteration limit
|
||||||
|
// We scale the sell amount to get the approximate target
|
||||||
|
takerTokenAmounts[i] = LibMath.getPartialAmountCeil(
|
||||||
|
makerTokenAmounts[i],
|
||||||
|
buyAmount,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,11 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|||||||
|
|
||||||
interface IERC20BridgeSampler {
|
interface IERC20BridgeSampler {
|
||||||
|
|
||||||
|
struct FakeBuyOptions {
|
||||||
|
uint256 targetSlippageBps;
|
||||||
|
uint256 maxIterations;
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Call multiple public functions on this contract in a single transaction.
|
/// @dev Call multiple public functions on this contract in a single transaction.
|
||||||
/// @param callDatas ABI-encoded call data for each function call.
|
/// @param callDatas ABI-encoded call data for each function call.
|
||||||
/// @return callResults ABI-encoded results data for each call.
|
/// @return callResults ABI-encoded results data for each call.
|
||||||
@ -73,6 +78,23 @@ interface IERC20BridgeSampler {
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
returns (uint256[] memory makerTokenAmounts);
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Kyber.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromKyberNetwork(
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] calldata makerTokenAmounts,
|
||||||
|
FakeBuyOptions calldata opts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts);
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
/// @dev Sample sell quotes from Eth2Dai/Oasis.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
@ -106,7 +128,7 @@ interface IERC20BridgeSampler {
|
|||||||
/// @dev Sample buy quotes from Uniswap.
|
/// @dev Sample buy quotes from Uniswap.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
/// @param makerTokenAmounts Maker token sell amount for each sample.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleBuysFromUniswap(
|
function sampleBuysFromUniswap(
|
||||||
@ -121,7 +143,7 @@ interface IERC20BridgeSampler {
|
|||||||
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
/// @dev Sample buy quotes from Eth2Dai/Oasis.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
/// @param takerTokenAmounts Maker token sell amount for each sample.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleBuysFromEth2Dai(
|
function sampleBuysFromEth2Dai(
|
||||||
@ -150,6 +172,23 @@ interface IERC20BridgeSampler {
|
|||||||
view
|
view
|
||||||
returns (uint256[] memory makerTokenAmounts);
|
returns (uint256[] memory makerTokenAmounts);
|
||||||
|
|
||||||
|
/// @dev Sample buy 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 makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromCurve(
|
||||||
|
address curveAddress,
|
||||||
|
int128 fromTokenIdx,
|
||||||
|
int128 toTokenIdx,
|
||||||
|
uint256[] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts);
|
||||||
|
|
||||||
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
|
||||||
/// @param registryAddress Address of the liquidity provider registry contract.
|
/// @param registryAddress Address of the liquidity provider registry contract.
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
@ -172,13 +211,16 @@ interface IERC20BridgeSampler {
|
|||||||
/// @param takerToken Address of the taker token (what to sell).
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleBuysFromLiquidityProviderRegistry(
|
function sampleBuysFromLiquidityProviderRegistry(
|
||||||
address registryAddress,
|
address registryAddress,
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] calldata makerTokenAmounts
|
uint256[] calldata makerTokenAmounts,
|
||||||
|
FakeBuyOptions calldata opts
|
||||||
|
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
view
|
view
|
||||||
|
@ -32,6 +32,10 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
|
||||||
const MAKER_TOKEN = randomAddress();
|
const MAKER_TOKEN = randomAddress();
|
||||||
const TAKER_TOKEN = randomAddress();
|
const TAKER_TOKEN = randomAddress();
|
||||||
|
const FAKE_BUY_OPTS = {
|
||||||
|
targetSlippageBps: new BigNumber(5),
|
||||||
|
maxIterations: new BigNumber(5),
|
||||||
|
};
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||||
@ -169,8 +173,15 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
const sampleOutputs = [];
|
const sampleOutputs = [];
|
||||||
for (const amount of sampleAmounts) {
|
for (const amount of sampleAmounts) {
|
||||||
if (source === 'Eth2Dai') {
|
if (source === 'Kyber' || source === 'Eth2Dai') {
|
||||||
sampleOutputs.push(getDeterministicBuyQuote(ETH2DAI_SALT, sellToken, buyToken, amount));
|
sampleOutputs.push(
|
||||||
|
getDeterministicBuyQuote(
|
||||||
|
source === 'Kyber' ? KYBER_SALT : ETH2DAI_SALT,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
amount,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else if (source === 'Uniswap') {
|
} else if (source === 'Uniswap') {
|
||||||
sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
|
sampleOutputs.push(getDeterministicUniswapBuyQuote(sellToken, buyToken, amount));
|
||||||
}
|
}
|
||||||
@ -204,7 +215,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
|
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
|
||||||
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
|
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
|
||||||
const _upperLimit = getRandomPortion(getRandomInteger(1, 1000).times(10 ** tokenDecimals));
|
const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals));
|
||||||
const _count = count || _.random(1, 16);
|
const _count = count || _.random(1, 16);
|
||||||
const d = _upperLimit.div(_count);
|
const d = _upperLimit.div(_count);
|
||||||
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
|
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
|
||||||
@ -330,7 +341,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
expect(quotes).to.deep.eq([]);
|
expect(quotes).to.deep.eq([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can quote token - token', async () => {
|
it('can quote token -> token', async () => {
|
||||||
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
const [expectedQuotes] = getDeterministicSellQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
const quotes = await testContract
|
const quotes = await testContract
|
||||||
@ -388,6 +399,108 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
blockchainTests.resets('sampleBuysFromKyberNetwork()', () => {
|
||||||
|
const ACCEPTABLE_SLIPPAGE = 0.0005;
|
||||||
|
before(async () => {
|
||||||
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws if tokens are the same', async () => {
|
||||||
|
const tx = testContract.sampleBuysFromKyberNetwork(MAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS).callAsync();
|
||||||
|
return expect(tx).to.revertWith(INVALID_TOKEN_PAIR_ERROR);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can return no quotes', async () => {
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, [], FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq([]);
|
||||||
|
});
|
||||||
|
const expectQuotesWithinRange = (
|
||||||
|
quotes: BigNumber[],
|
||||||
|
expectedQuotes: BigNumber[],
|
||||||
|
maxSlippage: BigNumber | number,
|
||||||
|
) => {
|
||||||
|
quotes.forEach((_q, i) => {
|
||||||
|
// If we're within 1 base unit of a low decimal token
|
||||||
|
// then that's as good as we're going to get (and slippage is "high")
|
||||||
|
if (
|
||||||
|
expectedQuotes[i].isZero() ||
|
||||||
|
BigNumber.max(expectedQuotes[i], quotes[i])
|
||||||
|
.minus(BigNumber.min(expectedQuotes[i], quotes[i]))
|
||||||
|
.eq(1)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const slippage = quotes[i]
|
||||||
|
.dividedBy(expectedQuotes[i])
|
||||||
|
.minus(1)
|
||||||
|
.decimalPlaces(4);
|
||||||
|
expect(slippage, `quote[${i}]: ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.gte(0);
|
||||||
|
expect(slippage, `quote[${i}] ${slippage} ${quotes[i]} ${expectedQuotes[i]}`).to.be.bignumber.lte(
|
||||||
|
new BigNumber(maxSlippage),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
it('can quote token -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, MAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, MAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote token -> ETH', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(TAKER_TOKEN, WETH_ADDRESS, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero if token -> ETH fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(TAKER_TOKEN, WETH_ADDRESS, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can quote ETH -> token', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const [expectedQuotes] = getDeterministicBuyQuotes(WETH_ADDRESS, TAKER_TOKEN, ['Kyber'], sampleAmounts);
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expectQuotesWithinRange(quotes, expectedQuotes, ACCEPTABLE_SLIPPAGE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns zero if ETH -> token fails', async () => {
|
||||||
|
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
|
||||||
|
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
|
||||||
|
await enableFailTriggerAsync();
|
||||||
|
const quotes = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(WETH_ADDRESS, TAKER_TOKEN, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(quotes).to.deep.eq(expectedQuotes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
|
blockchainTests.resets('sampleSellsFromEth2Dai()', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@ -773,7 +886,13 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
it('should be able to query buys from the liquidity provider', async () => {
|
it('should be able to query buys from the liquidity provider', async () => {
|
||||||
const result = await testContract
|
const result = await testContract
|
||||||
.sampleBuysFromLiquidityProviderRegistry(registryContract.address, yAsset, xAsset, sampleAmounts)
|
.sampleBuysFromLiquidityProviderRegistry(
|
||||||
|
registryContract.address,
|
||||||
|
yAsset,
|
||||||
|
xAsset,
|
||||||
|
sampleAmounts,
|
||||||
|
FAKE_BUY_OPTS,
|
||||||
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach((value, idx) => {
|
result.forEach((value, idx) => {
|
||||||
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
expect(value).is.bignumber.eql(sampleAmounts[idx].plus(1));
|
||||||
@ -787,6 +906,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
yAsset,
|
yAsset,
|
||||||
randomAddress(),
|
randomAddress(),
|
||||||
sampleAmounts,
|
sampleAmounts,
|
||||||
|
FAKE_BUY_OPTS,
|
||||||
)
|
)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach(value => {
|
result.forEach(value => {
|
||||||
@ -796,7 +916,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
it('should just return zeros if the registry does not exist', async () => {
|
it('should just return zeros if the registry does not exist', async () => {
|
||||||
const result = await testContract
|
const result = await testContract
|
||||||
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts)
|
.sampleBuysFromLiquidityProviderRegistry(randomAddress(), yAsset, xAsset, sampleAmounts, FAKE_BUY_OPTS)
|
||||||
.callAsync();
|
.callAsync();
|
||||||
result.forEach(value => {
|
result.forEach(value => {
|
||||||
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
expect(value).is.bignumber.eql(constants.ZERO_AMOUNT);
|
||||||
|
@ -2,26 +2,33 @@ import { artifacts, ERC20BridgeSamplerContract } from '@0x/contracts-erc20-bridg
|
|||||||
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
import { blockchainTests, constants, describe, expect, toBaseUnitAmount } from '@0x/contracts-test-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
export const VB = '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b';
|
||||||
|
blockchainTests.configure({
|
||||||
|
fork: {
|
||||||
|
unlockedAccounts: [VB],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
||||||
let testContract: ERC20BridgeSamplerContract;
|
let testContract: ERC20BridgeSamplerContract;
|
||||||
before(async () => {
|
before(async () => {
|
||||||
testContract = await ERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
testContract = await ERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.ERC20BridgeSampler,
|
artifacts.ERC20BridgeSampler,
|
||||||
env.provider,
|
env.provider,
|
||||||
{ ...env.txDefaults, from: '0x6cc5f688a315f3dc28a7781717a9a798a59fda7b' },
|
{ ...env.txDefaults, from: VB },
|
||||||
{},
|
{},
|
||||||
constants.NULL_ADDRESS,
|
constants.NULL_ADDRESS,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
describe('Curve', () => {
|
||||||
|
const CURVE_ADDRESS = '0xa2b47e3d5c44877cca798226b7b8118f9bfb7a56';
|
||||||
|
const DAI_TOKEN_INDEX = new BigNumber(0);
|
||||||
|
const USDC_TOKEN_INDEX = new BigNumber(1);
|
||||||
|
|
||||||
describe('sampleSellsFromCurve()', () => {
|
describe('sampleSellsFromCurve()', () => {
|
||||||
const curveAddress = '0x45f783cce6b7ff23b2ab2d70e416cdb7d6055f51';
|
|
||||||
const daiTokenIdx = new BigNumber(0);
|
|
||||||
const usdcTokenIdx = new BigNumber(1);
|
|
||||||
|
|
||||||
it('samples sells from Curve DAI->USDC', async () => {
|
it('samples sells from Curve DAI->USDC', async () => {
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleSellsFromCurve(curveAddress, daiTokenIdx, usdcTokenIdx, [toBaseUnitAmount(1)])
|
.sampleSellsFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1)])
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(samples.length).to.be.bignumber.greaterThan(0);
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
@ -29,10 +36,63 @@ blockchainTests.fork.resets('Mainnet Sampler Tests', env => {
|
|||||||
|
|
||||||
it('samples sells from Curve USDC->DAI', async () => {
|
it('samples sells from Curve USDC->DAI', async () => {
|
||||||
const samples = await testContract
|
const samples = await testContract
|
||||||
.sampleSellsFromCurve(curveAddress, usdcTokenIdx, daiTokenIdx, [toBaseUnitAmount(1, 6)])
|
.sampleSellsFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
||||||
|
.callAsync();
|
||||||
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sampleBuysFromCurve()', () => {
|
||||||
|
it('samples buys from Curve DAI->USDC', async () => {
|
||||||
|
// From DAI to USDC
|
||||||
|
// I want to buy 1 USDC
|
||||||
|
const samples = await testContract
|
||||||
|
.sampleBuysFromCurve(CURVE_ADDRESS, DAI_TOKEN_INDEX, USDC_TOKEN_INDEX, [toBaseUnitAmount(1, 6)])
|
||||||
|
.callAsync();
|
||||||
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('samples buys from Curve USDC->DAI', async () => {
|
||||||
|
// From USDC to DAI
|
||||||
|
// I want to buy 1 DAI
|
||||||
|
const samples = await testContract
|
||||||
|
.sampleBuysFromCurve(CURVE_ADDRESS, USDC_TOKEN_INDEX, DAI_TOKEN_INDEX, [toBaseUnitAmount(1)])
|
||||||
.callAsync();
|
.callAsync();
|
||||||
expect(samples.length).to.be.bignumber.greaterThan(0);
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('Kyber', () => {
|
||||||
|
const FAKE_BUY_OPTS = {
|
||||||
|
targetSlippageBps: new BigNumber(5),
|
||||||
|
maxIterations: new BigNumber(5),
|
||||||
|
};
|
||||||
|
const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
|
||||||
|
const WETH_ADDRESS = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
|
||||||
|
|
||||||
|
describe('sampleBuysFromKyber()', () => {
|
||||||
|
it('samples buys from Kyber WETH->DAI', async () => {
|
||||||
|
// From ETH to DAI
|
||||||
|
// I want to buy 1 DAI
|
||||||
|
const samples = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(WETH_ADDRESS, DAI_ADDRESS, [toBaseUnitAmount(1)], FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('samples buys from Kyber DAI->WETH', async () => {
|
||||||
|
// From USDC to DAI
|
||||||
|
// I want to buy 1 WETH
|
||||||
|
const samples = await testContract
|
||||||
|
.sampleBuysFromKyberNetwork(DAI_ADDRESS, WETH_ADDRESS, [toBaseUnitAmount(1)], FAKE_BUY_OPTS)
|
||||||
|
.callAsync();
|
||||||
|
expect(samples.length).to.be.bignumber.greaterThan(0);
|
||||||
|
expect(samples[0]).to.be.bignumber.greaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { ERC20BridgeSource, GetMarketOrdersOpts } from './types';
|
import { ERC20BridgeSource, FakeBuyOpts, GetMarketOrdersOpts } from './types';
|
||||||
|
|
||||||
// tslint:disable: custom-no-magic-numbers
|
// tslint:disable: custom-no-magic-numbers
|
||||||
|
|
||||||
@ -21,7 +21,16 @@ export const SELL_SOURCES = [
|
|||||||
/**
|
/**
|
||||||
* Valid sources for market buy.
|
* Valid sources for market buy.
|
||||||
*/
|
*/
|
||||||
export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
export const BUY_SOURCES = [
|
||||||
|
ERC20BridgeSource.Uniswap,
|
||||||
|
ERC20BridgeSource.Eth2Dai,
|
||||||
|
ERC20BridgeSource.Kyber,
|
||||||
|
// All Curve sources
|
||||||
|
ERC20BridgeSource.CurveUsdcDai,
|
||||||
|
ERC20BridgeSource.CurveUsdcDaiUsdt,
|
||||||
|
ERC20BridgeSource.CurveUsdcDaiUsdtBusd,
|
||||||
|
ERC20BridgeSource.CurveUsdcDaiUsdtTusd,
|
||||||
|
];
|
||||||
|
|
||||||
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
||||||
// tslint:disable-next-line: custom-no-magic-numbers
|
// tslint:disable-next-line: custom-no-magic-numbers
|
||||||
@ -37,6 +46,11 @@ export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
|||||||
shouldBatchBridgeOrders: true,
|
shouldBatchBridgeOrders: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_FAKE_BUY_OPTS: FakeBuyOpts = {
|
||||||
|
targetSlippageBps: new BigNumber(5),
|
||||||
|
maxIterations: new BigNumber(5),
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sources to poll for ETH fee price estimates.
|
* Sources to poll for ETH fee price estimates.
|
||||||
*/
|
*/
|
||||||
|
@ -4,9 +4,9 @@ import { ERC20BridgeAssetData, SignedOrder } from '@0x/types';
|
|||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, SignedOrderWithFillableAmounts } from '../../types';
|
||||||
|
import { getCurveInfo, isCurveSource } from '../source_utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_CURVE_OPTS,
|
|
||||||
ERC20_PROXY_ID,
|
ERC20_PROXY_ID,
|
||||||
NULL_ADDRESS,
|
NULL_ADDRESS,
|
||||||
NULL_BYTES,
|
NULL_BYTES,
|
||||||
@ -210,10 +210,8 @@ function createBridgeOrder(fill: CollapsedFill, opts: CreateOrderFromPathOpts):
|
|||||||
const bridgeAddress = getBridgeAddressFromSource(fill.source, opts);
|
const bridgeAddress = getBridgeAddressFromSource(fill.source, opts);
|
||||||
|
|
||||||
let makerAssetData;
|
let makerAssetData;
|
||||||
if (Object.keys(DEFAULT_CURVE_OPTS).includes(fill.source)) {
|
if (isCurveSource(fill.source)) {
|
||||||
const { curveAddress, tokens, version } = DEFAULT_CURVE_OPTS[fill.source];
|
const { curveAddress, fromTokenIdx, toTokenIdx, version } = getCurveInfo(fill.source, takerToken, makerToken);
|
||||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
|
||||||
const toTokenIdx = tokens.indexOf(makerToken);
|
|
||||||
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
makerAssetData = assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
makerToken,
|
makerToken,
|
||||||
bridgeAddress,
|
bridgeAddress,
|
||||||
|
@ -8,9 +8,9 @@ import { BatchedOperation } from './types';
|
|||||||
* Generate sample amounts up to `maxFillAmount`.
|
* Generate sample amounts up to `maxFillAmount`.
|
||||||
*/
|
*/
|
||||||
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
|
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 distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
|
||||||
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
|
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
|
||||||
const amounts = stepSizes.map((s, i) => {
|
const amounts = stepSizes.map((_s, i) => {
|
||||||
return maxFillAmount
|
return maxFillAmount
|
||||||
.times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)]))
|
.times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)]))
|
||||||
.integerValue(BigNumber.ROUND_UP);
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..';
|
import { BigNumber, ERC20BridgeSource, SignedOrder } from '../..';
|
||||||
|
import { getCurveInfo, isCurveSource } from '../source_utils';
|
||||||
|
|
||||||
import { DEFAULT_CURVE_OPTS } from './constants';
|
import { DEFAULT_FAKE_BUY_OPTS } from './constants';
|
||||||
import { BatchedOperation, DexSample } from './types';
|
import { BatchedOperation, DexSample, FakeBuyOpts } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Composable operations that can be batched in a single transaction,
|
* Composable operations that can be batched in a single transaction,
|
||||||
@ -48,6 +49,23 @@ export const samplerOperations = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getKyberBuyQuotes(
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS,
|
||||||
|
): BatchedOperation<BigNumber[]> {
|
||||||
|
return {
|
||||||
|
encodeCall: contract => {
|
||||||
|
return contract
|
||||||
|
.sampleBuysFromKyberNetwork(takerToken, makerToken, makerFillAmounts, fakeBuyOpts)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
},
|
||||||
|
handleCallResultsAsync: async (contract, callResults) => {
|
||||||
|
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromKyberNetwork', callResults);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
getUniswapSellQuotes(
|
getUniswapSellQuotes(
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
@ -64,6 +82,22 @@ export const samplerOperations = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getUniswapBuyQuotes(
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
): BatchedOperation<BigNumber[]> {
|
||||||
|
return {
|
||||||
|
encodeCall: contract => {
|
||||||
|
return contract
|
||||||
|
.sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
},
|
||||||
|
handleCallResultsAsync: async (contract, callResults) => {
|
||||||
|
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromUniswap', callResults);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
getLiquidityProviderSellQuotes(
|
getLiquidityProviderSellQuotes(
|
||||||
liquidityProviderRegistryAddress: string,
|
liquidityProviderRegistryAddress: string,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
@ -94,6 +128,7 @@ export const samplerOperations = {
|
|||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerFillAmounts: BigNumber[],
|
makerFillAmounts: BigNumber[],
|
||||||
|
fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS,
|
||||||
): BatchedOperation<BigNumber[]> {
|
): BatchedOperation<BigNumber[]> {
|
||||||
return {
|
return {
|
||||||
encodeCall: contract => {
|
encodeCall: contract => {
|
||||||
@ -103,6 +138,7 @@ export const samplerOperations = {
|
|||||||
takerToken,
|
takerToken,
|
||||||
makerToken,
|
makerToken,
|
||||||
makerFillAmounts,
|
makerFillAmounts,
|
||||||
|
fakeBuyOpts,
|
||||||
)
|
)
|
||||||
.getABIEncodedTransactionData();
|
.getABIEncodedTransactionData();
|
||||||
},
|
},
|
||||||
@ -130,6 +166,22 @@ export const samplerOperations = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getEth2DaiBuyQuotes(
|
||||||
|
makerToken: string,
|
||||||
|
takerToken: string,
|
||||||
|
makerFillAmounts: BigNumber[],
|
||||||
|
): BatchedOperation<BigNumber[]> {
|
||||||
|
return {
|
||||||
|
encodeCall: contract => {
|
||||||
|
return contract
|
||||||
|
.sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
},
|
||||||
|
handleCallResultsAsync: async (contract, callResults) => {
|
||||||
|
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromEth2Dai', callResults);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
getCurveSellQuotes(
|
getCurveSellQuotes(
|
||||||
curveAddress: string,
|
curveAddress: string,
|
||||||
fromTokenIdx: number,
|
fromTokenIdx: number,
|
||||||
@ -152,35 +204,25 @@ export const samplerOperations = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
getUniswapBuyQuotes(
|
getCurveBuyQuotes(
|
||||||
makerToken: string,
|
curveAddress: string,
|
||||||
takerToken: string,
|
fromTokenIdx: number,
|
||||||
|
toTokenIdx: number,
|
||||||
makerFillAmounts: BigNumber[],
|
makerFillAmounts: BigNumber[],
|
||||||
): BatchedOperation<BigNumber[]> {
|
): BatchedOperation<BigNumber[]> {
|
||||||
return {
|
return {
|
||||||
encodeCall: contract => {
|
encodeCall: contract => {
|
||||||
return contract
|
return contract
|
||||||
.sampleBuysFromUniswap(takerToken, makerToken, makerFillAmounts)
|
.sampleBuysFromCurve(
|
||||||
|
curveAddress,
|
||||||
|
new BigNumber(fromTokenIdx),
|
||||||
|
new BigNumber(toTokenIdx),
|
||||||
|
makerFillAmounts,
|
||||||
|
)
|
||||||
.getABIEncodedTransactionData();
|
.getABIEncodedTransactionData();
|
||||||
},
|
},
|
||||||
handleCallResultsAsync: async (contract, callResults) => {
|
handleCallResultsAsync: async (contract, callResults) => {
|
||||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromUniswap', callResults);
|
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromCurve', callResults);
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
getEth2DaiBuyQuotes(
|
|
||||||
makerToken: string,
|
|
||||||
takerToken: string,
|
|
||||||
makerFillAmounts: BigNumber[],
|
|
||||||
): BatchedOperation<BigNumber[]> {
|
|
||||||
return {
|
|
||||||
encodeCall: contract => {
|
|
||||||
return contract
|
|
||||||
.sampleBuysFromEth2Dai(takerToken, makerToken, makerFillAmounts)
|
|
||||||
.getABIEncodedTransactionData();
|
|
||||||
},
|
|
||||||
handleCallResultsAsync: async (contract, callResults) => {
|
|
||||||
return contract.getABIDecodedReturnData<BigNumber[]>('sampleBuysFromEth2Dai', callResults);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -226,10 +268,10 @@ export const samplerOperations = {
|
|||||||
},
|
},
|
||||||
constant<T>(result: T): BatchedOperation<T> {
|
constant<T>(result: T): BatchedOperation<T> {
|
||||||
return {
|
return {
|
||||||
encodeCall: contract => {
|
encodeCall: _contract => {
|
||||||
return '0x';
|
return '0x';
|
||||||
},
|
},
|
||||||
handleCallResultsAsync: async (contract, callResults) => {
|
handleCallResultsAsync: async (_contract, _callResults) => {
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -266,10 +308,8 @@ export const samplerOperations = {
|
|||||||
batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
batchedOperation = samplerOperations.getUniswapSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||||
} else if (source === ERC20BridgeSource.Kyber) {
|
} else if (source === ERC20BridgeSource.Kyber) {
|
||||||
batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
batchedOperation = samplerOperations.getKyberSellQuotes(makerToken, takerToken, takerFillAmounts);
|
||||||
} else if (Object.keys(DEFAULT_CURVE_OPTS).includes(source)) {
|
} else if (isCurveSource(source)) {
|
||||||
const { curveAddress, tokens } = DEFAULT_CURVE_OPTS[source];
|
const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken);
|
||||||
const fromTokenIdx = tokens.indexOf(takerToken);
|
|
||||||
const toTokenIdx = tokens.indexOf(makerToken);
|
|
||||||
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
||||||
batchedOperation = samplerOperations.getCurveSellQuotes(
|
batchedOperation = samplerOperations.getCurveSellQuotes(
|
||||||
curveAddress,
|
curveAddress,
|
||||||
@ -327,41 +367,69 @@ export const samplerOperations = {
|
|||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerFillAmounts: BigNumber[],
|
makerFillAmounts: BigNumber[],
|
||||||
liquidityProviderRegistryAddress?: string | undefined,
|
liquidityProviderRegistryAddress?: string | undefined,
|
||||||
|
fakeBuyOpts: FakeBuyOpts = DEFAULT_FAKE_BUY_OPTS,
|
||||||
): BatchedOperation<DexSample[][]> {
|
): BatchedOperation<DexSample[][]> {
|
||||||
const subOps = sources.map(source => {
|
const subOps = sources
|
||||||
|
.map(source => {
|
||||||
|
let batchedOperation;
|
||||||
if (source === ERC20BridgeSource.Eth2Dai) {
|
if (source === ERC20BridgeSource.Eth2Dai) {
|
||||||
return samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
batchedOperation = samplerOperations.getEth2DaiBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||||
} else if (source === ERC20BridgeSource.Uniswap) {
|
} else if (source === ERC20BridgeSource.Uniswap) {
|
||||||
return samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
batchedOperation = samplerOperations.getUniswapBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||||
|
} else if (source === ERC20BridgeSource.Kyber) {
|
||||||
|
batchedOperation = samplerOperations.getKyberBuyQuotes(
|
||||||
|
makerToken,
|
||||||
|
takerToken,
|
||||||
|
makerFillAmounts,
|
||||||
|
fakeBuyOpts,
|
||||||
|
);
|
||||||
|
} else if (isCurveSource(source)) {
|
||||||
|
const { curveAddress, fromTokenIdx, toTokenIdx } = getCurveInfo(source, takerToken, makerToken);
|
||||||
|
if (fromTokenIdx !== -1 && toTokenIdx !== -1) {
|
||||||
|
batchedOperation = samplerOperations.getCurveBuyQuotes(
|
||||||
|
curveAddress,
|
||||||
|
fromTokenIdx,
|
||||||
|
toTokenIdx,
|
||||||
|
makerFillAmounts,
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (source === ERC20BridgeSource.LiquidityProvider) {
|
} else if (source === ERC20BridgeSource.LiquidityProvider) {
|
||||||
if (liquidityProviderRegistryAddress === undefined) {
|
if (liquidityProviderRegistryAddress === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
|
'Cannot sample liquidity from a LiquidityProvider liquidity pool, if a registry is not provided.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return samplerOperations.getLiquidityProviderBuyQuotes(
|
batchedOperation = samplerOperations.getLiquidityProviderBuyQuotes(
|
||||||
liquidityProviderRegistryAddress,
|
liquidityProviderRegistryAddress,
|
||||||
makerToken,
|
makerToken,
|
||||||
takerToken,
|
takerToken,
|
||||||
makerFillAmounts,
|
makerFillAmounts,
|
||||||
|
fakeBuyOpts,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unsupported buy sample source: ${source}`);
|
throw new Error(`Unsupported buy sample source: ${source}`);
|
||||||
}
|
}
|
||||||
});
|
return { source, batchedOperation };
|
||||||
|
})
|
||||||
|
.filter(op => op.batchedOperation) as Array<{
|
||||||
|
batchedOperation: BatchedOperation<BigNumber[]>;
|
||||||
|
source: ERC20BridgeSource;
|
||||||
|
}>;
|
||||||
return {
|
return {
|
||||||
encodeCall: contract => {
|
encodeCall: contract => {
|
||||||
const subCalls = subOps.map(op => op.encodeCall(contract));
|
const subCalls = subOps.map(op => op.batchedOperation.encodeCall(contract));
|
||||||
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
return contract.batchCall(subCalls).getABIEncodedTransactionData();
|
||||||
},
|
},
|
||||||
handleCallResultsAsync: async (contract, callResults) => {
|
handleCallResultsAsync: async (contract, callResults) => {
|
||||||
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
const rawSubCallResults = contract.getABIDecodedReturnData<string[]>('batchCall', callResults);
|
||||||
const samples = await Promise.all(
|
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) => ({
|
return samples[i].map((output, j) => ({
|
||||||
source,
|
source: op.source,
|
||||||
output,
|
output,
|
||||||
input: makerFillAmounts[j],
|
input: makerFillAmounts[j],
|
||||||
}));
|
}));
|
||||||
|
@ -197,3 +197,12 @@ export interface BatchedOperation<TResult> {
|
|||||||
encodeCall(contract: IERC20BridgeSamplerContract): string;
|
encodeCall(contract: IERC20BridgeSamplerContract): string;
|
||||||
handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise<TResult>;
|
handleCallResultsAsync(contract: IERC20BridgeSamplerContract, callResults: string): Promise<TResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used in the ERC20BridgeSampler when a source does not natively
|
||||||
|
* support sampling via a specific buy amount.
|
||||||
|
*/
|
||||||
|
export interface FakeBuyOpts {
|
||||||
|
targetSlippageBps: BigNumber;
|
||||||
|
maxIterations: BigNumber;
|
||||||
|
}
|
||||||
|
@ -101,7 +101,6 @@ export function simulateBestCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
|
|||||||
...quoteInfo.opts,
|
...quoteInfo.opts,
|
||||||
};
|
};
|
||||||
const result = fillQuoteOrders(
|
const result = fillQuoteOrders(
|
||||||
quoteInfo.side,
|
|
||||||
createBestCaseFillOrderCalls(quoteInfo),
|
createBestCaseFillOrderCalls(quoteInfo),
|
||||||
quoteInfo.fillAmount,
|
quoteInfo.fillAmount,
|
||||||
quoteInfo.gasPrice.times(opts.protocolFeeMultiplier),
|
quoteInfo.gasPrice.times(opts.protocolFeeMultiplier),
|
||||||
@ -119,7 +118,6 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
|
|||||||
const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier);
|
const protocolFeePerFillOrder = quoteInfo.gasPrice.times(opts.protocolFeeMultiplier);
|
||||||
const result = {
|
const result = {
|
||||||
...fillQuoteOrders(
|
...fillQuoteOrders(
|
||||||
quoteInfo.side,
|
|
||||||
createWorstCaseFillOrderCalls(quoteInfo),
|
createWorstCaseFillOrderCalls(quoteInfo),
|
||||||
quoteInfo.fillAmount,
|
quoteInfo.fillAmount,
|
||||||
protocolFeePerFillOrder,
|
protocolFeePerFillOrder,
|
||||||
@ -136,7 +134,6 @@ export function simulateWorstCaseFill(quoteInfo: QuoteFillInfo): QuoteFillResult
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function fillQuoteOrders(
|
export function fillQuoteOrders(
|
||||||
side: MarketOperation,
|
|
||||||
fillOrders: QuoteFillOrderCall[],
|
fillOrders: QuoteFillOrderCall[],
|
||||||
inputAmount: BigNumber,
|
inputAmount: BigNumber,
|
||||||
protocolFeePerFillOrder: BigNumber,
|
protocolFeePerFillOrder: BigNumber,
|
||||||
@ -335,7 +332,7 @@ function fromIntermediateQuoteFillResult(ir: IntermediateQuoteFillResult, quoteI
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getFlattenedFillsFromOrders(orders: OptimizedMarketOrder[]): CollapsedFill[] {
|
export function getFlattenedFillsFromOrders(orders: OptimizedMarketOrder[]): CollapsedFill[] {
|
||||||
const fills = [];
|
const fills: CollapsedFill[] = [];
|
||||||
for (const o of orders) {
|
for (const o of orders) {
|
||||||
fills.push(...o.fills);
|
fills.push(...o.fills);
|
||||||
}
|
}
|
||||||
|
17
packages/asset-swapper/src/utils/source_utils.ts
Normal file
17
packages/asset-swapper/src/utils/source_utils.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { DEFAULT_CURVE_OPTS } from './market_operation_utils/constants';
|
||||||
|
import { ERC20BridgeSource } from './market_operation_utils/types';
|
||||||
|
|
||||||
|
export const isCurveSource = (source: ERC20BridgeSource): boolean => {
|
||||||
|
return Object.keys(DEFAULT_CURVE_OPTS).includes(source);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCurveInfo = (
|
||||||
|
source: ERC20BridgeSource,
|
||||||
|
takerToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
): { curveAddress: string; fromTokenIdx: number; toTokenIdx: number; version: number } => {
|
||||||
|
const { curveAddress, tokens, version } = DEFAULT_CURVE_OPTS[source];
|
||||||
|
const fromTokenIdx = tokens.indexOf(takerToken);
|
||||||
|
const toTokenIdx = tokens.indexOf(makerToken);
|
||||||
|
return { curveAddress, fromTokenIdx, toTokenIdx, version };
|
||||||
|
};
|
@ -721,7 +721,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
sampleDistributionBase: 1,
|
sampleDistributionBase: 1,
|
||||||
bridgeSlippage: 0,
|
bridgeSlippage: 0,
|
||||||
maxFallbackSlippage: 100,
|
maxFallbackSlippage: 100,
|
||||||
excludedSources: Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[],
|
excludedSources: [...(Object.keys(DEFAULT_CURVE_OPTS) as ERC20BridgeSource[]), ERC20BridgeSource.Kyber],
|
||||||
allowFallback: false,
|
allowFallback: false,
|
||||||
shouldBatchBridgeOrders: false,
|
shouldBatchBridgeOrders: false,
|
||||||
};
|
};
|
||||||
|
@ -232,7 +232,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
fillsCount,
|
fillsCount,
|
||||||
count: 1,
|
count: 1,
|
||||||
});
|
});
|
||||||
const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
||||||
@ -254,7 +254,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
count: 1,
|
count: 1,
|
||||||
});
|
});
|
||||||
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
||||||
@ -280,7 +280,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
count: 1,
|
count: 1,
|
||||||
});
|
});
|
||||||
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
||||||
@ -303,7 +303,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
count: 1,
|
count: 1,
|
||||||
});
|
});
|
||||||
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
||||||
@ -328,7 +328,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
});
|
});
|
||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
||||||
@ -355,7 +355,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
||||||
@ -382,7 +382,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
||||||
@ -408,7 +408,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
});
|
});
|
||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
||||||
@ -435,7 +435,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
||||||
@ -462,7 +462,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
||||||
@ -479,7 +479,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const fillableInput = getRandomOrderSize();
|
const fillableInput = getRandomOrderSize();
|
||||||
const fillableOutput = getRandomOrderSize();
|
const fillableOutput = getRandomOrderSize();
|
||||||
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
||||||
const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
||||||
@ -494,7 +494,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const fillableOutput = getRandomOrderSize();
|
const fillableOutput = getRandomOrderSize();
|
||||||
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
||||||
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
expect(totalFilledInput).to.bignumber.eq(inputFillAmount);
|
||||||
@ -508,7 +508,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const fillableOutput = getRandomOrderSize();
|
const fillableOutput = getRandomOrderSize();
|
||||||
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
||||||
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
const fillOrders = createQuoteFillOrders({ fillableInput, fillableOutput, side });
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
expect(totalFilledInput).to.bignumber.eq(fillableInput);
|
||||||
@ -530,7 +530,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
});
|
});
|
||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, totalFillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
||||||
@ -554,7 +554,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = totalFillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
||||||
@ -578,7 +578,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
const signedInputFeeRate = side === MarketOperation.Sell ? inputFeeRate : -inputFeeRate;
|
||||||
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
const totalFillableInput = fillableInput.times(signedInputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = totalFillableInput.times(3 / 2).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, totalFillableInput, EPS);
|
||||||
@ -601,7 +601,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
});
|
});
|
||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, fillableInput, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
||||||
@ -625,7 +625,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
const inputFillAmount = fillableInput.times(2 / 3).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, inputFillAmount, EPS);
|
||||||
@ -649,7 +649,7 @@ describe('quote_simulation tests', async () => {
|
|||||||
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
const signedOutputFeeRate = side === MarketOperation.Sell ? -outputFeeRate : outputFeeRate;
|
||||||
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
const totalFillableOutput = fillableOutput.times(signedOutputFeeRate + 1).integerValue();
|
||||||
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
const inputFillAmount = fillableInput.times(3 / 2).integerValue();
|
||||||
const result = fillQuoteOrders(side, fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
const result = fillQuoteOrders(fillOrders, inputFillAmount, ONE, GAS_SCHEDULE);
|
||||||
const totalFilledInput = result.input.plus(result.inputFee);
|
const totalFilledInput = result.input.plus(result.inputFee);
|
||||||
const totalFilledOutput = result.output.plus(result.outputFee);
|
const totalFilledOutput = result.output.plus(result.outputFee);
|
||||||
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
assertIntegerRoughlyEquals(totalFilledInput, fillableInput, EPS);
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547",
|
"devUtils": "0x74134cf88b21383713e096a5ecf59e297dc7f547",
|
||||||
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
|
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
|
||||||
"uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad",
|
"uniswapBridge": "0x36691c4f426eb8f42f150ebde43069a31cb080ad",
|
||||||
"erc20BridgeSampler": "0xb2dee8cf2a06fbf0942fda5521f890b6e9911bfe",
|
"erc20BridgeSampler": "0xabee9a41f928c3b3b799b239a0f524343c7260c5",
|
||||||
"kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610",
|
"kyberBridge": "0x1c29670f7a77f1052d30813a0a4f632c78a02610",
|
||||||
"eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1",
|
"eth2DaiBridge": "0x991c745401d5b5e469b8c3e2cb02c748f08754f1",
|
||||||
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
|
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
|
||||||
@ -120,7 +120,7 @@
|
|||||||
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
||||||
"uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d",
|
"uniswapBridge": "0x0e85f89f29998df65402391478e5924700c0079d",
|
||||||
"eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b",
|
"eth2DaiBridge": "0x2d47147429b474d2e4f83e658015858a1312ed5b",
|
||||||
"erc20BridgeSampler": "0xd3fccc4af0732e99290a57bbc2cc2afcbd08283f",
|
"erc20BridgeSampler": "0x1fdfcf612026c8deed586353430821bee6330dc8",
|
||||||
"kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44",
|
"kyberBridge": "0xaecfa25920f892b6eb496e1f6e84037f59da7f44",
|
||||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"dydxBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1",
|
"dydxBridge": "0x3be8e59038d8c4e8d8776ca40ef2f024bad95ad1",
|
||||||
|
@ -9,6 +9,14 @@
|
|||||||
{
|
{
|
||||||
"note": "Added `Forwarder.marketSellAmountWithEth`",
|
"note": "Added `Forwarder.marketSellAmountWithEth`",
|
||||||
"pr": 2521
|
"pr": 2521
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`",
|
||||||
|
"pr": 2551
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `ERC20BridgeSampler.sampleBuysFromKyberNetwork`",
|
||||||
|
"pr": 2551
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
File diff suppressed because one or more lines are too long
@ -93,6 +93,20 @@
|
|||||||
"stateMutability": "view",
|
"stateMutability": "view",
|
||||||
"type": "function"
|
"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": "makerTokenAmounts", "type": "uint256[]" }
|
||||||
|
],
|
||||||
|
"name": "sampleBuysFromCurve",
|
||||||
|
"outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"constant": true,
|
"constant": true,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@ -106,13 +120,44 @@
|
|||||||
"stateMutability": "view",
|
"stateMutability": "view",
|
||||||
"type": "function"
|
"type": "function"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||||
|
{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "maxIterations", "type": "uint256" }
|
||||||
|
],
|
||||||
|
"internalType": "struct IERC20BridgeSampler.FakeBuyOptions",
|
||||||
|
"name": "opts",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "sampleBuysFromKyberNetwork",
|
||||||
|
"outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"constant": true,
|
"constant": true,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{ "internalType": "address", "name": "registryAddress", "type": "address" },
|
{ "internalType": "address", "name": "registryAddress", "type": "address" },
|
||||||
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
{ "internalType": "address", "name": "takerToken", "type": "address" },
|
||||||
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
{ "internalType": "address", "name": "makerToken", "type": "address" },
|
||||||
{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" }
|
{ "internalType": "uint256[]", "name": "makerTokenAmounts", "type": "uint256[]" },
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "uint256", "name": "targetSlippageBps", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "maxIterations", "type": "uint256" }
|
||||||
|
],
|
||||||
|
"internalType": "struct IERC20BridgeSampler.FakeBuyOptions",
|
||||||
|
"name": "opts",
|
||||||
|
"type": "tuple"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"name": "sampleBuysFromLiquidityProviderRegistry",
|
"name": "sampleBuysFromLiquidityProviderRegistry",
|
||||||
"outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }],
|
"outputs": [{ "internalType": "uint256[]", "name": "takerTokenAmounts", "type": "uint256[]" }],
|
||||||
@ -232,20 +277,41 @@
|
|||||||
},
|
},
|
||||||
"return": "orderFillableTakerAssetAmounts How much taker asset can be filled by each order in `orders`."
|
"return": "orderFillableTakerAssetAmounts How much taker asset can be filled by each order in `orders`."
|
||||||
},
|
},
|
||||||
|
"sampleBuysFromCurve(address,int128,int128,uint256[])": {
|
||||||
|
"details": "Sample buy quotes from Curve.",
|
||||||
|
"params": {
|
||||||
|
"curveAddress": "Address of the Curve contract.",
|
||||||
|
"fromTokenIdx": "Index of the taker token (what to sell).",
|
||||||
|
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||||
|
"toTokenIdx": "Index of the maker token (what to buy)."
|
||||||
|
},
|
||||||
|
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||||
|
},
|
||||||
"sampleBuysFromEth2Dai(address,address,uint256[])": {
|
"sampleBuysFromEth2Dai(address,address,uint256[])": {
|
||||||
"details": "Sample buy quotes from Eth2Dai/Oasis.",
|
"details": "Sample buy quotes from Eth2Dai/Oasis.",
|
||||||
"params": {
|
"params": {
|
||||||
"makerToken": "Address of the maker token (what to buy).",
|
"makerToken": "Address of the maker token (what to buy).",
|
||||||
"takerToken": "Address of the taker token (what to sell).",
|
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||||
"takerTokenAmounts": "Maker token sell amount for each sample."
|
"takerToken": "Address of the taker token (what to sell)."
|
||||||
},
|
},
|
||||||
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||||
},
|
},
|
||||||
"sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])": {
|
"sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,uint256))": {
|
||||||
|
"details": "Sample buy quotes from Kyber.",
|
||||||
|
"params": {
|
||||||
|
"makerToken": "Address of the maker token (what to buy).",
|
||||||
|
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||||
|
"opts": "`FakeBuyOptions` specifying target slippage and max iterations.",
|
||||||
|
"takerToken": "Address of the taker token (what to sell)."
|
||||||
|
},
|
||||||
|
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||||
|
},
|
||||||
|
"sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))": {
|
||||||
"details": "Sample buy quotes from an arbitrary on-chain liquidity provider.",
|
"details": "Sample buy quotes from an arbitrary on-chain liquidity provider.",
|
||||||
"params": {
|
"params": {
|
||||||
"makerToken": "Address of the maker token (what to buy).",
|
"makerToken": "Address of the maker token (what to buy).",
|
||||||
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||||
|
"opts": "`FakeBuyOptions` specifying target slippage and max iterations.",
|
||||||
"registryAddress": "Address of the liquidity provider registry contract.",
|
"registryAddress": "Address of the liquidity provider registry contract.",
|
||||||
"takerToken": "Address of the taker token (what to sell)."
|
"takerToken": "Address of the taker token (what to sell)."
|
||||||
},
|
},
|
||||||
@ -255,7 +321,7 @@
|
|||||||
"details": "Sample buy quotes from Uniswap.",
|
"details": "Sample buy quotes from Uniswap.",
|
||||||
"params": {
|
"params": {
|
||||||
"makerToken": "Address of the maker token (what to buy).",
|
"makerToken": "Address of the maker token (what to buy).",
|
||||||
"makerTokenAmounts": "Maker token sell amount for each sample.",
|
"makerTokenAmounts": "Maker token buy amount for each sample.",
|
||||||
"takerToken": "Address of the taker token (what to sell)."
|
"takerToken": "Address of the taker token (what to sell)."
|
||||||
},
|
},
|
||||||
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
"return": "takerTokenAmounts Taker amounts sold at each maker token amount."
|
||||||
@ -313,7 +379,7 @@
|
|||||||
},
|
},
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"name": "solc",
|
"name": "solc",
|
||||||
"version": "soljson-v0.5.16+commit.9c3226ce.js",
|
"version": "0.5.17+commit.d19bba13",
|
||||||
"settings": {
|
"settings": {
|
||||||
"optimizer": {
|
"optimizer": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
@ -13,6 +13,14 @@
|
|||||||
{
|
{
|
||||||
"note": "Added `Forwarder.marketSellAmountWithEth`",
|
"note": "Added `Forwarder.marketSellAmountWithEth`",
|
||||||
"pr": 2521
|
"pr": 2521
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `ERC20BridgeSampler.sampleBuysFromCurve`",
|
||||||
|
"pr": 2551
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `ERC20BridgeSampler.sampleBuysFromKyberNetwork`",
|
||||||
|
"pr": 2551
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -49,6 +49,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||||
|
devUtilsAddress: string,
|
||||||
): Promise<ERC20BridgeSamplerContract> {
|
): Promise<ERC20BridgeSamplerContract> {
|
||||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||||
schemas.addressSchema,
|
schemas.addressSchema,
|
||||||
@ -73,6 +74,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
logDecodeDependenciesAbiOnly,
|
logDecodeDependenciesAbiOnly,
|
||||||
|
devUtilsAddress,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +84,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
|
||||||
|
devUtilsAddress: string,
|
||||||
): Promise<ERC20BridgeSamplerContract> {
|
): Promise<ERC20BridgeSamplerContract> {
|
||||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||||
schemas.addressSchema,
|
schemas.addressSchema,
|
||||||
@ -112,6 +115,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
logDecodeDependenciesAbiOnly,
|
logDecodeDependenciesAbiOnly,
|
||||||
|
devUtilsAddress,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +125,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
supportedProvider: SupportedProvider,
|
supportedProvider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
logDecodeDependencies: { [contractName: string]: ContractAbi },
|
logDecodeDependencies: { [contractName: string]: ContractAbi },
|
||||||
|
devUtilsAddress: string,
|
||||||
): Promise<ERC20BridgeSamplerContract> {
|
): Promise<ERC20BridgeSamplerContract> {
|
||||||
assert.isHexString('bytecode', bytecode);
|
assert.isHexString('bytecode', bytecode);
|
||||||
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
|
||||||
@ -130,10 +135,14 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
]);
|
]);
|
||||||
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
|
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
|
||||||
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
|
[devUtilsAddress] = BaseContract._formatABIDataItemList(
|
||||||
|
constructorAbi.inputs,
|
||||||
|
[devUtilsAddress],
|
||||||
|
BaseContract._bigNumberToString,
|
||||||
|
);
|
||||||
const iface = new ethers.utils.Interface(abi);
|
const iface = new ethers.utils.Interface(abi);
|
||||||
const deployInfo = iface.deployFunction;
|
const deployInfo = iface.deployFunction;
|
||||||
const txData = deployInfo.encode(bytecode, []);
|
const txData = deployInfo.encode(bytecode, [devUtilsAddress]);
|
||||||
const web3Wrapper = new Web3Wrapper(provider);
|
const web3Wrapper = new Web3Wrapper(provider);
|
||||||
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
const txDataWithDefaults = await BaseContract._applyDefaultsToContractTxDataAsync(
|
||||||
{
|
{
|
||||||
@ -152,7 +161,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
txDefaults,
|
txDefaults,
|
||||||
logDecodeDependencies,
|
logDecodeDependencies,
|
||||||
);
|
);
|
||||||
contractInstance.constructorArgs = [];
|
contractInstance.constructorArgs = [devUtilsAddress];
|
||||||
return contractInstance;
|
return contractInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +170,18 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
*/
|
*/
|
||||||
public static ABI(): ContractAbi {
|
public static ABI(): ContractAbi {
|
||||||
const abi = [
|
const abi = [
|
||||||
|
{
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'devUtilsAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
outputs: [],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'nonpayable',
|
||||||
|
type: 'constructor',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -369,6 +390,37 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
stateMutability: 'view',
|
stateMutability: 'view',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'curveAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fromTokenIdx',
|
||||||
|
type: 'int128',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'toTokenIdx',
|
||||||
|
type: 'int128',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'sampleBuysFromCurve',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'takerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -396,6 +448,47 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
stateMutability: 'view',
|
stateMutability: 'view',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'takerToken',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerToken',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'opts',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'targetSlippageBps',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maxIterations',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'sampleBuysFromKyberNetwork',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'takerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -415,6 +508,20 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
name: 'makerTokenAmounts',
|
name: 'makerTokenAmounts',
|
||||||
type: 'uint256[]',
|
type: 'uint256[]',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'opts',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'targetSlippageBps',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maxIterations',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
name: 'sampleBuysFromLiquidityProviderRegistry',
|
name: 'sampleBuysFromLiquidityProviderRegistry',
|
||||||
outputs: [
|
outputs: [
|
||||||
@ -838,6 +945,48 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sample buy 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 makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
*/
|
||||||
|
public sampleBuysFromCurve(
|
||||||
|
curveAddress: string,
|
||||||
|
fromTokenIdx: BigNumber,
|
||||||
|
toTokenIdx: BigNumber,
|
||||||
|
makerTokenAmounts: BigNumber[],
|
||||||
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
|
const self = (this as any) as ERC20BridgeSamplerContract;
|
||||||
|
assert.isString('curveAddress', curveAddress);
|
||||||
|
assert.isBigNumber('fromTokenIdx', fromTokenIdx);
|
||||||
|
assert.isBigNumber('toTokenIdx', toTokenIdx);
|
||||||
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
|
const functionSignature = 'sampleBuysFromCurve(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,
|
||||||
|
makerTokenAmounts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sample buy quotes from Eth2Dai/Oasis.
|
* Sample buy quotes from Eth2Dai/Oasis.
|
||||||
* @param takerToken Address of the taker token (what to sell).
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
@ -875,12 +1024,55 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sample buy quotes from Kyber.
|
||||||
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
*/
|
||||||
|
public sampleBuysFromKyberNetwork(
|
||||||
|
takerToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
makerTokenAmounts: BigNumber[],
|
||||||
|
opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber },
|
||||||
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
|
const self = (this as any) as ERC20BridgeSamplerContract;
|
||||||
|
assert.isString('takerToken', takerToken);
|
||||||
|
assert.isString('makerToken', makerToken);
|
||||||
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
|
|
||||||
|
const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,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, [
|
||||||
|
takerToken.toLowerCase(),
|
||||||
|
makerToken.toLowerCase(),
|
||||||
|
makerTokenAmounts,
|
||||||
|
opts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sample buy quotes from an arbitrary on-chain liquidity provider.
|
* Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||||
* @param registryAddress Address of the liquidity provider registry contract.
|
* @param registryAddress Address of the liquidity provider registry contract.
|
||||||
* @param takerToken Address of the taker token (what to sell).
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
* @param makerToken Address of the maker token (what to buy).
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
* @param makerTokenAmounts Maker token buy amount for each sample.
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
*/
|
*/
|
||||||
public sampleBuysFromLiquidityProviderRegistry(
|
public sampleBuysFromLiquidityProviderRegistry(
|
||||||
@ -888,13 +1080,16 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
makerTokenAmounts: BigNumber[],
|
makerTokenAmounts: BigNumber[],
|
||||||
|
opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber },
|
||||||
): ContractFunctionObj<BigNumber[]> {
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
const self = (this as any) as ERC20BridgeSamplerContract;
|
const self = (this as any) as ERC20BridgeSamplerContract;
|
||||||
assert.isString('registryAddress', registryAddress);
|
assert.isString('registryAddress', registryAddress);
|
||||||
assert.isString('takerToken', takerToken);
|
assert.isString('takerToken', takerToken);
|
||||||
assert.isString('makerToken', makerToken);
|
assert.isString('makerToken', makerToken);
|
||||||
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
const functionSignature = 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])';
|
|
||||||
|
const functionSignature =
|
||||||
|
'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
||||||
@ -913,6 +1108,7 @@ export class ERC20BridgeSamplerContract extends BaseContract {
|
|||||||
takerToken.toLowerCase(),
|
takerToken.toLowerCase(),
|
||||||
makerToken.toLowerCase(),
|
makerToken.toLowerCase(),
|
||||||
makerTokenAmounts,
|
makerTokenAmounts,
|
||||||
|
opts,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -369,6 +369,37 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
stateMutability: 'view',
|
stateMutability: 'view',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'curveAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fromTokenIdx',
|
||||||
|
type: 'int128',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'toTokenIdx',
|
||||||
|
type: 'int128',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'sampleBuysFromCurve',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'takerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -396,6 +427,47 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
stateMutability: 'view',
|
stateMutability: 'view',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'takerToken',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerToken',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'opts',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'targetSlippageBps',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maxIterations',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'sampleBuysFromKyberNetwork',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'takerTokenAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -415,6 +487,20 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
name: 'makerTokenAmounts',
|
name: 'makerTokenAmounts',
|
||||||
type: 'uint256[]',
|
type: 'uint256[]',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'opts',
|
||||||
|
type: 'tuple',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'targetSlippageBps',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'maxIterations',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
name: 'sampleBuysFromLiquidityProviderRegistry',
|
name: 'sampleBuysFromLiquidityProviderRegistry',
|
||||||
outputs: [
|
outputs: [
|
||||||
@ -835,10 +921,53 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sample buy 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 makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
*/
|
||||||
|
public sampleBuysFromCurve(
|
||||||
|
curveAddress: string,
|
||||||
|
fromTokenIdx: BigNumber,
|
||||||
|
toTokenIdx: BigNumber,
|
||||||
|
makerTokenAmounts: BigNumber[],
|
||||||
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
|
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||||
|
assert.isString('curveAddress', curveAddress);
|
||||||
|
assert.isBigNumber('fromTokenIdx', fromTokenIdx);
|
||||||
|
assert.isBigNumber('toTokenIdx', toTokenIdx);
|
||||||
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
|
const functionSignature = 'sampleBuysFromCurve(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,
|
||||||
|
makerTokenAmounts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sample buy quotes from Eth2Dai/Oasis.
|
* Sample buy quotes from Eth2Dai/Oasis.
|
||||||
* @param takerToken Address of the taker token (what to sell).
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
* @param makerToken Address of the maker token (what to buy).
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
*/
|
*/
|
||||||
public sampleBuysFromEth2Dai(
|
public sampleBuysFromEth2Dai(
|
||||||
@ -872,12 +1001,55 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sample buy quotes from Kyber.
|
||||||
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
*/
|
||||||
|
public sampleBuysFromKyberNetwork(
|
||||||
|
takerToken: string,
|
||||||
|
makerToken: string,
|
||||||
|
makerTokenAmounts: BigNumber[],
|
||||||
|
opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber },
|
||||||
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
|
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||||
|
assert.isString('takerToken', takerToken);
|
||||||
|
assert.isString('makerToken', makerToken);
|
||||||
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
|
|
||||||
|
const functionSignature = 'sampleBuysFromKyberNetwork(address,address,uint256[],(uint256,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, [
|
||||||
|
takerToken.toLowerCase(),
|
||||||
|
makerToken.toLowerCase(),
|
||||||
|
makerTokenAmounts,
|
||||||
|
opts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sample buy quotes from an arbitrary on-chain liquidity provider.
|
* Sample buy quotes from an arbitrary on-chain liquidity provider.
|
||||||
* @param registryAddress Address of the liquidity provider registry contract.
|
* @param registryAddress Address of the liquidity provider registry contract.
|
||||||
* @param takerToken Address of the taker token (what to sell).
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
* @param makerToken Address of the maker token (what to buy).
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
* @param makerTokenAmounts Maker token buy amount for each sample.
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
* @param opts `FakeBuyOptions` specifying target slippage and max iterations.
|
||||||
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
*/
|
*/
|
||||||
public sampleBuysFromLiquidityProviderRegistry(
|
public sampleBuysFromLiquidityProviderRegistry(
|
||||||
@ -885,13 +1057,16 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
makerTokenAmounts: BigNumber[],
|
makerTokenAmounts: BigNumber[],
|
||||||
|
opts: { targetSlippageBps: BigNumber; maxIterations: BigNumber },
|
||||||
): ContractFunctionObj<BigNumber[]> {
|
): ContractFunctionObj<BigNumber[]> {
|
||||||
const self = (this as any) as IERC20BridgeSamplerContract;
|
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||||
assert.isString('registryAddress', registryAddress);
|
assert.isString('registryAddress', registryAddress);
|
||||||
assert.isString('takerToken', takerToken);
|
assert.isString('takerToken', takerToken);
|
||||||
assert.isString('makerToken', makerToken);
|
assert.isString('makerToken', makerToken);
|
||||||
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
const functionSignature = 'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[])';
|
|
||||||
|
const functionSignature =
|
||||||
|
'sampleBuysFromLiquidityProviderRegistry(address,address,address,uint256[],(uint256,uint256))';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
async callAsync(callData: Partial<CallData> = {}, defaultBlock?: BlockParam): Promise<BigNumber[]> {
|
||||||
@ -910,6 +1085,7 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
takerToken.toLowerCase(),
|
takerToken.toLowerCase(),
|
||||||
makerToken.toLowerCase(),
|
makerToken.toLowerCase(),
|
||||||
makerTokenAmounts,
|
makerTokenAmounts,
|
||||||
|
opts,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -918,7 +1094,7 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
* Sample buy quotes from Uniswap.
|
* Sample buy quotes from Uniswap.
|
||||||
* @param takerToken Address of the taker token (what to sell).
|
* @param takerToken Address of the taker token (what to sell).
|
||||||
* @param makerToken Address of the maker token (what to buy).
|
* @param makerToken Address of the maker token (what to buy).
|
||||||
* @param makerTokenAmounts Maker token sell amount for each sample.
|
* @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
* @returns takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
*/
|
*/
|
||||||
public sampleBuysFromUniswap(
|
public sampleBuysFromUniswap(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user