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, SwapQuoteExecutionOpts,
SwapQuoteInfo, SwapQuoteInfo,
SwapQuoteRequestOpts, SwapQuoteRequestOpts,
MarketBuySwapQuote,
MarketSellSwapQuote,
LiquidityForAssetData, LiquidityForAssetData,
LiquidityRequestOpts, LiquidityRequestOpts,
OrdersAndFillableAmounts, OrdersAndFillableAmounts,

View File

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

View File

@ -77,6 +77,12 @@ export interface ExchangeMarketBuySmartContractParams {
signatures: string[]; 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. * 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. * makerAssetFillAmount: The amount of makerAsset to swap for.
@ -101,18 +107,6 @@ export interface ForwarderMarketSellSmartContractParams extends ExchangeMarketSe
feeRecipient: string; 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) * 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. * 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 { export interface MarketSellSwapQuote extends SwapQuote {
takerAssetFillAmount: BigNumber; takerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketSellSwapQuoteInfo; bestCaseQuoteInfo: SwapQuoteInfo;
worstCaseQuoteInfo: MarketSellSwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo;
} }
export interface MarketBuySwapQuote extends SwapQuote { export interface MarketBuySwapQuote extends SwapQuote {
makerAssetFillAmount: BigNumber; makerAssetFillAmount: BigNumber;
bestCaseQuoteInfo: MarketBuySwapQuoteInfo; bestCaseQuoteInfo: SwapQuoteInfo;
worstCaseQuoteInfo: MarketBuySwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo;
} }
export interface SwapQuoteWithAffiliateFee extends SwapQuote { export interface SwapQuoteWithAffiliateFee extends SwapQuote {
@ -209,14 +203,8 @@ export interface SwapQuoteWithAffiliateFee extends SwapQuote {
export interface SwapQuoteInfo { export interface SwapQuoteInfo {
feeTakerTokenAmount: BigNumber; feeTakerTokenAmount: BigNumber;
totalTakerTokenAmount: BigNumber; totalTakerTokenAmount: BigNumber;
}
export interface MarketSellSwapQuoteInfo extends SwapQuoteInfo {
makerTokenAmount: BigNumber;
}
export interface MarketBuySwapQuoteInfo extends SwapQuoteInfo {
takerTokenAmount: BigNumber; takerTokenAmount: BigNumber;
makerTokenAmount: BigNumber;
} }
/** /**

View File

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

View File

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

View File

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