upgraded exchange sell with market sell

This commit is contained in:
David Sun 2019-07-01 14:46:02 -07:00
parent 64a0080616
commit 347c6d02cf
6 changed files with 106 additions and 72 deletions

View File

@ -32,6 +32,8 @@ export {
SwapQuoteExecutionOpts,
SwapQuoteInfo,
SwapQuoteRequestOpts,
MarketBuySwapQuote,
MarketSellSwapQuote,
LiquidityForAssetData,
LiquidityRequestOpts,
OrdersAndFillableAmounts,

View File

@ -8,6 +8,9 @@ import { constants } from '../constants';
import {
CalldataInfo,
ExchangeMarketBuySmartContractParams,
ExchangeMarketSellSmartContractParams,
MarketBuySwapQuote,
MarketSellSwapQuote,
SmartContractParamsInfo,
SwapQuote,
SwapQuoteConsumer,
@ -20,7 +23,7 @@ import { assert } from '../utils/assert';
import { swapQuoteConsumerUtils } from '../utils/swap_quote_consumer_utils';
import { utils } from '../utils/utils';
export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumer<ExchangeMarketBuySmartContractParams> {
export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumer<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams> {
public readonly provider: ZeroExProvider;
public readonly networkId: number;
@ -44,9 +47,20 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumer<ExchangeMark
): Promise<CalldataInfo> {
assert.isValidSwapQuote('quote', quote);
const { params, to, ethAmount, methodAbi } = await this.getSmartContractParamsOrThrowAsync(quote, opts);
const consumableQuote = (quote as any) as (MarketBuySwapQuote | MarketSellSwapQuote);
const smartContractParamsInfo = await this.getSmartContractParamsOrThrowAsync(consumableQuote, opts);
const { to, methodAbi, ethAmount } = smartContractParamsInfo;
const abiEncoder = new AbiEncoder.Method(methodAbi);
const args = [params.orders, params.makerAssetFillAmount, params.signatures];
let args: any[];
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
const marketBuyParams = (smartContractParamsInfo.params as any) as ExchangeMarketBuySmartContractParams;
args = [marketBuyParams.orders, marketBuyParams.makerAssetFillAmount, marketBuyParams.signatures];
} else {
const marketSellParams = (smartContractParamsInfo.params as any) as ExchangeMarketSellSmartContractParams;
args = [marketSellParams.orders, marketSellParams.takerAssetFillAmount, marketSellParams.signatures];
}
const calldataHexString = abiEncoder.encode(args);
return {
calldataHexString,
@ -59,22 +73,45 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumer<ExchangeMark
public async getSmartContractParamsOrThrowAsync(
quote: SwapQuote,
opts: Partial<SwapQuoteGetOutputOpts>,
): Promise<SmartContractParamsInfo<ExchangeMarketBuySmartContractParams>> {
): Promise<SmartContractParamsInfo<ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams>> {
assert.isValidSwapQuote('quote', quote);
const { orders, makerAssetFillAmount } = quote;
const consumableQuote = (quote as any) as (
| MarketBuySwapQuote
| MarketSellSwapQuote);
const { orders } = consumableQuote;
const signatures = _.map(orders, o => o.signature);
const params: ExchangeMarketBuySmartContractParams = {
orders,
signatures,
makerAssetFillAmount,
};
let params: ExchangeMarketBuySmartContractParams | ExchangeMarketSellSmartContractParams;
let methodName: string;
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
const { makerAssetFillAmount } = consumableQuote;
params = {
orders,
signatures,
makerAssetFillAmount,
};
methodName = 'marketBuyOrders';
} else {
const { takerAssetFillAmount } = consumableQuote;
params = {
orders,
signatures,
takerAssetFillAmount,
};
methodName = 'marketSellOrders';
}
const methodAbi = utils.getMethodAbiFromContractAbi(
this._contractWrappers.exchange.abi,
'marketBuyOrdersNoThrow',
methodName,
) as MethodAbi;
return {
@ -102,21 +139,41 @@ export class ExchangeSwapQuoteConsumer implements SwapQuoteConsumer<ExchangeMark
assert.isBigNumber('gasPrice', gasPrice);
}
const { orders, makerAssetFillAmount } = quote;
const consumableQuote = (quote as any) as (
| MarketBuySwapQuote
| MarketSellSwapQuote);
const { orders } = consumableQuote;
const finalTakerAddress = await swapQuoteConsumerUtils.getTakerAddressOrThrowAsync(this.provider, opts);
try {
const txHash = await this._contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
orders,
makerAssetFillAmount,
finalTakerAddress,
{
gasLimit,
gasPrice,
shouldValidate: true,
},
);
let txHash: string;
if (utils.isSwapQuoteMarketBuy(consumableQuote)) {
const { makerAssetFillAmount } = consumableQuote;
txHash = await this._contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
orders,
makerAssetFillAmount,
finalTakerAddress,
{
gasLimit,
gasPrice,
shouldValidate: true,
},
);
} else {
const { takerAssetFillAmount } = consumableQuote;
txHash = await this._contractWrappers.exchange.marketSellOrdersNoThrowAsync(
orders,
takerAssetFillAmount,
finalTakerAddress,
{
gasLimit,
gasPrice,
shouldValidate: true,
},
);
}
return txHash;
} catch (err) {
if (_.includes(err.message, ContractWrappersError.SignatureRequestDenied)) {

View File

@ -77,6 +77,12 @@ export interface ExchangeMarketBuySmartContractParams {
signatures: string[];
}
export interface ExchangeMarketSellSmartContractParams {
orders: SignedOrder[];
takerAssetFillAmount: BigNumber;
signatures: string[];
}
/**
* orders: An array of objects conforming to SignedOrder. These orders can be used to cover the requested assetBuyAmount plus slippage.
* makerAssetFillAmount: The amount of makerAsset to swap for.
@ -101,18 +107,6 @@ export interface ForwarderMarketSellSmartContractParams extends ExchangeMarketSe
feeRecipient: string;
}
export interface ExchangeMarketBuySmartContractParams {
orders: SignedOrder[];
makerAssetFillAmount: BigNumber;
signatures: string[];
}
export interface ExchangeMarketSellSmartContractParams {
orders: SignedOrder[];
takerAssetFillAmount: BigNumber;
signatures: string[];
}
/**
* Interface that varying SwapQuoteConsumers adhere to (exchange consumer, router consumer, forwarder consumer, coordinator consumer)
* getCalldataOrThrow: Get CalldataInfo to swap for tokens with provided SwapQuote. Throws if invalid SwapQuote is provided.
@ -187,14 +181,14 @@ export interface SwapQuote {
export interface MarketSellSwapQuote extends SwapQuote {
takerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketSellSwapQuoteInfo;
worstCaseQuoteInfo: MarketSellSwapQuoteInfo;
bestCaseQuoteInfo: SwapQuoteInfo;
worstCaseQuoteInfo: SwapQuoteInfo;
}
export interface MarketBuySwapQuote extends SwapQuote {
makerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketBuySwapQuoteInfo;
worstCaseQuoteInfo: MarketBuySwapQuoteInfo;
bestCaseQuoteInfo: SwapQuoteInfo;
worstCaseQuoteInfo: SwapQuoteInfo;
}
export interface SwapQuoteWithAffiliateFee extends SwapQuote {
@ -209,14 +203,8 @@ export interface SwapQuoteWithAffiliateFee extends SwapQuote {
export interface SwapQuoteInfo {
feeTakerTokenAmount: BigNumber;
totalTakerTokenAmount: BigNumber;
}
export interface MarketSellSwapQuoteInfo extends SwapQuoteInfo {
makerTokenAmount: BigNumber;
}
export interface MarketBuySwapQuoteInfo extends SwapQuoteInfo {
takerTokenAmount: BigNumber;
makerTokenAmount: BigNumber;
}
/**

View File

@ -42,13 +42,8 @@ export const assert = {
isValidSwapQuoteInfo(variableName: string, swapQuoteInfo: SwapQuoteInfo): void {
sharedAssert.isBigNumber(`${variableName}.feeTakerTokenAmount`, swapQuoteInfo.feeTakerTokenAmount);
sharedAssert.isBigNumber(`${variableName}.totalTakerTokenAmount`, swapQuoteInfo.totalTakerTokenAmount);
if (utils.isSwapQuoteInfoMarketBuy(swapQuoteInfo)) {
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.takerTokenAmount);
} else if (utils.isSwapQuoteInfoMarketSell(swapQuoteInfo)) {
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.makerTokenAmount);
} else {
throw new Error(SwapQuoteConsumerError.InvalidMarketSellOrMarketBuySwapQuote);
}
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.takerTokenAmount);
sharedAssert.isBigNumber(`${variableName}.takerTokenAmount`, swapQuoteInfo.makerTokenAmount);
},
isValidOrderProvider(variableName: string, orderFetcher: OrderProvider): void {
sharedAssert.isFunction(`${variableName}.getOrdersAsync`, orderFetcher.getOrdersAsync);

View File

@ -6,10 +6,9 @@ import { constants } from '../constants';
import { InsufficientAssetLiquidityError } from '../errors';
import {
MarketBuySwapQuote,
MarketBuySwapQuoteInfo,
MarketSellSwapQuote,
MarketSellSwapQuoteInfo,
OrdersAndFillableAmounts,
SwapQuoteInfo,
SwapQuoterError,
} from '../types';
@ -231,19 +230,19 @@ export const swapQuoteCalculator = {
function calculateMarketBuyQuoteInfo(
ordersAndFillableAmounts: OrdersAndFillableAmounts,
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
makerAssetBuyAmount: BigNumber,
makerTokenAmount: BigNumber,
isMakerAssetZrxToken: boolean,
): MarketBuySwapQuoteInfo {
): SwapQuoteInfo {
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
let takerTokenAmount = constants.ZERO_AMOUNT;
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
if (isMakerAssetZrxToken) {
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerAssetBuyAmount);
takerTokenAmount = findTakerTokenAmountNeededToBuyZrx(ordersAndFillableAmounts, makerTokenAmount);
} else {
// find eth and zrx amounts needed to buy
const takerTokenAndZrxAmountToBuyAsset = findTakerTokenAndZrxAmountNeededToBuyAsset(
ordersAndFillableAmounts,
makerAssetBuyAmount,
makerTokenAmount,
);
takerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
const zrxAmountToBuyAsset = takerTokenAndZrxAmountToBuyAsset[1];
@ -256,6 +255,7 @@ function calculateMarketBuyQuoteInfo(
// eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees
const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
return {
makerTokenAmount,
takerTokenAmount,
feeTakerTokenAmount,
totalTakerTokenAmount,
@ -265,22 +265,22 @@ function calculateMarketBuyQuoteInfo(
function calculateMarketSellQuoteInfo(
ordersAndFillableAmounts: OrdersAndFillableAmounts,
feeOrdersAndFillableAmounts: OrdersAndFillableAmounts,
takerAssetSellAmount: BigNumber,
takerTokenAmount: BigNumber,
isMakerAssetZrxToken: boolean,
): MarketSellSwapQuoteInfo {
): SwapQuoteInfo {
// find the total eth and zrx needed to buy assetAmount from the resultOrders from left to right
let makerTokenAmount = constants.ZERO_AMOUNT;
let zrxTakerTokenAmount = constants.ZERO_AMOUNT;
if (isMakerAssetZrxToken) {
makerTokenAmount = findZrxTokenAmountFromSellingTakerTokenAmount(
ordersAndFillableAmounts,
takerAssetSellAmount,
takerTokenAmount,
);
} else {
// find eth and zrx amounts needed to buy
const takerTokenAndZrxAmountToBuyAsset = findMakerTokenAmountReceivedAndZrxAmountNeededToSellAsset(
ordersAndFillableAmounts,
takerAssetSellAmount,
takerTokenAmount,
);
makerTokenAmount = takerTokenAndZrxAmountToBuyAsset[0];
const zrxAmountToSellAsset = takerTokenAndZrxAmountToBuyAsset[1];
@ -291,9 +291,10 @@ function calculateMarketSellQuoteInfo(
const feeTakerTokenAmount = zrxTakerTokenAmount;
// eth amount needed in total is the sum of the amount needed for the asset and the amount needed for fees
const totalTakerTokenAmount = takerAssetSellAmount.plus(feeTakerTokenAmount);
const totalTakerTokenAmount = takerTokenAmount.plus(feeTakerTokenAmount);
return {
makerTokenAmount,
takerTokenAmount,
feeTakerTokenAmount,
totalTakerTokenAmount,
};

View File

@ -6,11 +6,8 @@ import * as _ from 'lodash';
import { constants } from '../constants';
import {
MarketBuySwapQuote,
MarketBuySwapQuoteInfo,
MarketSellSwapQuote,
MarketSellSwapQuoteInfo,
SwapQuote,
SwapQuoteInfo,
} from '../types';
// tslint:disable:no-unnecessary-type-assertion
@ -39,10 +36,4 @@ export const utils = {
isSwapQuoteMarketSell(quote: SwapQuote): quote is MarketSellSwapQuote {
return (quote as MarketBuySwapQuote).makerAssetFillAmount !== undefined;
},
isSwapQuoteInfoMarketBuy(quote: SwapQuoteInfo): quote is MarketBuySwapQuoteInfo {
return (quote as MarketBuySwapQuoteInfo).takerTokenAmount !== undefined;
},
isSwapQuoteInfoMarketSell(quote: SwapQuoteInfo): quote is MarketSellSwapQuoteInfo {
return (quote as MarketSellSwapQuoteInfo).makerTokenAmount !== undefined;
},
};