Merge pull request #2427 from 0xProject/feature/erc20-bridge-sampler/query-multiple-buys-sells
ERC20BridgeSampler: Allow for batching multiple buy/sell samples
This commit is contained in:
commit
4e46bf4697
@ -1,4 +1,17 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "1.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add batch functions to query quotes",
|
||||||
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Early exit if a DEX sample fails",
|
||||||
|
"pr": 2427
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -37,9 +37,73 @@ contract ERC20BridgeSampler is
|
|||||||
DeploymentConstants
|
DeploymentConstants
|
||||||
{
|
{
|
||||||
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
bytes4 constant internal ERC20_PROXY_ID = 0xf47261b0; // bytes4(keccak256("ERC20Token(address)"));
|
||||||
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 600e3;
|
uint256 constant internal KYBER_SAMPLE_CALL_GAS = 1500e3;
|
||||||
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
uint256 constant internal UNISWAP_SAMPLE_CALL_GAS = 150e3;
|
||||||
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 250e3;
|
uint256 constant internal ETH2DAI_SAMPLE_CALL_GAS = 1000e3;
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[][] memory orders,
|
||||||
|
bytes[][] memory orderSignatures,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[][] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ordersAndSamples = new OrdersAndSample[](orders.length);
|
||||||
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
|
(
|
||||||
|
uint256[] memory orderFillableAssetAmounts,
|
||||||
|
uint256[][] memory tokenAmountsBySource
|
||||||
|
) = queryOrdersAndSampleSells(orders[i], orderSignatures[i], sources, takerTokenAmounts[i]);
|
||||||
|
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
||||||
|
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[][] memory orders,
|
||||||
|
bytes[][] memory orderSignatures,
|
||||||
|
address[] memory sources,
|
||||||
|
uint256[][] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ordersAndSamples = new OrdersAndSample[](orders.length);
|
||||||
|
for (uint256 i = 0; i != orders.length; i++) {
|
||||||
|
(
|
||||||
|
uint256[] memory orderFillableAssetAmounts,
|
||||||
|
uint256[][] memory tokenAmountsBySource
|
||||||
|
) = queryOrdersAndSampleBuys(orders[i], orderSignatures[i], sources, makerTokenAmounts[i]);
|
||||||
|
ordersAndSamples[i].orderFillableAssetAmounts = orderFillableAssetAmounts;
|
||||||
|
ordersAndSamples[i].tokenAmountsBySource = tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
@ -281,6 +345,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 rate = 0;
|
uint256 rate = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
rate = abi.decode(resultData, (uint256));
|
rate = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] =
|
makerTokenAmounts[i] =
|
||||||
rate *
|
rate *
|
||||||
@ -321,6 +387,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 buyAmount = 0;
|
uint256 buyAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
buyAmount = abi.decode(resultData, (uint256));
|
buyAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
makerTokenAmounts[i] = buyAmount;
|
makerTokenAmounts[i] = buyAmount;
|
||||||
}
|
}
|
||||||
@ -356,6 +424,8 @@ contract ERC20BridgeSampler is
|
|||||||
uint256 sellAmount = 0;
|
uint256 sellAmount = 0;
|
||||||
if (didSucceed) {
|
if (didSucceed) {
|
||||||
sellAmount = abi.decode(resultData, (uint256));
|
sellAmount = abi.decode(resultData, (uint256));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
takerTokenAmounts[i] = sellAmount;
|
takerTokenAmounts[i] = sellAmount;
|
||||||
}
|
}
|
||||||
@ -384,26 +454,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethBought = _callUniswapExchangePriceFunction(
|
uint256 ethBought;
|
||||||
|
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthInputPrice.selector,
|
takerTokenExchange.getTokenToEthInputPrice.selector,
|
||||||
takerTokenAmounts[i]
|
takerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethBought != 0) {
|
if (ethBought != 0) {
|
||||||
makerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenInputPrice.selector,
|
makerTokenExchange.getEthToTokenInputPrice.selector,
|
||||||
ethBought
|
ethBought
|
||||||
@ -412,6 +484,9 @@ contract ERC20BridgeSampler is
|
|||||||
makerTokenAmounts[i] = 0;
|
makerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,26 +513,28 @@ contract ERC20BridgeSampler is
|
|||||||
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
|
||||||
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
IUniswapExchangeQuotes(0) : _getUniswapExchange(makerToken);
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
bool didSucceed = true;
|
||||||
if (makerToken == _getWethAddress()) {
|
if (makerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else if (takerToken == _getWethAddress()) {
|
} else if (takerToken == _getWethAddress()) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
uint256 ethSold = _callUniswapExchangePriceFunction(
|
uint256 ethSold;
|
||||||
|
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(makerTokenExchange),
|
address(makerTokenExchange),
|
||||||
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
makerTokenExchange.getEthToTokenOutputPrice.selector,
|
||||||
makerTokenAmounts[i]
|
makerTokenAmounts[i]
|
||||||
);
|
);
|
||||||
if (ethSold != 0) {
|
if (ethSold != 0) {
|
||||||
takerTokenAmounts[i] = _callUniswapExchangePriceFunction(
|
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
|
||||||
address(takerTokenExchange),
|
address(takerTokenExchange),
|
||||||
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
takerTokenExchange.getTokenToEthOutputPrice.selector,
|
||||||
ethSold
|
ethSold
|
||||||
@ -466,6 +543,9 @@ contract ERC20BridgeSampler is
|
|||||||
takerTokenAmounts[i] = 0;
|
takerTokenAmounts[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!didSucceed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,12 +573,13 @@ contract ERC20BridgeSampler is
|
|||||||
)
|
)
|
||||||
private
|
private
|
||||||
view
|
view
|
||||||
returns (uint256 outputAmount)
|
returns (uint256 outputAmount, bool didSucceed)
|
||||||
{
|
{
|
||||||
if (uniswapExchangeAddress == address(0)) {
|
if (uniswapExchangeAddress == address(0)) {
|
||||||
return 0;
|
return (outputAmount, didSucceed);
|
||||||
}
|
}
|
||||||
(bool didSucceed, bytes memory resultData) =
|
bytes memory resultData;
|
||||||
|
(didSucceed, resultData) =
|
||||||
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
uniswapExchangeAddress.staticcall.gas(UNISWAP_SAMPLE_CALL_GAS)(
|
||||||
abi.encodeWithSelector(
|
abi.encodeWithSelector(
|
||||||
functionSelector,
|
functionSelector,
|
||||||
|
@ -23,6 +23,52 @@ import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
|||||||
|
|
||||||
|
|
||||||
interface IERC20BridgeSampler {
|
interface IERC20BridgeSampler {
|
||||||
|
struct OrdersAndSample {
|
||||||
|
uint256[] orderFillableAssetAmounts;
|
||||||
|
uint256[][] tokenAmountsBySource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample sell quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param takerTokenAmounts Batches of Taker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Maker amounts bought for each source at
|
||||||
|
/// each taker token amount. First indexed by source index, then sample
|
||||||
|
/// index.
|
||||||
|
function queryBatchOrdersAndSampleSells(
|
||||||
|
LibOrder.Order[][] calldata orders,
|
||||||
|
bytes[][] calldata orderSignatures,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[][] calldata takerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Query batches of native orders and sample buy quotes on multiple DEXes at once.
|
||||||
|
/// @param orders Batches of Native orders to query.
|
||||||
|
/// @param orderSignatures Batches of Signatures for each respective order in `orders`.
|
||||||
|
/// @param sources Address of each DEX. Passing in an unsupported DEX will throw.
|
||||||
|
/// @param makerTokenAmounts Batches of Maker token sell amount for each sample.
|
||||||
|
/// @return ordersAndSamples How much taker asset can be filled
|
||||||
|
/// by each order in `orders`. Taker amounts sold for each source at
|
||||||
|
/// each maker token amount. First indexed by source index, then sample
|
||||||
|
/// index
|
||||||
|
function queryBatchOrdersAndSampleBuys(
|
||||||
|
LibOrder.Order[][] calldata orders,
|
||||||
|
bytes[][] calldata orderSignatures,
|
||||||
|
address[] calldata sources,
|
||||||
|
uint256[][] calldata makerTokenAmounts
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
OrdersAndSample[] memory ordersAndSamples
|
||||||
|
);
|
||||||
|
|
||||||
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
/// @dev Query native orders and sample sell quotes on multiple DEXes at once.
|
||||||
/// @param orders Native orders to query.
|
/// @param orders Native orders to query.
|
||||||
|
@ -338,7 +338,11 @@ contract TestERC20BridgeSampler is
|
|||||||
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
bytes32 orderHash = keccak256(abi.encode(order.salt));
|
||||||
// Everything else is derived from the hash.
|
// Everything else is derived from the hash.
|
||||||
orderInfo.orderHash = orderHash;
|
orderInfo.orderHash = orderHash;
|
||||||
orderInfo.orderStatus = LibOrder.OrderStatus(uint256(orderHash) % MAX_ORDER_STATUS);
|
if (uint256(orderHash) % 100 > 90) {
|
||||||
|
orderInfo.orderStatus = LibOrder.OrderStatus.FULLY_FILLED;
|
||||||
|
} else {
|
||||||
|
orderInfo.orderStatus = LibOrder.OrderStatus.FILLABLE;
|
||||||
|
}
|
||||||
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
orderInfo.orderTakerAssetFilledAmount = uint256(orderHash) % order.takerAssetAmount;
|
||||||
fillableTakerAssetAmount =
|
fillableTakerAssetAmount =
|
||||||
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
order.takerAssetAmount - orderInfo.orderTakerAssetFilledAmount;
|
||||||
|
@ -195,7 +195,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
|
||||||
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
const hash = getPackedHash(hexUtils.toHex(order.salt, 32));
|
||||||
const orderStatus = new BigNumber(hash).mod(255).toNumber();
|
const orderStatus = new BigNumber(hash).mod(100).toNumber() > 90 ? 5 : 3;
|
||||||
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
const isValidSignature = !!new BigNumber(hash).mod(2).toNumber();
|
||||||
if (orderStatus !== 3 || !isValidSignature) {
|
if (orderStatus !== 3 || !isValidSignature) {
|
||||||
return constants.ZERO_AMOUNT;
|
return constants.ZERO_AMOUNT;
|
||||||
@ -208,7 +208,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
return order.makerAssetAmount
|
return order.makerAssetAmount
|
||||||
.times(takerAmount)
|
.times(takerAmount)
|
||||||
.div(order.takerAssetAmount)
|
.div(order.takerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getERC20AssetData(tokenAddress: string): string {
|
function getERC20AssetData(tokenAddress: string): string {
|
||||||
@ -255,7 +255,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableTakerAssetAmounts()', () => {
|
describe('getOrderFillableTakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@ -269,7 +269,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@ -277,7 +277,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableTakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@ -293,7 +293,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
describe('getOrderFillableMakerAssetAmounts()', () => {
|
describe('getOrderFillableMakerAssetAmounts()', () => {
|
||||||
it('returns the expected amount for each order', async () => {
|
it('returns the expected amount for each order', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq(expected);
|
expect(actual).to.deep.eq(expected);
|
||||||
@ -307,7 +307,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero maker asset amount', async () => {
|
it('returns zero for an order with zero maker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@ -315,7 +315,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
it('returns zero for an order with zero taker asset amount', async () => {
|
it('returns zero for an order with zero taker asset amount', async () => {
|
||||||
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, 1);
|
||||||
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
orders[0].takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const signatures: string[] = _.times(orders.length, hexUtils.random);
|
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
|
||||||
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
const actual = await testContract.getOrderFillableMakerAssetAmounts(orders, signatures).callAsync();
|
||||||
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
expect(actual).to.deep.eq([constants.ZERO_AMOUNT]);
|
||||||
});
|
});
|
||||||
@ -330,7 +330,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleSells()', () => {
|
describe('queryOrdersAndSampleSells()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
@ -436,7 +436,7 @@ blockchainTests('erc20-bridge-sampler', env => {
|
|||||||
|
|
||||||
describe('queryOrdersAndSampleBuys()', () => {
|
describe('queryOrdersAndSampleBuys()', () => {
|
||||||
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
const ORDERS = createOrders(MAKER_TOKEN, TAKER_TOKEN);
|
||||||
const SIGNATURES: string[] = _.times(ORDERS.length, hexUtils.random);
|
const SIGNATURES: string[] = _.times(ORDERS.length, i => hexUtils.random());
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
await testContract.createTokenExchanges([MAKER_TOKEN, TAKER_TOKEN]).awaitTransactionSuccessAsync();
|
||||||
|
@ -277,10 +277,16 @@ for (const abiFileName of abiFileNames) {
|
|||||||
// use command-line tool black to reformat, if its available
|
// use command-line tool black to reformat, if its available
|
||||||
try {
|
try {
|
||||||
execSync(`black --line-length 79 ${outFilePath}`);
|
execSync(`black --line-length 79 ${outFilePath}`);
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
const BLACK_RC_CANNOT_PARSE = 123; // empirical black exit code
|
||||||
|
if (e.status === BLACK_RC_CANNOT_PARSE) {
|
||||||
logUtils.warn(
|
logUtils.warn(
|
||||||
'Failed to reformat generated Python with black. Do you have it installed? Proceeding anyways...',
|
'Failed to reformat generated Python with black. Exception thrown by execSync("black ...") follows.',
|
||||||
);
|
);
|
||||||
|
throw e;
|
||||||
|
} else {
|
||||||
|
logUtils.warn('Failed to invoke black. Do you have it installed? Proceeding anyways...');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ export const utils = {
|
|||||||
return tuple.internalType
|
return tuple.internalType
|
||||||
.replace('struct ', '')
|
.replace('struct ', '')
|
||||||
.replace('.', '')
|
.replace('.', '')
|
||||||
.replace('[]', '');
|
.replace(/\[\]/g, '');
|
||||||
} else {
|
} else {
|
||||||
const tupleComponents = tuple.components;
|
const tupleComponents = tuple.components;
|
||||||
const lengthOfHashSuffix = 8;
|
const lengthOfHashSuffix = 8;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -138,6 +138,8 @@ contract AbiGenDummy
|
|||||||
}
|
}
|
||||||
|
|
||||||
function methodReturningArrayOfStructs() public pure returns(Struct[] memory) {}
|
function methodReturningArrayOfStructs() public pure returns(Struct[] memory) {}
|
||||||
|
function methodAcceptingArrayOfStructs(Struct[] memory) public pure {}
|
||||||
|
function methodAcceptingArrayOfArrayOfStructs(Struct[][] memory) public pure {}
|
||||||
|
|
||||||
struct NestedStruct {
|
struct NestedStruct {
|
||||||
Struct innerStruct;
|
Struct innerStruct;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -90,7 +90,7 @@ class LibDummy:
|
|||||||
try:
|
try:
|
||||||
for middleware in MIDDLEWARE:
|
for middleware in MIDDLEWARE:
|
||||||
web3.middleware_onion.inject(
|
web3.middleware_onion.inject(
|
||||||
middleware["function"], layer=middleware["layer"]
|
middleware["function"], layer=middleware["layer"],
|
||||||
)
|
)
|
||||||
except ValueError as value_error:
|
except ValueError as value_error:
|
||||||
if value_error.args == (
|
if value_error.args == (
|
||||||
|
File diff suppressed because one or more lines are too long
@ -9,6 +9,18 @@
|
|||||||
{
|
{
|
||||||
"note": "Remove `getSmartContractParamsOrThrow()` from `SwapQuoteConsumer`s.",
|
"note": "Remove `getSmartContractParamsOrThrow()` from `SwapQuoteConsumer`s.",
|
||||||
"pr": 2432
|
"pr": 2432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Added `getBatchMarketBuySwapQuoteForAssetDataAsync` on `SwapQuoter`",
|
||||||
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add exponential sampling distribution and `sampleDistributionBase` option to `SwapQuoter`",
|
||||||
|
"pr": 2427
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Compute more accurate best quote price",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -54,6 +54,11 @@ export {
|
|||||||
SwapQuoteConsumerError,
|
SwapQuoteConsumerError,
|
||||||
SignedOrderWithFillableAmounts,
|
SignedOrderWithFillableAmounts,
|
||||||
} from './types';
|
} from './types';
|
||||||
export { ERC20BridgeSource } from './utils/market_operation_utils/types';
|
export {
|
||||||
|
ERC20BridgeSource,
|
||||||
|
CollapsedFill,
|
||||||
|
NativeCollapsedFill,
|
||||||
|
OptimizedMarketOrder,
|
||||||
|
} from './utils/market_operation_utils/types';
|
||||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
|
@ -226,6 +226,58 @@ export class SwapQuoter {
|
|||||||
options,
|
options,
|
||||||
)) as MarketBuySwapQuote;
|
)) as MarketBuySwapQuote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getBatchMarketBuySwapQuoteForAssetDataAsync(
|
||||||
|
makerAssetDatas: string[],
|
||||||
|
takerAssetData: string,
|
||||||
|
makerAssetBuyAmount: BigNumber[],
|
||||||
|
options: Partial<SwapQuoteRequestOpts> = {},
|
||||||
|
): Promise<Array<MarketBuySwapQuote | undefined>> {
|
||||||
|
makerAssetBuyAmount.map((a, i) => assert.isBigNumber(`makerAssetBuyAmount[${i}]`, a));
|
||||||
|
let gasPrice: BigNumber;
|
||||||
|
const { slippagePercentage, ...calculateSwapQuoteOpts } = _.merge(
|
||||||
|
{},
|
||||||
|
constants.DEFAULT_SWAP_QUOTE_REQUEST_OPTS,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
if (!!options.gasPrice) {
|
||||||
|
gasPrice = options.gasPrice;
|
||||||
|
assert.isBigNumber('gasPrice', gasPrice);
|
||||||
|
} else {
|
||||||
|
gasPrice = await this._protocolFeeUtils.getGasPriceEstimationOrThrowAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiOrders = await this.orderbook.getBatchOrdersAsync(makerAssetDatas, [takerAssetData]);
|
||||||
|
const allOrders = apiOrders.map(orders => orders.map(o => o.order));
|
||||||
|
const allPrunedOrders = allOrders.map((orders, i) => {
|
||||||
|
if (orders.length === 0) {
|
||||||
|
return [
|
||||||
|
dummyOrderUtils.createDummyOrderForSampler(
|
||||||
|
makerAssetDatas[i],
|
||||||
|
takerAssetData,
|
||||||
|
this._contractAddresses.uniswapBridge,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return sortingUtils.sortOrders(
|
||||||
|
orderPrunerUtils.pruneForUsableSignedOrders(
|
||||||
|
orders,
|
||||||
|
this.permittedOrderFeeTypes,
|
||||||
|
this.expiryBufferMs,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const swapQuotes = await this._swapQuoteCalculator.calculateBatchMarketBuySwapQuoteAsync(
|
||||||
|
allPrunedOrders,
|
||||||
|
makerAssetBuyAmount,
|
||||||
|
slippagePercentage,
|
||||||
|
gasPrice,
|
||||||
|
calculateSwapQuoteOpts,
|
||||||
|
);
|
||||||
|
return swapQuotes;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address.
|
* Get a `SwapQuote` containing all information relevant to fulfilling a swap between a desired ERC20 token address and ERC20 owned by a provided address.
|
||||||
* You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption.
|
* You can then pass the `SwapQuote` to a `SwapQuoteConsumer` to execute a buy, or process SwapQuote for on-chain consumption.
|
||||||
|
@ -24,12 +24,14 @@ export const SELL_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Da
|
|||||||
export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
export const BUY_SOURCES = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
||||||
|
|
||||||
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
export const DEFAULT_GET_MARKET_ORDERS_OPTS: GetMarketOrdersOpts = {
|
||||||
runLimit: 4096,
|
// tslint:disable-next-line: custom-no-magic-numbers
|
||||||
|
runLimit: 2 ** 15,
|
||||||
excludedSources: [],
|
excludedSources: [],
|
||||||
bridgeSlippage: 0.0005,
|
bridgeSlippage: 0.0005,
|
||||||
dustFractionThreshold: 0.01,
|
dustFractionThreshold: 0.0025,
|
||||||
numSamples: 10,
|
numSamples: 13,
|
||||||
noConflicts: true,
|
noConflicts: true,
|
||||||
|
sampleDistributionBase: 1.05,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
@ -40,5 +42,5 @@ export const constants = {
|
|||||||
DEFAULT_GET_MARKET_ORDERS_OPTS,
|
DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||||
ERC20_PROXY_ID: '0xf47261b0',
|
ERC20_PROXY_ID: '0xf47261b0',
|
||||||
WALLET_SIGNATURE: '0x04',
|
WALLET_SIGNATURE: '0x04',
|
||||||
SAMPLER_CONTRACT_GAS_LIMIT: 10e6,
|
SAMPLER_CONTRACT_GAS_LIMIT: 16e6,
|
||||||
};
|
};
|
||||||
|
@ -3,11 +3,17 @@ import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
|||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { constants } from '../../constants';
|
import { constants } from '../../constants';
|
||||||
import { SignedOrderWithFillableAmounts } from '../../types';
|
|
||||||
import { sortingUtils } from '../../utils/sorting_utils';
|
import { sortingUtils } from '../../utils/sorting_utils';
|
||||||
|
|
||||||
import { constants as marketOperationUtilConstants } from './constants';
|
import { constants as marketOperationUtilConstants } from './constants';
|
||||||
import { AggregationError, ERC20BridgeSource, Fill, FillData, NativeFillData, OrderDomain } from './types';
|
import {
|
||||||
|
AggregationError,
|
||||||
|
CollapsedFill,
|
||||||
|
ERC20BridgeSource,
|
||||||
|
NativeCollapsedFill,
|
||||||
|
OptimizedMarketOrder,
|
||||||
|
OrderDomain,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
const { NULL_BYTES, NULL_ADDRESS, ZERO_AMOUNT } = constants;
|
const { NULL_BYTES, NULL_ADDRESS, ZERO_AMOUNT } = constants;
|
||||||
const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstants;
|
const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstants;
|
||||||
@ -24,23 +30,21 @@ export class CreateOrderUtils {
|
|||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
inputToken: string,
|
inputToken: string,
|
||||||
outputToken: string,
|
outputToken: string,
|
||||||
path: Fill[],
|
path: CollapsedFill[],
|
||||||
bridgeSlippage: number,
|
bridgeSlippage: number,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): OptimizedMarketOrder[] {
|
||||||
const orders: SignedOrderWithFillableAmounts[] = [];
|
const orders: OptimizedMarketOrder[] = [];
|
||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
const source = (fill.fillData as FillData).source;
|
if (fill.source === ERC20BridgeSource.Native) {
|
||||||
if (source === ERC20BridgeSource.Native) {
|
orders.push(createNativeOrder(fill));
|
||||||
orders.push((fill.fillData as NativeFillData).order);
|
|
||||||
} else {
|
} else {
|
||||||
orders.push(
|
orders.push(
|
||||||
createBridgeOrder(
|
createBridgeOrder(
|
||||||
orderDomain,
|
orderDomain,
|
||||||
this._getBridgeAddressFromSource(source),
|
fill,
|
||||||
|
this._getBridgeAddressFromSource(fill.source),
|
||||||
outputToken,
|
outputToken,
|
||||||
inputToken,
|
inputToken,
|
||||||
fill.output,
|
|
||||||
fill.input,
|
|
||||||
bridgeSlippage,
|
bridgeSlippage,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -54,23 +58,21 @@ export class CreateOrderUtils {
|
|||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
inputToken: string,
|
inputToken: string,
|
||||||
outputToken: string,
|
outputToken: string,
|
||||||
path: Fill[],
|
path: CollapsedFill[],
|
||||||
bridgeSlippage: number,
|
bridgeSlippage: number,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): OptimizedMarketOrder[] {
|
||||||
const orders: SignedOrderWithFillableAmounts[] = [];
|
const orders: OptimizedMarketOrder[] = [];
|
||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
const source = (fill.fillData as FillData).source;
|
if (fill.source === ERC20BridgeSource.Native) {
|
||||||
if (source === ERC20BridgeSource.Native) {
|
orders.push(createNativeOrder(fill));
|
||||||
orders.push((fill.fillData as NativeFillData).order);
|
|
||||||
} else {
|
} else {
|
||||||
orders.push(
|
orders.push(
|
||||||
createBridgeOrder(
|
createBridgeOrder(
|
||||||
orderDomain,
|
orderDomain,
|
||||||
this._getBridgeAddressFromSource(source),
|
fill,
|
||||||
|
this._getBridgeAddressFromSource(fill.source),
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
fill.input,
|
|
||||||
fill.output,
|
|
||||||
bridgeSlippage,
|
bridgeSlippage,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
@ -97,14 +99,13 @@ export class CreateOrderUtils {
|
|||||||
|
|
||||||
function createBridgeOrder(
|
function createBridgeOrder(
|
||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
|
fill: CollapsedFill,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
makerAssetAmount: BigNumber,
|
|
||||||
takerAssetAmount: BigNumber,
|
|
||||||
slippage: number,
|
slippage: number,
|
||||||
isBuy: boolean = false,
|
isBuy: boolean = false,
|
||||||
): SignedOrderWithFillableAmounts {
|
): OptimizedMarketOrder {
|
||||||
return {
|
return {
|
||||||
makerAddress: bridgeAddress,
|
makerAddress: bridgeAddress,
|
||||||
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
|
makerAssetData: assetDataUtils.encodeERC20BridgeAssetData(
|
||||||
@ -113,7 +114,7 @@ function createBridgeOrder(
|
|||||||
createBridgeData(takerToken),
|
createBridgeData(takerToken),
|
||||||
),
|
),
|
||||||
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken),
|
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken),
|
||||||
...createCommonOrderFields(orderDomain, makerAssetAmount, takerAssetAmount, slippage, isBuy),
|
...createCommonOrderFields(orderDomain, fill, slippage, isBuy),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,24 +124,24 @@ function createBridgeData(tokenAddress: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommonOrderFields = Pick<
|
type CommonOrderFields = Pick<
|
||||||
SignedOrderWithFillableAmounts,
|
OptimizedMarketOrder,
|
||||||
Exclude<keyof SignedOrderWithFillableAmounts, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
|
Exclude<keyof OptimizedMarketOrder, 'makerAddress' | 'makerAssetData' | 'takerAssetData'>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
function createCommonOrderFields(
|
function createCommonOrderFields(
|
||||||
orderDomain: OrderDomain,
|
orderDomain: OrderDomain,
|
||||||
makerAssetAmount: BigNumber,
|
fill: CollapsedFill,
|
||||||
takerAssetAmount: BigNumber,
|
|
||||||
slippage: number,
|
slippage: number,
|
||||||
isBuy: boolean = false,
|
isBuy: boolean = false,
|
||||||
): CommonOrderFields {
|
): CommonOrderFields {
|
||||||
const makerAssetAmountAdjustedWithSlippage = isBuy
|
const makerAssetAmountAdjustedWithSlippage = isBuy
|
||||||
? makerAssetAmount
|
? fill.totalMakerAssetAmount
|
||||||
: makerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
|
: fill.totalMakerAssetAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN);
|
||||||
const takerAssetAmountAdjustedWithSlippage = isBuy
|
const takerAssetAmountAdjustedWithSlippage = isBuy
|
||||||
? takerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
|
? fill.totalTakerAssetAmount.times(slippage + 1).integerValue(BigNumber.ROUND_UP)
|
||||||
: takerAssetAmount;
|
: fill.totalTakerAssetAmount;
|
||||||
return {
|
return {
|
||||||
|
fill,
|
||||||
takerAddress: NULL_ADDRESS,
|
takerAddress: NULL_ADDRESS,
|
||||||
senderAddress: NULL_ADDRESS,
|
senderAddress: NULL_ADDRESS,
|
||||||
feeRecipientAddress: NULL_ADDRESS,
|
feeRecipientAddress: NULL_ADDRESS,
|
||||||
@ -159,3 +160,15 @@ function createCommonOrderFields(
|
|||||||
...orderDomain,
|
...orderDomain,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createNativeOrder(fill: CollapsedFill): OptimizedMarketOrder {
|
||||||
|
return {
|
||||||
|
fill: {
|
||||||
|
source: fill.source,
|
||||||
|
totalMakerAssetAmount: fill.totalMakerAssetAmount,
|
||||||
|
totalTakerAssetAmount: fill.totalTakerAssetAmount,
|
||||||
|
subFills: fill.subFills,
|
||||||
|
},
|
||||||
|
...(fill as NativeCollapsedFill).nativeOrder,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -14,12 +14,16 @@ import { comparePathOutputs, FillsOptimizer, getPathOutput } from './fill_optimi
|
|||||||
import { DexOrderSampler } from './sampler';
|
import { DexOrderSampler } from './sampler';
|
||||||
import {
|
import {
|
||||||
AggregationError,
|
AggregationError,
|
||||||
|
CollapsedFill,
|
||||||
DexSample,
|
DexSample,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
Fill,
|
Fill,
|
||||||
FillData,
|
FillData,
|
||||||
FillFlags,
|
FillFlags,
|
||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
|
NativeCollapsedFill,
|
||||||
|
NativeFillData,
|
||||||
|
OptimizedMarketOrder,
|
||||||
OrderDomain,
|
OrderDomain,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
@ -53,7 +57,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
takerAmount: BigNumber,
|
takerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<SignedOrderWithFillableAmounts[]> {
|
): Promise<OptimizedMarketOrder[]> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@ -63,7 +67,7 @@ export class MarketOperationUtils {
|
|||||||
};
|
};
|
||||||
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketSellAsync(
|
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketSellAsync(
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples),
|
DexOrderSampler.getSampleAmounts(takerAmount, _opts.numSamples, _opts.sampleDistributionBase),
|
||||||
difference(SELL_SOURCES, _opts.excludedSources),
|
difference(SELL_SOURCES, _opts.excludedSources),
|
||||||
);
|
);
|
||||||
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
||||||
@ -77,8 +81,7 @@ export class MarketOperationUtils {
|
|||||||
takerAmount,
|
takerAmount,
|
||||||
_opts.dustFractionThreshold,
|
_opts.dustFractionThreshold,
|
||||||
);
|
);
|
||||||
const clippedNativePath = clipPathToInput(prunedNativePath, takerAmount);
|
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath), takerAmount);
|
||||||
|
|
||||||
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
|
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
|
||||||
const allPaths = [...dexPaths];
|
const allPaths = [...dexPaths];
|
||||||
const allFills = flattenDexPaths(dexPaths);
|
const allFills = flattenDexPaths(dexPaths);
|
||||||
@ -106,7 +109,7 @@ export class MarketOperationUtils {
|
|||||||
this._orderDomain,
|
this._orderDomain,
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
simplifyPath(optimalPath),
|
collapsePath(optimalPath, false),
|
||||||
_opts.bridgeSlippage,
|
_opts.bridgeSlippage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -123,7 +126,7 @@ export class MarketOperationUtils {
|
|||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
makerAmount: BigNumber,
|
makerAmount: BigNumber,
|
||||||
opts?: Partial<GetMarketOrdersOpts>,
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
): Promise<SignedOrderWithFillableAmounts[]> {
|
): Promise<OptimizedMarketOrder[]> {
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
throw new Error(AggregationError.EmptyOrders);
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
}
|
}
|
||||||
@ -134,29 +137,86 @@ export class MarketOperationUtils {
|
|||||||
|
|
||||||
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketBuyAsync(
|
const [fillableAmounts, dexQuotes] = await this._dexSampler.getFillableAmountsAndSampleMarketBuyAsync(
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples),
|
DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples, _opts.sampleDistributionBase),
|
||||||
difference(BUY_SOURCES, _opts.excludedSources),
|
difference(BUY_SOURCES, _opts.excludedSources),
|
||||||
);
|
);
|
||||||
|
const signedOrderWithFillableAmounts = this._createBuyOrdersPathFromSamplerResultIfExists(
|
||||||
|
nativeOrders,
|
||||||
|
makerAmount,
|
||||||
|
fillableAmounts,
|
||||||
|
dexQuotes,
|
||||||
|
_opts,
|
||||||
|
);
|
||||||
|
if (!signedOrderWithFillableAmounts) {
|
||||||
|
throw new Error(AggregationError.NoOptimalPath);
|
||||||
|
}
|
||||||
|
return signedOrderWithFillableAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gets the orders required for a batch of market buy operations by (potentially) merging native orders with
|
||||||
|
* generated bridge orders.
|
||||||
|
* @param batchNativeOrders Batch of Native orders.
|
||||||
|
* @param makerAmounts Array amount of maker asset to buy for each batch.
|
||||||
|
* @param opts Options object.
|
||||||
|
* @return orders.
|
||||||
|
*/
|
||||||
|
public async getBatchMarketBuyOrdersAsync(
|
||||||
|
batchNativeOrders: SignedOrder[][],
|
||||||
|
makerAmounts: BigNumber[],
|
||||||
|
opts?: Partial<GetMarketOrdersOpts>,
|
||||||
|
): Promise<Array<OptimizedMarketOrder[] | undefined>> {
|
||||||
|
if (batchNativeOrders.length === 0) {
|
||||||
|
throw new Error(AggregationError.EmptyOrders);
|
||||||
|
}
|
||||||
|
const _opts = {
|
||||||
|
...DEFAULT_GET_MARKET_ORDERS_OPTS,
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
|
||||||
|
const batchSampleResults = await this._dexSampler.getBatchFillableAmountsAndSampleMarketBuyAsync(
|
||||||
|
batchNativeOrders,
|
||||||
|
makerAmounts.map(makerAmount => DexOrderSampler.getSampleAmounts(makerAmount, _opts.numSamples)),
|
||||||
|
difference(BUY_SOURCES, _opts.excludedSources),
|
||||||
|
);
|
||||||
|
return batchSampleResults.map(([fillableAmounts, dexQuotes], i) =>
|
||||||
|
this._createBuyOrdersPathFromSamplerResultIfExists(
|
||||||
|
batchNativeOrders[i],
|
||||||
|
makerAmounts[i],
|
||||||
|
fillableAmounts,
|
||||||
|
dexQuotes,
|
||||||
|
_opts,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createBuyOrdersPathFromSamplerResultIfExists(
|
||||||
|
nativeOrders: SignedOrder[],
|
||||||
|
makerAmount: BigNumber,
|
||||||
|
nativeOrderFillableAmounts: BigNumber[],
|
||||||
|
dexQuotes: DexSample[][],
|
||||||
|
opts: GetMarketOrdersOpts,
|
||||||
|
): OptimizedMarketOrder[] | undefined {
|
||||||
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
const nativeOrdersWithFillableAmounts = createSignedOrdersWithFillableAmounts(
|
||||||
nativeOrders,
|
nativeOrders,
|
||||||
fillableAmounts,
|
nativeOrderFillableAmounts,
|
||||||
MarketOperation.Buy,
|
MarketOperation.Buy,
|
||||||
);
|
);
|
||||||
const prunedNativePath = pruneDustFillsFromNativePath(
|
const prunedNativePath = pruneDustFillsFromNativePath(
|
||||||
createBuyPathFromNativeOrders(nativeOrdersWithFillableAmounts),
|
createBuyPathFromNativeOrders(nativeOrdersWithFillableAmounts),
|
||||||
makerAmount,
|
makerAmount,
|
||||||
_opts.dustFractionThreshold,
|
opts.dustFractionThreshold,
|
||||||
);
|
);
|
||||||
const clippedNativePath = clipPathToInput(prunedNativePath, makerAmount);
|
const clippedNativePath = clipPathToInput(sortFillsByPrice(prunedNativePath).reverse(), makerAmount);
|
||||||
const dexPaths = createPathsFromDexQuotes(dexQuotes, _opts.noConflicts);
|
const dexPaths = createPathsFromDexQuotes(dexQuotes, opts.noConflicts);
|
||||||
const allPaths = [...dexPaths];
|
const allPaths = [...dexPaths];
|
||||||
const allFills = flattenDexPaths(dexPaths);
|
const allFills = flattenDexPaths(dexPaths);
|
||||||
// If native orders are allowed, splice them in.
|
// If native orders are allowed, splice them in.
|
||||||
if (!_opts.excludedSources.includes(ERC20BridgeSource.Native)) {
|
if (!opts.excludedSources.includes(ERC20BridgeSource.Native)) {
|
||||||
allPaths.splice(0, 0, clippedNativePath);
|
allPaths.splice(0, 0, clippedNativePath);
|
||||||
allFills.splice(0, 0, ...clippedNativePath);
|
allFills.splice(0, 0, ...clippedNativePath);
|
||||||
}
|
}
|
||||||
const optimizer = new FillsOptimizer(_opts.runLimit, true);
|
const optimizer = new FillsOptimizer(opts.runLimit, true);
|
||||||
const upperBoundPath = pickBestUpperBoundPath(allPaths, makerAmount, true);
|
const upperBoundPath = pickBestUpperBoundPath(allPaths, makerAmount, true);
|
||||||
const optimalPath = optimizer.optimize(
|
const optimalPath = optimizer.optimize(
|
||||||
// Sorting the orders by price effectively causes the optimizer to walk
|
// Sorting the orders by price effectively causes the optimizer to walk
|
||||||
@ -167,15 +227,15 @@ export class MarketOperationUtils {
|
|||||||
upperBoundPath,
|
upperBoundPath,
|
||||||
);
|
);
|
||||||
if (!optimalPath) {
|
if (!optimalPath) {
|
||||||
throw new Error(AggregationError.NoOptimalPath);
|
return undefined;
|
||||||
}
|
}
|
||||||
const [inputToken, outputToken] = getOrderTokens(nativeOrders[0]);
|
const [inputToken, outputToken] = getOrderTokens(nativeOrders[0]);
|
||||||
return this._createOrderUtils.createBuyOrdersFromPath(
|
return this._createOrderUtils.createBuyOrdersFromPath(
|
||||||
this._orderDomain,
|
this._orderDomain,
|
||||||
inputToken,
|
inputToken,
|
||||||
outputToken,
|
outputToken,
|
||||||
simplifyPath(optimalPath),
|
collapsePath(optimalPath, true),
|
||||||
_opts.bridgeSlippage,
|
opts.bridgeSlippage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,8 +246,7 @@ function createSignedOrdersWithFillableAmounts(
|
|||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
): SignedOrderWithFillableAmounts[] {
|
): SignedOrderWithFillableAmounts[] {
|
||||||
return signedOrders
|
return signedOrders
|
||||||
.map(
|
.map((order: SignedOrder, i: number) => {
|
||||||
(order: SignedOrder, i: number): SignedOrderWithFillableAmounts => {
|
|
||||||
const fillableAmount = fillableAmounts[i];
|
const fillableAmount = fillableAmounts[i];
|
||||||
const fillableMakerAssetAmount =
|
const fillableMakerAssetAmount =
|
||||||
operation === MarketOperation.Buy
|
operation === MarketOperation.Buy
|
||||||
@ -204,8 +263,7 @@ function createSignedOrdersWithFillableAmounts(
|
|||||||
fillableTakerFeeAmount,
|
fillableTakerFeeAmount,
|
||||||
...order,
|
...order,
|
||||||
};
|
};
|
||||||
},
|
})
|
||||||
)
|
|
||||||
.filter(order => {
|
.filter(order => {
|
||||||
return !order.fillableMakerAssetAmount.isZero() && !order.fillableTakerAssetAmount.isZero();
|
return !order.fillableMakerAssetAmount.isZero() && !order.fillableTakerAssetAmount.isZero();
|
||||||
});
|
});
|
||||||
@ -356,23 +414,31 @@ function getPathInput(path: Fill[]): BigNumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Merges contiguous fills from the same DEX.
|
// Merges contiguous fills from the same DEX.
|
||||||
function simplifyPath(path: Fill[]): Fill[] {
|
function collapsePath(path: Fill[], isBuy: boolean): CollapsedFill[] {
|
||||||
const simplified: Fill[] = [];
|
const collapsed: Array<CollapsedFill | NativeCollapsedFill> = [];
|
||||||
for (const fill of path) {
|
for (const fill of path) {
|
||||||
|
const makerAssetAmount = isBuy ? fill.input : fill.output;
|
||||||
|
const takerAssetAmount = isBuy ? fill.output : fill.input;
|
||||||
const source = (fill.fillData as FillData).source;
|
const source = (fill.fillData as FillData).source;
|
||||||
if (simplified.length !== 0 && source !== ERC20BridgeSource.Native) {
|
if (collapsed.length !== 0 && source !== ERC20BridgeSource.Native) {
|
||||||
const prevFill = simplified[simplified.length - 1];
|
const prevFill = collapsed[collapsed.length - 1];
|
||||||
const prevSource = (prevFill.fillData as FillData).source;
|
|
||||||
// If the last fill is from the same source, merge them.
|
// If the last fill is from the same source, merge them.
|
||||||
if (prevSource === source) {
|
if (prevFill.source === source) {
|
||||||
prevFill.input = prevFill.input.plus(fill.input);
|
prevFill.totalMakerAssetAmount = prevFill.totalMakerAssetAmount.plus(makerAssetAmount);
|
||||||
prevFill.output = prevFill.output.plus(fill.output);
|
prevFill.totalTakerAssetAmount = prevFill.totalTakerAssetAmount.plus(takerAssetAmount);
|
||||||
|
prevFill.subFills.push({ makerAssetAmount, takerAssetAmount });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
simplified.push(fill);
|
collapsed.push({
|
||||||
|
source: fill.fillData.source,
|
||||||
|
totalMakerAssetAmount: makerAssetAmount,
|
||||||
|
totalTakerAssetAmount: takerAssetAmount,
|
||||||
|
subFills: [{ makerAssetAmount, takerAssetAmount }],
|
||||||
|
nativeOrder: (fill.fillData as NativeFillData).order,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return simplified;
|
return collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort fills by descending price.
|
// Sort fills by descending price.
|
||||||
|
@ -13,16 +13,14 @@ export class DexOrderSampler {
|
|||||||
/**
|
/**
|
||||||
* Generate sample amounts up to `maxFillAmount`.
|
* Generate sample amounts up to `maxFillAmount`.
|
||||||
*/
|
*/
|
||||||
public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number): BigNumber[] {
|
public static getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
|
||||||
const amounts = [];
|
const distribution = [...Array<BigNumber>(numSamples)].map((v, i) => new BigNumber(expBase).pow(i));
|
||||||
for (let i = 0; i < numSamples; i++) {
|
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
|
||||||
amounts.push(
|
const amounts = stepSizes.map((s, i) => {
|
||||||
maxFillAmount
|
return maxFillAmount
|
||||||
.times(i + 1)
|
.times(BigNumber.sum(...[0, ...stepSizes.slice(0, i + 1)]))
|
||||||
.div(numSamples)
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
.integerValue(BigNumber.ROUND_UP),
|
});
|
||||||
);
|
|
||||||
}
|
|
||||||
return amounts;
|
return amounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,6 +48,36 @@ export class DexOrderSampler {
|
|||||||
return [fillableAmount, quotes];
|
return [fillableAmount, quotes];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getBatchFillableAmountsAndSampleMarketBuyAsync(
|
||||||
|
nativeOrders: SignedOrder[][],
|
||||||
|
sampleAmounts: BigNumber[][],
|
||||||
|
sources: ERC20BridgeSource[],
|
||||||
|
): Promise<Array<[BigNumber[], DexSample[][]]>> {
|
||||||
|
const signatures = nativeOrders.map(o => o.map(i => i.signature));
|
||||||
|
const fillableAmountsAndSamples = await this._samplerContract
|
||||||
|
.queryBatchOrdersAndSampleBuys(
|
||||||
|
nativeOrders,
|
||||||
|
signatures,
|
||||||
|
sources.map(s => SOURCE_TO_ADDRESS[s]),
|
||||||
|
sampleAmounts,
|
||||||
|
)
|
||||||
|
.callAsync();
|
||||||
|
const batchFillableAmountsAndQuotes: Array<[BigNumber[], DexSample[][]]> = [];
|
||||||
|
fillableAmountsAndSamples.forEach((sampleResult, i) => {
|
||||||
|
const { tokenAmountsBySource, orderFillableAssetAmounts } = sampleResult;
|
||||||
|
const quotes = tokenAmountsBySource.map((rawDexSamples, sourceIdx) => {
|
||||||
|
const source = sources[sourceIdx];
|
||||||
|
return rawDexSamples.map((sample, sampleIdx) => ({
|
||||||
|
source,
|
||||||
|
input: sampleAmounts[i][sampleIdx],
|
||||||
|
output: sample,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
batchFillableAmountsAndQuotes.push([orderFillableAssetAmounts, quotes]);
|
||||||
|
});
|
||||||
|
return batchFillableAmountsAndQuotes;
|
||||||
|
}
|
||||||
|
|
||||||
public async getFillableAmountsAndSampleMarketSellAsync(
|
public async getFillableAmountsAndSampleMarketSellAsync(
|
||||||
nativeOrders: SignedOrder[],
|
nativeOrders: SignedOrder[],
|
||||||
sampleAmounts: BigNumber[],
|
sampleAmounts: BigNumber[],
|
||||||
|
@ -79,6 +79,48 @@ export interface Fill {
|
|||||||
fillData: FillData | NativeFillData;
|
fillData: FillData | NativeFillData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents continguous fills on a path that have been merged together.
|
||||||
|
*/
|
||||||
|
export interface CollapsedFill {
|
||||||
|
/**
|
||||||
|
* The source DEX.
|
||||||
|
*/
|
||||||
|
source: ERC20BridgeSource;
|
||||||
|
/**
|
||||||
|
* Total maker asset amount.
|
||||||
|
*/
|
||||||
|
totalMakerAssetAmount: BigNumber;
|
||||||
|
/**
|
||||||
|
* Total taker asset amount.
|
||||||
|
*/
|
||||||
|
totalTakerAssetAmount: BigNumber;
|
||||||
|
/**
|
||||||
|
* All the fill asset amounts that were collapsed into this node.
|
||||||
|
*/
|
||||||
|
subFills: Array<{
|
||||||
|
makerAssetAmount: BigNumber;
|
||||||
|
takerAssetAmount: BigNumber;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `CollapsedFill` wrapping a native order.
|
||||||
|
*/
|
||||||
|
export interface NativeCollapsedFill extends CollapsedFill {
|
||||||
|
nativeOrder: SignedOrderWithFillableAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimized orders to fill.
|
||||||
|
*/
|
||||||
|
export interface OptimizedMarketOrder extends SignedOrderWithFillableAmounts {
|
||||||
|
/**
|
||||||
|
* The optimized fills that generated this order.
|
||||||
|
*/
|
||||||
|
fill: CollapsedFill;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
|
* Options for `getMarketSellOrdersAsync()` and `getMarketBuyOrdersAsync()`.
|
||||||
*/
|
*/
|
||||||
@ -114,4 +156,12 @@ export interface GetMarketOrdersOpts {
|
|||||||
* Default is 0.01 (100 basis points).
|
* Default is 0.01 (100 basis points).
|
||||||
*/
|
*/
|
||||||
dustFractionThreshold: number;
|
dustFractionThreshold: number;
|
||||||
|
/**
|
||||||
|
* The exponential sampling distribution base.
|
||||||
|
* A value of 1 will result in evenly spaced samples.
|
||||||
|
* > 1 will result in more samples at lower sizes.
|
||||||
|
* < 1 will result in more samples at higher sizes.
|
||||||
|
* Default: 1.25.
|
||||||
|
*/
|
||||||
|
sampleDistributionBase: number;
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@ export class ProtocolFeeUtils {
|
|||||||
public gasPriceEstimation: BigNumber;
|
public gasPriceEstimation: BigNumber;
|
||||||
private readonly _gasPriceHeart: any;
|
private readonly _gasPriceHeart: any;
|
||||||
|
|
||||||
constructor(gasPricePollingIntervalInMs: number) {
|
constructor(gasPricePollingIntervalInMs: number, initialGasPrice: BigNumber = constants.ZERO_AMOUNT) {
|
||||||
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
|
this._gasPriceHeart = heartbeats.createHeart(gasPricePollingIntervalInMs);
|
||||||
this.gasPriceEstimation = constants.ZERO_AMOUNT;
|
this.gasPriceEstimation = initialGasPrice;
|
||||||
this._initializeHeartBeat();
|
this._initializeHeartBeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
|
|
||||||
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
||||||
import { MarketOperationUtils } from './market_operation_utils';
|
import { MarketOperationUtils } from './market_operation_utils';
|
||||||
|
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
|
||||||
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
||||||
import { utils } from './utils';
|
import { utils } from './utils';
|
||||||
|
|
||||||
@ -63,6 +64,58 @@ export class SwapQuoteCalculator {
|
|||||||
)) as MarketBuySwapQuote;
|
)) as MarketBuySwapQuote;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async calculateBatchMarketBuySwapQuoteAsync(
|
||||||
|
batchPrunedOrders: SignedOrder[][],
|
||||||
|
takerAssetFillAmounts: BigNumber[],
|
||||||
|
slippagePercentage: number,
|
||||||
|
gasPrice: BigNumber,
|
||||||
|
opts: CalculateSwapQuoteOpts,
|
||||||
|
): Promise<Array<MarketBuySwapQuote | undefined>> {
|
||||||
|
return (await this._calculateBatchBuySwapQuoteAsync(
|
||||||
|
batchPrunedOrders,
|
||||||
|
takerAssetFillAmounts,
|
||||||
|
slippagePercentage,
|
||||||
|
gasPrice,
|
||||||
|
MarketOperation.Buy,
|
||||||
|
opts,
|
||||||
|
)) as Array<MarketBuySwapQuote | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _calculateBatchBuySwapQuoteAsync(
|
||||||
|
batchPrunedOrders: SignedOrder[][],
|
||||||
|
assetFillAmounts: BigNumber[],
|
||||||
|
slippagePercentage: number,
|
||||||
|
gasPrice: BigNumber,
|
||||||
|
operation: MarketOperation,
|
||||||
|
opts: CalculateSwapQuoteOpts,
|
||||||
|
): Promise<Array<SwapQuote | undefined>> {
|
||||||
|
const assetFillAmountsWithSlippage = assetFillAmounts.map(a =>
|
||||||
|
a.plus(a.multipliedBy(slippagePercentage).integerValue()),
|
||||||
|
);
|
||||||
|
const batchSignedOrders = await this._marketOperationUtils.getBatchMarketBuyOrdersAsync(
|
||||||
|
batchPrunedOrders,
|
||||||
|
assetFillAmountsWithSlippage,
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
const batchSwapQuotes = await Promise.all(
|
||||||
|
batchSignedOrders.map(async (orders, i) => {
|
||||||
|
if (orders) {
|
||||||
|
const { makerAssetData, takerAssetData } = batchPrunedOrders[i][0];
|
||||||
|
return this._createSwapQuoteAsync(
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
orders,
|
||||||
|
operation,
|
||||||
|
assetFillAmounts[i],
|
||||||
|
gasPrice,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return batchSwapQuotes;
|
||||||
|
}
|
||||||
private async _calculateSwapQuoteAsync(
|
private async _calculateSwapQuoteAsync(
|
||||||
prunedOrders: SignedOrder[],
|
prunedOrders: SignedOrder[],
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
@ -74,7 +127,7 @@ export class SwapQuoteCalculator {
|
|||||||
// since prunedOrders do not have fillState, we will add a buffer of fillable orders to consider that some native are orders are partially filled
|
// since prunedOrders do not have fillState, we will add a buffer of fillable orders to consider that some native are orders are partially filled
|
||||||
|
|
||||||
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||||
let resultOrders: SignedOrderWithFillableAmounts[] = [];
|
let resultOrders: OptimizedMarketOrder[] = [];
|
||||||
|
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
@ -91,28 +144,43 @@ export class SwapQuoteCalculator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assetData information for the result
|
// assetData information for the result
|
||||||
const takerAssetData = prunedOrders[0].takerAssetData;
|
const { makerAssetData, takerAssetData } = prunedOrders[0];
|
||||||
const makerAssetData = prunedOrders[0].makerAssetData;
|
return this._createSwapQuoteAsync(
|
||||||
|
makerAssetData,
|
||||||
|
takerAssetData,
|
||||||
|
resultOrders,
|
||||||
|
operation,
|
||||||
|
assetFillAmount,
|
||||||
|
gasPrice,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
private async _createSwapQuoteAsync(
|
||||||
|
makerAssetData: string,
|
||||||
|
takerAssetData: string,
|
||||||
|
resultOrders: OptimizedMarketOrder[],
|
||||||
|
operation: MarketOperation,
|
||||||
|
assetFillAmount: BigNumber,
|
||||||
|
gasPrice: BigNumber,
|
||||||
|
): Promise<SwapQuote> {
|
||||||
const bestCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
const bestCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
||||||
createBestCaseOrders(resultOrders, operation, opts.bridgeSlippage),
|
resultOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
operation,
|
operation,
|
||||||
);
|
);
|
||||||
// in order to calculate the maxRate, reverse the ordersAndFillableAmounts
|
|
||||||
// such that they are sorted from worst rate to best rate
|
|
||||||
const worstCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
const worstCaseQuoteInfo = await this._calculateQuoteInfoAsync(
|
||||||
_.reverse(resultOrders.slice()),
|
resultOrders,
|
||||||
assetFillAmount,
|
assetFillAmount,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
operation,
|
operation,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
const quoteBase = {
|
const quoteBase = {
|
||||||
takerAssetData,
|
takerAssetData,
|
||||||
makerAssetData,
|
makerAssetData,
|
||||||
orders: resultOrders,
|
// Remove fill metadata.
|
||||||
|
orders: resultOrders.map(o => _.omit(o, 'fill')) as SignedOrderWithFillableAmounts[],
|
||||||
bestCaseQuoteInfo,
|
bestCaseQuoteInfo,
|
||||||
worstCaseQuoteInfo,
|
worstCaseQuoteInfo,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
@ -135,31 +203,39 @@ export class SwapQuoteCalculator {
|
|||||||
|
|
||||||
// tslint:disable-next-line: prefer-function-over-method
|
// tslint:disable-next-line: prefer-function-over-method
|
||||||
private async _calculateQuoteInfoAsync(
|
private async _calculateQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
if (operation === MarketOperation.Buy) {
|
if (operation === MarketOperation.Buy) {
|
||||||
return this._calculateMarketBuyQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
|
return this._calculateMarketBuyQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
|
||||||
} else {
|
} else {
|
||||||
return this._calculateMarketSellQuoteInfoAsync(prunedOrders, assetFillAmount, gasPrice);
|
return this._calculateMarketSellQuoteInfoAsync(orders, assetFillAmount, gasPrice, worstCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calculateMarketSellQuoteInfoAsync(
|
private async _calculateMarketSellQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
takerAssetSellAmount: BigNumber,
|
takerAssetSellAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
const result = prunedOrders.reduce(
|
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
(acc, order) => {
|
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const {
|
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
totalMakerAssetAmount,
|
let remainingTakerAssetFillAmount = takerAssetSellAmount;
|
||||||
totalTakerAssetAmount,
|
const filledOrders = [] as OptimizedMarketOrder[];
|
||||||
totalFeeTakerAssetAmount,
|
const _orders = !worstCase ? orders : orders.slice().reverse();
|
||||||
remainingTakerAssetFillAmount,
|
for (const order of _orders) {
|
||||||
} = acc;
|
let makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
let takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
let feeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
if (remainingTakerAssetFillAmount.lte(0)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (order.fill.source === ERC20BridgeSource.Native) {
|
||||||
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
@ -170,99 +246,155 @@ export class SwapQuoteCalculator {
|
|||||||
remainingTakerAssetFillAmount,
|
remainingTakerAssetFillAmount,
|
||||||
adjustedFillableTakerAssetAmount,
|
adjustedFillableTakerAssetAmount,
|
||||||
);
|
);
|
||||||
const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown(
|
const takerAssetAmountBreakDown = getTakerAssetAmountBreakDown(order, takerAssetAmountWithFees);
|
||||||
order,
|
takerAssetAmount = takerAssetAmountBreakDown.takerAssetAmount;
|
||||||
takerAssetAmountWithFees,
|
feeTakerAssetAmount = takerAssetAmountBreakDown.feeTakerAssetAmount;
|
||||||
);
|
makerAssetAmount = takerAssetAmountWithFees
|
||||||
const makerAssetAmount = takerAssetAmountWithFees
|
|
||||||
.div(adjustedFillableTakerAssetAmount)
|
.div(adjustedFillableTakerAssetAmount)
|
||||||
.multipliedBy(adjustedFillableMakerAssetAmount)
|
.times(adjustedFillableMakerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_DOWN);
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
return {
|
} else {
|
||||||
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerAssetAmount),
|
// This is a collapsed bridge order.
|
||||||
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
|
// Because collapsed bridge orders actually fill at different rates,
|
||||||
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
|
// we can iterate over the uncollapsed fills to get the actual
|
||||||
remainingTakerAssetFillAmount: BigNumber.max(
|
// asset amounts transfered.
|
||||||
constants.ZERO_AMOUNT,
|
// We can also assume there are no fees and the order is not
|
||||||
remainingTakerAssetFillAmount.minus(takerAssetAmountWithFees),
|
// partially filled.
|
||||||
),
|
|
||||||
};
|
// Infer the bridge slippage from the difference between the fill
|
||||||
},
|
// size and the actual order asset amounts.
|
||||||
{
|
const makerAssetBridgeSlippage = !worstCase
|
||||||
totalMakerAssetAmount: constants.ZERO_AMOUNT,
|
? constants.ONE_AMOUNT
|
||||||
totalTakerAssetAmount: constants.ZERO_AMOUNT,
|
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
|
||||||
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
|
const takerAssetBridgeSlippage = !worstCase
|
||||||
remainingTakerAssetFillAmount: takerAssetSellAmount,
|
? constants.ONE_AMOUNT
|
||||||
},
|
: order.takerAssetAmount.div(order.fill.totalTakerAssetAmount);
|
||||||
|
// Consecutively fill the subfills in this order.
|
||||||
|
const subFills = !worstCase ? order.fill.subFills : order.fill.subFills.slice().reverse();
|
||||||
|
for (const subFill of subFills) {
|
||||||
|
if (remainingTakerAssetFillAmount.minus(takerAssetAmount).lte(0)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const partialTakerAssetAmount = subFill.takerAssetAmount.times(takerAssetBridgeSlippage);
|
||||||
|
const partialMakerAssetAmount = subFill.makerAssetAmount.times(makerAssetBridgeSlippage);
|
||||||
|
const partialTakerAssetFillAmount = BigNumber.min(
|
||||||
|
partialTakerAssetAmount,
|
||||||
|
remainingTakerAssetFillAmount.minus(takerAssetAmount),
|
||||||
);
|
);
|
||||||
|
const partialMakerAssetFillAmount = partialTakerAssetFillAmount
|
||||||
|
.div(partialTakerAssetAmount)
|
||||||
|
.times(partialMakerAssetAmount)
|
||||||
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
|
takerAssetAmount = takerAssetAmount.plus(partialTakerAssetFillAmount);
|
||||||
|
makerAssetAmount = makerAssetAmount.plus(partialMakerAssetFillAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalMakerAssetAmount = totalMakerAssetAmount.plus(makerAssetAmount);
|
||||||
|
totalTakerAssetAmount = totalTakerAssetAmount.plus(takerAssetAmount);
|
||||||
|
totalFeeTakerAssetAmount = totalFeeTakerAssetAmount.plus(feeTakerAssetAmount);
|
||||||
|
remainingTakerAssetFillAmount = remainingTakerAssetFillAmount
|
||||||
|
.minus(takerAssetAmount)
|
||||||
|
.minus(feeTakerAssetAmount);
|
||||||
|
filledOrders.push(order);
|
||||||
|
}
|
||||||
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
||||||
prunedOrders,
|
filledOrders,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
|
feeTakerAssetAmount: totalFeeTakerAssetAmount,
|
||||||
takerAssetAmount: result.totalTakerAssetAmount,
|
takerAssetAmount: totalTakerAssetAmount,
|
||||||
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
|
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
|
||||||
makerAssetAmount: result.totalMakerAssetAmount,
|
makerAssetAmount: totalMakerAssetAmount,
|
||||||
protocolFeeInWeiAmount,
|
protocolFeeInWeiAmount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calculateMarketBuyQuoteInfoAsync(
|
private async _calculateMarketBuyQuoteInfoAsync(
|
||||||
prunedOrders: SignedOrderWithFillableAmounts[],
|
orders: OptimizedMarketOrder[],
|
||||||
makerAssetBuyAmount: BigNumber,
|
makerAssetBuyAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
|
worstCase: boolean = false,
|
||||||
): Promise<SwapQuoteInfo> {
|
): Promise<SwapQuoteInfo> {
|
||||||
const result = prunedOrders.reduce(
|
let totalMakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
(acc, order) => {
|
let totalTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
const {
|
let totalFeeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
totalMakerAssetAmount,
|
let remainingMakerAssetFillAmount = makerAssetBuyAmount;
|
||||||
totalTakerAssetAmount,
|
const filledOrders = [] as OptimizedMarketOrder[];
|
||||||
totalFeeTakerAssetAmount,
|
const _orders = !worstCase ? orders : orders.slice().reverse();
|
||||||
remainingMakerAssetFillAmount,
|
for (const order of _orders) {
|
||||||
} = acc;
|
let makerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
let takerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
let feeTakerAssetAmount = constants.ZERO_AMOUNT;
|
||||||
|
if (remainingMakerAssetFillAmount.lte(0)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (order.fill.source === ERC20BridgeSource.Native) {
|
||||||
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
const adjustedFillableMakerAssetAmount = fillableAmountsUtils.getMakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
const adjustedFillableTakerAssetAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterFees(
|
const adjustedFillableTakerAssetAmount = fillableAmountsUtils.getTakerAssetAmountSwappedAfterFees(
|
||||||
order,
|
order,
|
||||||
);
|
);
|
||||||
const makerFillAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
|
makerAssetAmount = BigNumber.min(remainingMakerAssetFillAmount, adjustedFillableMakerAssetAmount);
|
||||||
const takerAssetAmountWithFees = makerFillAmount
|
const takerAssetAmountWithFees = makerAssetAmount
|
||||||
.div(adjustedFillableMakerAssetAmount)
|
.div(adjustedFillableMakerAssetAmount)
|
||||||
.multipliedBy(adjustedFillableTakerAssetAmount)
|
.multipliedBy(adjustedFillableTakerAssetAmount)
|
||||||
.integerValue(BigNumber.ROUND_UP);
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
|
const takerAssetAmountBreakDown = getTakerAssetAmountBreakDown(order, takerAssetAmountWithFees);
|
||||||
|
takerAssetAmount = takerAssetAmountBreakDown.takerAssetAmount;
|
||||||
|
feeTakerAssetAmount = takerAssetAmountBreakDown.feeTakerAssetAmount;
|
||||||
|
} else {
|
||||||
|
// This is a collapsed bridge order.
|
||||||
|
// Because collapsed bridge orders actually fill at different rates,
|
||||||
|
// we can iterate over the uncollapsed fills to get the actual
|
||||||
|
// asset amounts transfered.
|
||||||
|
// We can also assume there are no fees and the order is not
|
||||||
|
// partially filled.
|
||||||
|
|
||||||
const { takerAssetAmount, feeTakerAssetAmount } = getTakerAssetAmountBreakDown(
|
// Infer the bridge slippage from the difference between the fill
|
||||||
order,
|
// size and the actual order asset amounts.
|
||||||
takerAssetAmountWithFees,
|
const makerAssetBridgeSlippage = !worstCase
|
||||||
);
|
? constants.ONE_AMOUNT
|
||||||
return {
|
: order.makerAssetAmount.div(order.fill.totalMakerAssetAmount);
|
||||||
totalMakerAssetAmount: totalMakerAssetAmount.plus(makerFillAmount),
|
const takerAssetBridgeSlippage = !worstCase
|
||||||
totalTakerAssetAmount: totalTakerAssetAmount.plus(takerAssetAmount),
|
? constants.ONE_AMOUNT
|
||||||
totalFeeTakerAssetAmount: totalFeeTakerAssetAmount.plus(feeTakerAssetAmount),
|
: order.takerAssetAmount.div(order.fill.totalTakerAssetAmount);
|
||||||
remainingMakerAssetFillAmount: BigNumber.max(
|
// Consecutively fill the subfills in this order.
|
||||||
constants.ZERO_AMOUNT,
|
const subFills = !worstCase ? order.fill.subFills : order.fill.subFills.slice().reverse();
|
||||||
remainingMakerAssetFillAmount.minus(makerFillAmount),
|
for (const subFill of subFills) {
|
||||||
),
|
if (remainingMakerAssetFillAmount.minus(makerAssetAmount).lte(0)) {
|
||||||
};
|
break;
|
||||||
},
|
}
|
||||||
{
|
const partialTakerAssetAmount = subFill.takerAssetAmount.times(takerAssetBridgeSlippage);
|
||||||
totalMakerAssetAmount: constants.ZERO_AMOUNT,
|
const partialMakerAssetAmount = subFill.makerAssetAmount.times(makerAssetBridgeSlippage);
|
||||||
totalTakerAssetAmount: constants.ZERO_AMOUNT,
|
const partialMakerAssetFillAmount = BigNumber.min(
|
||||||
totalFeeTakerAssetAmount: constants.ZERO_AMOUNT,
|
partialMakerAssetAmount,
|
||||||
remainingMakerAssetFillAmount: makerAssetBuyAmount,
|
remainingMakerAssetFillAmount.minus(makerAssetAmount),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
const partialTakerAssetFillAmount = partialMakerAssetFillAmount
|
||||||
|
.div(partialMakerAssetAmount)
|
||||||
|
.times(partialTakerAssetAmount)
|
||||||
|
.integerValue(BigNumber.ROUND_UP);
|
||||||
|
takerAssetAmount = takerAssetAmount.plus(partialTakerAssetFillAmount);
|
||||||
|
makerAssetAmount = makerAssetAmount.plus(partialMakerAssetFillAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalMakerAssetAmount = totalMakerAssetAmount.plus(makerAssetAmount);
|
||||||
|
totalTakerAssetAmount = totalTakerAssetAmount.plus(takerAssetAmount);
|
||||||
|
totalFeeTakerAssetAmount = totalFeeTakerAssetAmount.plus(feeTakerAssetAmount);
|
||||||
|
remainingMakerAssetFillAmount = remainingMakerAssetFillAmount.minus(makerAssetAmount);
|
||||||
|
filledOrders.push(order);
|
||||||
|
}
|
||||||
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
const protocolFeeInWeiAmount = await this._protocolFeeUtils.calculateWorstCaseProtocolFeeAsync(
|
||||||
prunedOrders,
|
filledOrders,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
feeTakerAssetAmount: result.totalFeeTakerAssetAmount,
|
feeTakerAssetAmount: totalFeeTakerAssetAmount,
|
||||||
takerAssetAmount: result.totalTakerAssetAmount,
|
takerAssetAmount: totalTakerAssetAmount,
|
||||||
totalTakerAssetAmount: result.totalFeeTakerAssetAmount.plus(result.totalTakerAssetAmount),
|
totalTakerAssetAmount: totalFeeTakerAssetAmount.plus(totalTakerAssetAmount),
|
||||||
makerAssetAmount: result.totalMakerAssetAmount,
|
makerAssetAmount: totalMakerAssetAmount,
|
||||||
protocolFeeInWeiAmount,
|
protocolFeeInWeiAmount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -303,35 +435,3 @@ function getTakerAssetAmountBreakDown(
|
|||||||
takerAssetAmount: takerAssetAmountWithFees,
|
takerAssetAmount: takerAssetAmountWithFees,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBestCaseOrders(
|
|
||||||
orders: SignedOrderWithFillableAmounts[],
|
|
||||||
operation: MarketOperation,
|
|
||||||
bridgeSlippage: number,
|
|
||||||
): SignedOrderWithFillableAmounts[] {
|
|
||||||
// Scale the asset amounts to undo the bridge slippage applied to
|
|
||||||
// bridge orders.
|
|
||||||
const bestCaseOrders: SignedOrderWithFillableAmounts[] = [];
|
|
||||||
for (const order of orders) {
|
|
||||||
if (!order.makerAssetData.startsWith(constants.BRIDGE_ASSET_DATA_PREFIX)) {
|
|
||||||
bestCaseOrders.push(order);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bestCaseOrders.push({
|
|
||||||
...order,
|
|
||||||
...(operation === MarketOperation.Sell
|
|
||||||
? {
|
|
||||||
makerAssetAmount: order.makerAssetAmount
|
|
||||||
.dividedBy(1 - bridgeSlippage)
|
|
||||||
.integerValue(BigNumber.ROUND_DOWN),
|
|
||||||
}
|
|
||||||
: // Buy operation
|
|
||||||
{
|
|
||||||
takerAssetAmount: order.takerAssetAmount
|
|
||||||
.dividedBy(bridgeSlippage + 1)
|
|
||||||
.integerValue(BigNumber.ROUND_UP),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return bestCaseOrders;
|
|
||||||
}
|
|
||||||
|
@ -23,7 +23,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(9));
|
||||||
});
|
});
|
||||||
it('should provide correct liquidity result with orders with takerFees in takerAsset', () => {
|
it('should provide correct liquidity result with orders with takerFees in takerAsset', () => {
|
||||||
@ -31,7 +31,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(10));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(11));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(15));
|
||||||
});
|
});
|
||||||
it('should provide correct liquidity result with orders with takerFees in makerAsset', () => {
|
it('should provide correct liquidity result with orders with takerFees in makerAsset', () => {
|
||||||
@ -51,7 +51,7 @@ describe('#calculateLiquidity', () => {
|
|||||||
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
const { makerAssetAvailableInBaseUnits, takerAssetAvailableInBaseUnits } = calculateLiquidity(
|
||||||
prunedSignedOrders,
|
prunedSignedOrders,
|
||||||
);
|
);
|
||||||
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(25));
|
expect(makerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(27));
|
||||||
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33));
|
expect(takerAssetAvailableInBaseUnits).to.bignumber.eq(baseUnitAmount(33));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -123,7 +123,7 @@ describe('ExchangeSwapQuoteConsumer', () => {
|
|||||||
};
|
};
|
||||||
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
|
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
|
||||||
expectMakerAndTakerBalancesForTakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory(
|
expectMakerAndTakerBalancesForTakerAssetAsync = expectMakerAndTakerBalancesAsyncFactory(
|
||||||
erc20TakerTokenContract,
|
erc20TakerTokenContract,
|
||||||
makerAddress,
|
makerAddress,
|
||||||
|
@ -126,7 +126,7 @@ describe('ForwarderSwapQuoteConsumer', () => {
|
|||||||
};
|
};
|
||||||
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
|
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
|
||||||
expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory(
|
expectMakerAndTakerBalancesAsync = expectMakerAndTakerBalancesAsyncFactory(
|
||||||
erc20TokenContract,
|
erc20TokenContract,
|
||||||
makerAddress,
|
makerAddress,
|
||||||
|
@ -306,7 +306,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
|
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
|
||||||
);
|
);
|
||||||
const DEFAULT_SAMPLER = createSamplerFromSellRates(SOURCE_RATES);
|
const DEFAULT_SAMPLER = createSamplerFromSellRates(SOURCE_RATES);
|
||||||
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 };
|
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 };
|
||||||
const defaultMarketOperationUtils = new MarketOperationUtils(
|
const defaultMarketOperationUtils = new MarketOperationUtils(
|
||||||
DEFAULT_SAMPLER,
|
DEFAULT_SAMPLER,
|
||||||
contractAddresses,
|
contractAddresses,
|
||||||
@ -552,7 +552,7 @@ describe('MarketOperationUtils tests', () => {
|
|||||||
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
|
_.times(3, () => SOURCE_RATES[ERC20BridgeSource.Native][0]),
|
||||||
);
|
);
|
||||||
const DEFAULT_SAMPLER = createSamplerFromBuyRates(SOURCE_RATES);
|
const DEFAULT_SAMPLER = createSamplerFromBuyRates(SOURCE_RATES);
|
||||||
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0 };
|
const DEFAULT_OPTS = { numSamples: 3, runLimit: 0, sampleDistributionBase: 1 };
|
||||||
const defaultMarketOperationUtils = new MarketOperationUtils(
|
const defaultMarketOperationUtils = new MarketOperationUtils(
|
||||||
DEFAULT_SAMPLER,
|
DEFAULT_SAMPLER,
|
||||||
contractAddresses,
|
contractAddresses,
|
||||||
|
@ -65,6 +65,9 @@ const createSamplerFromSignedOrdersWithFillableAmounts = (
|
|||||||
return sampler;
|
return sampler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO(dorothy-zbornak): Replace these tests entirely with unit tests because
|
||||||
|
// omg they're a nightmare to maintain.
|
||||||
|
|
||||||
// tslint:disable:max-file-line-count
|
// tslint:disable:max-file-line-count
|
||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
describe('swapQuoteCalculator', () => {
|
describe('swapQuoteCalculator', () => {
|
||||||
@ -272,7 +275,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with slippage (feeless orders)', async () => {
|
it('calculates a correct swapQuote with slippage (feeless orders)', async () => {
|
||||||
const assetSellAmount = baseUnitAmount(1);
|
const assetSellAmount = baseUnitAmount(4);
|
||||||
const slippagePercentage = 0.2;
|
const slippagePercentage = 0.2;
|
||||||
const sampler = createSamplerFromSignedOrdersWithFillableAmounts(
|
const sampler = createSamplerFromSignedOrdersWithFillableAmounts(
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS,
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS,
|
||||||
@ -289,10 +292,10 @@ describe('swapQuoteCalculator', () => {
|
|||||||
GAS_PRICE,
|
GAS_PRICE,
|
||||||
CALCULATE_SWAP_QUOTE_OPTS,
|
CALCULATE_SWAP_QUOTE_OPTS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
||||||
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[2],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
@ -301,15 +304,15 @@ describe('swapQuoteCalculator', () => {
|
|||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: assetSellAmount,
|
takerAssetAmount: assetSellAmount,
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(9),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: assetSellAmount,
|
takerAssetAmount: assetSellAmount,
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.4),
|
makerAssetAmount: baseUnitAmount(1.6),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with no slippage (takerAsset denominated fee orders)', async () => {
|
||||||
@ -372,7 +375,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[2],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
@ -381,14 +384,14 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2.25)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(4.5),
|
makerAssetAmount: baseUnitAmount(4.5),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0.5),
|
feeTakerAssetAmount: baseUnitAmount(1.2),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(0.5)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.2)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(1),
|
makerAssetAmount: baseUnitAmount(1.8),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with no slippage (makerAsset denominated fee orders)', async () => {
|
||||||
@ -411,23 +414,24 @@ describe('swapQuoteCalculator', () => {
|
|||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
||||||
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(4),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(4),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', async () => {
|
it('calculates a correct swapQuote with slippage (makerAsset denominated fee orders)', async () => {
|
||||||
@ -451,24 +455,24 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
||||||
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
expect(swapQuote.takerAssetFillAmount).to.bignumber.equal(assetSellAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
// 50 takerAsset units to fill the first order + 100 takerAsset units for fees
|
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
||||||
|
feeTakerAssetAmount: baseUnitAmount(1.5).minus(1),
|
||||||
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(1.5)).plus(1),
|
||||||
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
|
makerAssetAmount: baseUnitAmount(4),
|
||||||
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
feeTakerAssetAmount: baseUnitAmount(2),
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
totalTakerAssetAmount: assetSellAmount,
|
||||||
makerAssetAmount: baseUnitAmount(0.8),
|
makerAssetAmount: baseUnitAmount(0.8),
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
|
||||||
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
|
||||||
feeTakerAssetAmount: baseUnitAmount(2),
|
|
||||||
takerAssetAmount: assetSellAmount.minus(baseUnitAmount(2)),
|
|
||||||
totalTakerAssetAmount: assetSellAmount,
|
|
||||||
makerAssetAmount: baseUnitAmount(3.6),
|
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -678,7 +682,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[0],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS[2],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
|
|
||||||
@ -692,12 +696,16 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount,
|
takerAssetAmount,
|
||||||
totalTakerAssetAmount: takerAssetAmount,
|
totalTakerAssetAmount: takerAssetAmount,
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(0),
|
feeTakerAssetAmount: baseUnitAmount(0),
|
||||||
takerAssetAmount: baseUnitAmount(5.5),
|
takerAssetAmount: baseUnitAmount(20)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5.5),
|
.div(6)
|
||||||
|
.integerValue(BigNumber.ROUND_UP),
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(20)
|
||||||
|
.div(6)
|
||||||
|
.integerValue(BigNumber.ROUND_UP),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@ -766,7 +774,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[0],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET[2],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
@ -775,12 +783,16 @@ describe('swapQuoteCalculator', () => {
|
|||||||
takerAssetAmount: fiveSixthEthInWei,
|
takerAssetAmount: fiveSixthEthInWei,
|
||||||
totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei),
|
totalTakerAssetAmount: baseUnitAmount(2.5).plus(fiveSixthEthInWei),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(3),
|
||||||
takerAssetAmount: baseUnitAmount(5.5),
|
takerAssetAmount: baseUnitAmount(10)
|
||||||
totalTakerAssetAmount: baseUnitAmount(8),
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 3.3333...
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(19)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 6.3333...
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@ -805,21 +817,33 @@ describe('swapQuoteCalculator', () => {
|
|||||||
);
|
);
|
||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.bestCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(0.5)
|
||||||
takerAssetAmount: baseUnitAmount(2.5),
|
.div(3)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5),
|
.integerValue(BigNumber.ROUND_UP), // 0.16666...
|
||||||
|
takerAssetAmount: baseUnitAmount(0.5)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 0.1666...
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(1)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 0.3333...
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.5),
|
feeTakerAssetAmount: baseUnitAmount(0.5)
|
||||||
takerAssetAmount: baseUnitAmount(2.5),
|
.div(3)
|
||||||
totalTakerAssetAmount: baseUnitAmount(5),
|
.integerValue(BigNumber.ROUND_UP), // 0.16666...
|
||||||
|
takerAssetAmount: baseUnitAmount(0.5)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 0.1666...
|
||||||
|
totalTakerAssetAmount: baseUnitAmount(1)
|
||||||
|
.div(3)
|
||||||
|
.integerValue(BigNumber.ROUND_UP), // 0.3333...
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
@ -845,14 +869,14 @@ describe('swapQuoteCalculator', () => {
|
|||||||
// test if orders are correct
|
// test if orders are correct
|
||||||
expect(swapQuote.orders).to.deep.equal([
|
expect(swapQuote.orders).to.deep.equal([
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[1],
|
||||||
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[0],
|
testOrders.SIGNED_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_MAKER_ASSET[2],
|
||||||
]);
|
]);
|
||||||
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
expect(swapQuote.makerAssetFillAmount).to.bignumber.equal(assetBuyAmount);
|
||||||
// test if rates are correct
|
// test if rates are correct
|
||||||
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
expect(swapQuote.worstCaseQuoteInfo).to.deep.equal({
|
||||||
feeTakerAssetAmount: baseUnitAmount(2.75),
|
feeTakerAssetAmount: baseUnitAmount(1.25).minus(1),
|
||||||
takerAssetAmount: baseUnitAmount(2.75),
|
takerAssetAmount: baseUnitAmount(2.25).plus(1),
|
||||||
totalTakerAssetAmount: baseUnitAmount(5.5),
|
totalTakerAssetAmount: baseUnitAmount(3.5),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
||||||
});
|
});
|
||||||
@ -879,7 +903,7 @@ describe('swapQuoteCalculator', () => {
|
|||||||
.multipliedBy(0.1)
|
.multipliedBy(0.1)
|
||||||
.integerValue(BigNumber.ROUND_CEIL),
|
.integerValue(BigNumber.ROUND_CEIL),
|
||||||
makerAssetAmount: assetBuyAmount,
|
makerAssetAmount: assetBuyAmount,
|
||||||
protocolFeeInWeiAmount: baseUnitAmount(30, 4),
|
protocolFeeInWeiAmount: baseUnitAmount(15, 4),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -119,7 +119,7 @@ describe('swapQuoteConsumerUtils', () => {
|
|||||||
};
|
};
|
||||||
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
const privateKey = devConstants.TESTRPC_PRIVATE_KEYS[userAddresses.indexOf(makerAddress)];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS);
|
protocolFeeUtils = new ProtocolFeeUtils(constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS, new BigNumber(1));
|
||||||
forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams);
|
forwarderOrderFactory = new OrderFactory(privateKey, defaultForwarderOrderParams);
|
||||||
|
|
||||||
swapQuoteConsumer = new SwapQuoteConsumer(provider, {
|
swapQuoteConsumer = new SwapQuoteConsumer(provider, {
|
||||||
|
@ -51,7 +51,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEELESS: Array<Partial<SignedOrderWit
|
|||||||
takerAssetAmount: baseUnitAmount(6),
|
takerAssetAmount: baseUnitAmount(6),
|
||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(6),
|
||||||
fillableTakerAssetAmount: baseUnitAmount(3),
|
fillableTakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableMakerAssetAmount: baseUnitAmount(2),
|
fillableMakerAssetAmount: baseUnitAmount(3),
|
||||||
},
|
},
|
||||||
...PARTIAL_ORDER,
|
...PARTIAL_ORDER,
|
||||||
},
|
},
|
||||||
@ -86,7 +86,7 @@ const PARTIAL_ORDERS_WITH_FILLABLE_AMOUNTS_FEE_IN_TAKER_ASSET: Array<Partial<Sig
|
|||||||
makerAssetAmount: baseUnitAmount(6),
|
makerAssetAmount: baseUnitAmount(6),
|
||||||
takerFee: baseUnitAmount(4),
|
takerFee: baseUnitAmount(4),
|
||||||
fillableTakerAssetAmount: baseUnitAmount(3),
|
fillableTakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableMakerAssetAmount: baseUnitAmount(2),
|
fillableMakerAssetAmount: baseUnitAmount(3),
|
||||||
fillableTakerFeeAmount: baseUnitAmount(2),
|
fillableTakerFeeAmount: baseUnitAmount(2),
|
||||||
},
|
},
|
||||||
...PARTIAL_ORDER_FEE_IN_TAKER_ASSET,
|
...PARTIAL_ORDER_FEE_IN_TAKER_ASSET,
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update `DevUtils` mainnet, Ropsten, Rinkeby, Kovan addresses",
|
"note": "Update `DevUtils` mainnet, Ropsten, Rinkeby, Kovan addresses",
|
||||||
"pr": 2436
|
"pr": 2436
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Update `ERC20BridgeSampler` address on kovan and mainnet.",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
"devUtils": "0x5f53f2aa72cb3a9371bf3c58e8fb3a313478b2f4",
|
"devUtils": "0x5f53f2aa72cb3a9371bf3c58e8fb3a313478b2f4",
|
||||||
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
|
"erc20BridgeProxy": "0x8ed95d1746bf1e4dab58d8ed4724f1ef95b20db0",
|
||||||
"uniswapBridge": "0x533344cfdf2a3e911e2cf4c6f5ed08e791f5355f",
|
"uniswapBridge": "0x533344cfdf2a3e911e2cf4c6f5ed08e791f5355f",
|
||||||
"erc20BridgeSampler": "0x1b402fdb5ee87f989c11e3963557e89cc313b6c0",
|
"erc20BridgeSampler": "0x25840bf3582cb9e5acabbf45148b3092ac3f6b56",
|
||||||
"kyberBridge": "0xf342f3a80fdc9b48713d58fe97e17f5cc764ee62",
|
"kyberBridge": "0xf342f3a80fdc9b48713d58fe97e17f5cc764ee62",
|
||||||
"eth2DaiBridge": "0xe97ea901d034ba2e018155264f77c417ce7717f9",
|
"eth2DaiBridge": "0xe97ea901d034ba2e018155264f77c417ce7717f9",
|
||||||
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
|
"chaiBridge": "0x77c31eba23043b9a72d13470f3a3a311344d7438",
|
||||||
@ -110,7 +110,7 @@
|
|||||||
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
"erc20BridgeProxy": "0xfb2dd2a1366de37f7241c83d47da58fd503e2c64",
|
||||||
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
"uniswapBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
"eth2DaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"erc20BridgeSampler": "0x551f0e213dcb71f676558d8b0ab559d1cdd103f2",
|
"erc20BridgeSampler": "0x8c9f255253bcf2c9539c887fee4d71c69fd035b9",
|
||||||
"kyberBridge": "0x0000000000000000000000000000000000000000",
|
"kyberBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
"chaiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"dydxBridge": "0x0000000000000000000000000000000000000000"
|
"dydxBridge": "0x0000000000000000000000000000000000000000"
|
||||||
|
@ -9,6 +9,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update all artifacts.",
|
"note": "Update all artifacts.",
|
||||||
"pr": 2432
|
"pr": 2432
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Update `IERC20BridgeSampler artifact",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -71,6 +71,94 @@
|
|||||||
"stateMutability": "view",
|
"stateMutability": "view",
|
||||||
"type": "function"
|
"type": "function"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "makerAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "takerAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "feeRecipientAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "senderAddress", "type": "address" },
|
||||||
|
{ "internalType": "uint256", "name": "makerAssetAmount", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "takerAssetAmount", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "makerFee", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "takerFee", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "expirationTimeSeconds", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "salt", "type": "uint256" },
|
||||||
|
{ "internalType": "bytes", "name": "makerAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "takerAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "makerFeeAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "takerFeeAssetData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct LibOrder.Order[][]",
|
||||||
|
"name": "orders",
|
||||||
|
"type": "tuple[][]"
|
||||||
|
},
|
||||||
|
{ "internalType": "bytes[][]", "name": "orderSignatures", "type": "bytes[][]" },
|
||||||
|
{ "internalType": "address[]", "name": "sources", "type": "address[]" },
|
||||||
|
{ "internalType": "uint256[][]", "name": "makerTokenAmounts", "type": "uint256[][]" }
|
||||||
|
],
|
||||||
|
"name": "queryBatchOrdersAndSampleBuys",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "uint256[]", "name": "orderFillableAssetAmounts", "type": "uint256[]" },
|
||||||
|
{ "internalType": "uint256[][]", "name": "tokenAmountsBySource", "type": "uint256[][]" }
|
||||||
|
],
|
||||||
|
"internalType": "struct IERC20BridgeSampler.OrdersAndSample[]",
|
||||||
|
"name": "ordersAndSamples",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "address", "name": "makerAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "takerAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "feeRecipientAddress", "type": "address" },
|
||||||
|
{ "internalType": "address", "name": "senderAddress", "type": "address" },
|
||||||
|
{ "internalType": "uint256", "name": "makerAssetAmount", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "takerAssetAmount", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "makerFee", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "takerFee", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "expirationTimeSeconds", "type": "uint256" },
|
||||||
|
{ "internalType": "uint256", "name": "salt", "type": "uint256" },
|
||||||
|
{ "internalType": "bytes", "name": "makerAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "takerAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "makerFeeAssetData", "type": "bytes" },
|
||||||
|
{ "internalType": "bytes", "name": "takerFeeAssetData", "type": "bytes" }
|
||||||
|
],
|
||||||
|
"internalType": "struct LibOrder.Order[][]",
|
||||||
|
"name": "orders",
|
||||||
|
"type": "tuple[][]"
|
||||||
|
},
|
||||||
|
{ "internalType": "bytes[][]", "name": "orderSignatures", "type": "bytes[][]" },
|
||||||
|
{ "internalType": "address[]", "name": "sources", "type": "address[]" },
|
||||||
|
{ "internalType": "uint256[][]", "name": "takerTokenAmounts", "type": "uint256[][]" }
|
||||||
|
],
|
||||||
|
"name": "queryBatchOrdersAndSampleSells",
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"components": [
|
||||||
|
{ "internalType": "uint256[]", "name": "orderFillableAssetAmounts", "type": "uint256[]" },
|
||||||
|
{ "internalType": "uint256[][]", "name": "tokenAmountsBySource", "type": "uint256[][]" }
|
||||||
|
],
|
||||||
|
"internalType": "struct IERC20BridgeSampler.OrdersAndSample[]",
|
||||||
|
"name": "ordersAndSamples",
|
||||||
|
"type": "tuple[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"constant": true,
|
"constant": true,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
@ -196,6 +284,26 @@
|
|||||||
},
|
},
|
||||||
"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`."
|
||||||
},
|
},
|
||||||
|
"queryBatchOrdersAndSampleBuys((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[][],bytes[][],address[],uint256[][])": {
|
||||||
|
"details": "Query batches of native orders and sample buy quotes on multiple DEXes at once.",
|
||||||
|
"params": {
|
||||||
|
"makerTokenAmounts": "Batches of Maker token sell amount for each sample.",
|
||||||
|
"orderSignatures": "Batches of Signatures for each respective order in `orders`.",
|
||||||
|
"orders": "Batches of Native orders to query.",
|
||||||
|
"sources": "Address of each DEX. Passing in an unsupported DEX will throw."
|
||||||
|
},
|
||||||
|
"return": "ordersAndSamples How much taker asset can be filled by each order in `orders`. Taker amounts sold for each source at each maker token amount. First indexed by source index, then sample index"
|
||||||
|
},
|
||||||
|
"queryBatchOrdersAndSampleSells((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[][],bytes[][],address[],uint256[][])": {
|
||||||
|
"details": "Query batches of native orders and sample sell quotes on multiple DEXes at once.",
|
||||||
|
"params": {
|
||||||
|
"orderSignatures": "Batches of Signatures for each respective order in `orders`.",
|
||||||
|
"orders": "Batches of Native orders to query.",
|
||||||
|
"sources": "Address of each DEX. Passing in an unsupported DEX will throw.",
|
||||||
|
"takerTokenAmounts": "Batches of Taker token sell amount for each sample."
|
||||||
|
},
|
||||||
|
"return": "ordersAndSamples How much taker asset can be filled by each order in `orders`. Maker amounts bought for each source at each taker token amount. First indexed by source index, then sample index."
|
||||||
|
},
|
||||||
"queryOrdersAndSampleBuys((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],address[],uint256[])": {
|
"queryOrdersAndSampleBuys((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[],bytes[],address[],uint256[])": {
|
||||||
"details": "Query native orders and sample buy quotes on multiple DEXes at once.",
|
"details": "Query native orders and sample buy quotes on multiple DEXes at once.",
|
||||||
"params": {
|
"params": {
|
||||||
|
@ -13,6 +13,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Regenerate wrappers to catch empty reverts on live networks.",
|
"note": "Regenerate wrappers to catch empty reverts on live networks.",
|
||||||
"pr": 2433
|
"pr": 2433
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Update `IERC20BridgeSampler`",
|
||||||
|
"pr": 2427
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"rebuild": "yarn wrappers:clean && yarn wrappers:generate && yarn wrappers:prettier && yarn build",
|
"rebuild": "yarn wrappers:clean && yarn wrappers:generate && yarn wrappers:prettier && yarn build",
|
||||||
"build:ci": "yarn build",
|
"build:ci": "yarn build",
|
||||||
"lint": "tslint --format stylish --project . --exclude **/lib/**/*",
|
"lint": "tslint --format stylish --project . --exclude **/lib/**/*",
|
||||||
"fix": "tslint --fix --format stylish --project .--exclude **/lib/**/*",
|
"fix": "tslint --fix --format stylish --project . --exclude **/lib/**/*",
|
||||||
"prettier": "prettier --write **/* --config ../../.prettierrc",
|
"prettier": "prettier --write **/* --config ../../.prettierrc",
|
||||||
"clean": "shx rm -rf lib generated_docs",
|
"clean": "shx rm -rf lib generated_docs",
|
||||||
"docs_test": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --out generated_docs ./src/generated-wrappers/*",
|
"docs_test": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --out generated_docs ./src/generated-wrappers/*",
|
||||||
|
@ -281,6 +281,204 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
stateMutability: 'view',
|
stateMutability: 'view',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'orders',
|
||||||
|
type: 'tuple[][]',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'makerAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'feeRecipientAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'senderAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerAssetAmount',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAssetAmount',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerFee',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerFee',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'expirationTimeSeconds',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'salt',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerFeeAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerFeeAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'orderSignatures',
|
||||||
|
type: 'bytes[][]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sources',
|
||||||
|
type: 'address[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerTokenAmounts',
|
||||||
|
type: 'uint256[][]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'queryBatchOrdersAndSampleBuys',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'ordersAndSamples',
|
||||||
|
type: 'tuple[]',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'orderFillableAssetAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tokenAmountsBySource',
|
||||||
|
type: 'uint256[][]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
constant: true,
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
name: 'orders',
|
||||||
|
type: 'tuple[][]',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'makerAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'feeRecipientAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'senderAddress',
|
||||||
|
type: 'address',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerAssetAmount',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAssetAmount',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerFee',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerFee',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'expirationTimeSeconds',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'salt',
|
||||||
|
type: 'uint256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'makerFeeAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerFeeAssetData',
|
||||||
|
type: 'bytes',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'orderSignatures',
|
||||||
|
type: 'bytes[][]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sources',
|
||||||
|
type: 'address[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'takerTokenAmounts',
|
||||||
|
type: 'uint256[][]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: 'queryBatchOrdersAndSampleSells',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
name: 'ordersAndSamples',
|
||||||
|
type: 'tuple[]',
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
name: 'orderFillableAssetAmounts',
|
||||||
|
type: 'uint256[]',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tokenAmountsBySource',
|
||||||
|
type: 'uint256[][]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
payable: false,
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
constant: true,
|
constant: true,
|
||||||
inputs: [
|
inputs: [
|
||||||
@ -654,6 +852,118 @@ export class IERC20BridgeSamplerContract extends BaseContract {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public queryBatchOrdersAndSampleBuys(
|
||||||
|
orders: Array<
|
||||||
|
Array<{
|
||||||
|
makerAddress: string;
|
||||||
|
takerAddress: string;
|
||||||
|
feeRecipientAddress: string;
|
||||||
|
senderAddress: string;
|
||||||
|
makerAssetAmount: BigNumber;
|
||||||
|
takerAssetAmount: BigNumber;
|
||||||
|
makerFee: BigNumber;
|
||||||
|
takerFee: BigNumber;
|
||||||
|
expirationTimeSeconds: BigNumber;
|
||||||
|
salt: BigNumber;
|
||||||
|
makerAssetData: string;
|
||||||
|
takerAssetData: string;
|
||||||
|
makerFeeAssetData: string;
|
||||||
|
takerFeeAssetData: string;
|
||||||
|
}>
|
||||||
|
>,
|
||||||
|
orderSignatures: string[][],
|
||||||
|
sources: string[],
|
||||||
|
makerTokenAmounts: BigNumber[][],
|
||||||
|
): ContractFunctionObj<Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>> {
|
||||||
|
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||||
|
assert.isArray('orders', orders);
|
||||||
|
assert.isArray('orderSignatures', orderSignatures);
|
||||||
|
assert.isArray('sources', sources);
|
||||||
|
assert.isArray('makerTokenAmounts', makerTokenAmounts);
|
||||||
|
const functionSignature =
|
||||||
|
'queryBatchOrdersAndSampleBuys((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[][],bytes[][],address[],uint256[][])';
|
||||||
|
|
||||||
|
return {
|
||||||
|
async callAsync(
|
||||||
|
callData: Partial<CallData> = {},
|
||||||
|
defaultBlock?: BlockParam,
|
||||||
|
): Promise<Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>> {
|
||||||
|
BaseContract._assertCallParams(callData, defaultBlock);
|
||||||
|
const rawCallResult = await self._performCallAsync(
|
||||||
|
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||||
|
defaultBlock,
|
||||||
|
);
|
||||||
|
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||||
|
return abiEncoder.strictDecodeReturnValue<
|
||||||
|
Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>
|
||||||
|
>(rawCallResult);
|
||||||
|
},
|
||||||
|
getABIEncodedTransactionData(): string {
|
||||||
|
return self._strictEncodeArguments(functionSignature, [
|
||||||
|
orders,
|
||||||
|
orderSignatures,
|
||||||
|
sources,
|
||||||
|
makerTokenAmounts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public queryBatchOrdersAndSampleSells(
|
||||||
|
orders: Array<
|
||||||
|
Array<{
|
||||||
|
makerAddress: string;
|
||||||
|
takerAddress: string;
|
||||||
|
feeRecipientAddress: string;
|
||||||
|
senderAddress: string;
|
||||||
|
makerAssetAmount: BigNumber;
|
||||||
|
takerAssetAmount: BigNumber;
|
||||||
|
makerFee: BigNumber;
|
||||||
|
takerFee: BigNumber;
|
||||||
|
expirationTimeSeconds: BigNumber;
|
||||||
|
salt: BigNumber;
|
||||||
|
makerAssetData: string;
|
||||||
|
takerAssetData: string;
|
||||||
|
makerFeeAssetData: string;
|
||||||
|
takerFeeAssetData: string;
|
||||||
|
}>
|
||||||
|
>,
|
||||||
|
orderSignatures: string[][],
|
||||||
|
sources: string[],
|
||||||
|
takerTokenAmounts: BigNumber[][],
|
||||||
|
): ContractFunctionObj<Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>> {
|
||||||
|
const self = (this as any) as IERC20BridgeSamplerContract;
|
||||||
|
assert.isArray('orders', orders);
|
||||||
|
assert.isArray('orderSignatures', orderSignatures);
|
||||||
|
assert.isArray('sources', sources);
|
||||||
|
assert.isArray('takerTokenAmounts', takerTokenAmounts);
|
||||||
|
const functionSignature =
|
||||||
|
'queryBatchOrdersAndSampleSells((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes,bytes,bytes)[][],bytes[][],address[],uint256[][])';
|
||||||
|
|
||||||
|
return {
|
||||||
|
async callAsync(
|
||||||
|
callData: Partial<CallData> = {},
|
||||||
|
defaultBlock?: BlockParam,
|
||||||
|
): Promise<Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>> {
|
||||||
|
BaseContract._assertCallParams(callData, defaultBlock);
|
||||||
|
const rawCallResult = await self._performCallAsync(
|
||||||
|
{ ...callData, data: this.getABIEncodedTransactionData() },
|
||||||
|
defaultBlock,
|
||||||
|
);
|
||||||
|
const abiEncoder = self._lookupAbiEncoder(functionSignature);
|
||||||
|
return abiEncoder.strictDecodeReturnValue<
|
||||||
|
Array<{ orderFillableAssetAmounts: BigNumber[]; tokenAmountsBySource: BigNumber[][] }>
|
||||||
|
>(rawCallResult);
|
||||||
|
},
|
||||||
|
getABIEncodedTransactionData(): string {
|
||||||
|
return self._strictEncodeArguments(functionSignature, [
|
||||||
|
orders,
|
||||||
|
orderSignatures,
|
||||||
|
sources,
|
||||||
|
takerTokenAmounts,
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Query native orders and sample buy quotes on multiple DEXes at once.
|
* Query native orders and sample buy quotes on multiple DEXes at once.
|
||||||
* @param orders Native orders to query.
|
* @param orders Native orders to query.
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "2.1.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Added `getBatchOrdersAsync` for fetching batches of orders",
|
||||||
|
"pr": 2427
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1578272714,
|
"timestamp": 1578272714,
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
|
@ -25,6 +25,19 @@ export class OrderStore {
|
|||||||
}
|
}
|
||||||
return orderSet;
|
return orderSet;
|
||||||
}
|
}
|
||||||
|
public async getBatchOrderSetsForAssetsAsync(
|
||||||
|
makerAssetDatas: string[],
|
||||||
|
takerAssetDatas: string[],
|
||||||
|
): Promise<OrderSet[]> {
|
||||||
|
const orderSets: OrderSet[] = [];
|
||||||
|
for (const makerAssetData of makerAssetDatas) {
|
||||||
|
for (const takerAssetData of takerAssetDatas) {
|
||||||
|
const orderSet = await this.getOrderSetForAssetsAsync(makerAssetData, takerAssetData);
|
||||||
|
orderSets.push(orderSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return orderSets;
|
||||||
|
}
|
||||||
public async updateAsync(addedRemoved: AddedRemovedOrders): Promise<void> {
|
public async updateAsync(addedRemoved: AddedRemovedOrders): Promise<void> {
|
||||||
const { added, removed, assetPairKey } = addedRemoved;
|
const { added, removed, assetPairKey } = addedRemoved;
|
||||||
const orders = await this.getOrderSetForAssetPairAsync(assetPairKey);
|
const orders = await this.getOrderSetForAssetPairAsync(assetPairKey);
|
||||||
|
@ -84,6 +84,26 @@ export class Orderbook {
|
|||||||
o => o.order.makerAssetData === makerAssetData && o.order.takerAssetData === takerAssetData,
|
o => o.order.makerAssetData === makerAssetData && o.order.takerAssetData === takerAssetData,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
public async getBatchOrdersAsync(makerAssetDatas: string[], takerAssetDatas: string[]): Promise<APIOrder[][]> {
|
||||||
|
for (const [mi, makerAssetData] of makerAssetDatas.entries()) {
|
||||||
|
for (const [ti, takerAssetData] of makerAssetDatas.entries()) {
|
||||||
|
assert.isString(`makerAssetDatas[${mi}]`, makerAssetData);
|
||||||
|
assert.isString(`takerAssetDatas[${ti}]`, takerAssetData);
|
||||||
|
const assetPairKey = OrderStore.getKeyForAssetPair(makerAssetData, takerAssetData);
|
||||||
|
if (!(await this._orderStore.hasAsync(assetPairKey))) {
|
||||||
|
await this._orderProvider.createSubscriptionForAssetPairAsync(makerAssetData, takerAssetData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const orderSets = await this._orderStore.getBatchOrderSetsForAssetsAsync(makerAssetDatas, takerAssetDatas);
|
||||||
|
return orderSets.map(orderSet =>
|
||||||
|
Array.from(orderSet.values()).filter(
|
||||||
|
o =>
|
||||||
|
makerAssetDatas.includes(o.order.makerAssetData) &&
|
||||||
|
takerAssetDatas.includes(o.order.takerAssetData),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Returns all of the Available Asset Pairs for the provided Order Provider.
|
* Returns all of the Available Asset Pairs for the provided Order Provider.
|
||||||
*/
|
*/
|
||||||
|
@ -117,17 +117,11 @@ class CleanCommandExtension(clean):
|
|||||||
)
|
)
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
remove(
|
print(f"Removing {contract}...", end="")
|
||||||
path.join(
|
remove(contract)
|
||||||
"src",
|
print("done")
|
||||||
"zero_ex",
|
|
||||||
"contract_wrappers",
|
|
||||||
contract,
|
|
||||||
"__init__.py",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
print("file not found")
|
||||||
|
|
||||||
|
|
||||||
class TestPublishCommand(distutils.command.build_py.build_py):
|
class TestPublishCommand(distutils.command.build_py.build_py):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user