Implement StandardRelayerAPIOrderFetcher
This commit is contained in:
@@ -1,262 +0,0 @@
|
||||
// import { assert } from '@0xproject/assert';
|
||||
// import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect';
|
||||
// import { ContractWrappers, OrderAndTraderInfo, OrderStatus } from '@0xproject/contract-wrappers';
|
||||
// import { schemas } from '@0xproject/json-schemas';
|
||||
// import { assetDataUtils } from '@0xproject/order-utils';
|
||||
// import { RemainingFillableCalculator } from '@0xproject/order-utils/lib/src/remaining_fillable_calculator';
|
||||
// import { RPCSubprovider, Web3ProviderEngine } from '@0xproject/subproviders';
|
||||
// import { SignedOrder } from '@0xproject/types';
|
||||
// import { BigNumber } from '@0xproject/utils';
|
||||
// import * as _ from 'lodash';
|
||||
|
||||
// import { constants } from './constants';
|
||||
// import { ForwarderHelperImpl, ForwarderHelperImplConfig } from '@0xproject/asset-buyer/src/asset_buyer';
|
||||
// import { ForwarderHelper, ForwarderHelperFactoryError } from './types';
|
||||
// import { orderUtils } from './utils/order_utils';
|
||||
|
||||
// export const forwarderHelperFactory = {
|
||||
// /**
|
||||
// * Given an array of orders and an array of feeOrders, get a ForwarderHelper
|
||||
// * @param orders An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData
|
||||
// * @param feeOrders An array of objects conforming to SignedOrder. Each order should specify ZRX as makerAssetData WETH as takerAssetData
|
||||
// * @return A ForwarderHelper, see type for definition
|
||||
// */
|
||||
// getForwarderHelperForOrders(orders: SignedOrder[], feeOrders: SignedOrder[] = []): ForwarderHelper {
|
||||
// assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
|
||||
// assert.doesConformToSchema('feeOrders', orders, schemas.signedOrdersSchema);
|
||||
// // TODO: Add assertion here for orders all having the same makerAsset and takerAsset
|
||||
// const config: ForwarderHelperImplConfig = {
|
||||
// orders,
|
||||
// feeOrders,
|
||||
// };
|
||||
// const helper = new ForwarderHelperImpl(config);
|
||||
// return helper;
|
||||
// },
|
||||
// /**
|
||||
// * Given a desired makerAsset and SRA url, get a ForwarderHelper
|
||||
// * @param makerAssetData An array of objects conforming to SignedOrder. Each order should specify the same makerAssetData and takerAssetData
|
||||
// * @param sraUrl A url pointing to an SRA v2 compliant endpoint.
|
||||
// * @param rpcUrl A url pointing to an ethereum node.
|
||||
// * @param networkId The ethereum networkId, defaults to 1 (mainnet).
|
||||
// * @return A ForwarderHelper, see type for definition
|
||||
// */
|
||||
// async getForwarderHelperForMakerAssetDataAsync(
|
||||
// makerAssetData: string,
|
||||
// sraUrl: string,
|
||||
// rpcUrl?: string,
|
||||
// networkId: number = 1,
|
||||
// ): Promise<ForwarderHelper> {
|
||||
// assert.isHexString('makerAssetData', makerAssetData);
|
||||
// assert.isWebUri('sraUrl', sraUrl);
|
||||
// if (!_.isUndefined(rpcUrl)) {
|
||||
// assert.isWebUri('rpcUrl', rpcUrl);
|
||||
// }
|
||||
// assert.isNumber('networkId', networkId);
|
||||
// // create provider
|
||||
// const providerEngine = new Web3ProviderEngine();
|
||||
// if (!_.isUndefined(rpcUrl)) {
|
||||
// providerEngine.addProvider(new RPCSubprovider(rpcUrl));
|
||||
// }
|
||||
// providerEngine.start();
|
||||
// // create contract wrappers given provider and networkId
|
||||
// const contractWrappers = new ContractWrappers(providerEngine, { networkId });
|
||||
// // find ether token asset data
|
||||
// const etherTokenAddressIfExists = contractWrappers.etherToken.getContractAddressIfExists();
|
||||
// if (_.isUndefined(etherTokenAddressIfExists)) {
|
||||
// throw new Error(ForwarderHelperFactoryError.NoEtherTokenContractFound);
|
||||
// }
|
||||
// const etherTokenAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddressIfExists);
|
||||
// // find zrx token asset data
|
||||
// let zrxTokenAssetData: string;
|
||||
// try {
|
||||
// zrxTokenAssetData = contractWrappers.exchange.getZRXAssetData();
|
||||
// } catch (err) {
|
||||
// throw new Error(ForwarderHelperFactoryError.NoZrxTokenContractFound);
|
||||
// }
|
||||
// // get orderbooks for makerAsset/WETH and ZRX/WETH
|
||||
// const sraClient = new HttpClient(sraUrl);
|
||||
// const orderbookRequests = [
|
||||
// { baseAssetData: makerAssetData, quoteAssetData: etherTokenAssetData },
|
||||
// { baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData },
|
||||
// ];
|
||||
// const requestOpts = { networkId };
|
||||
// let makerAssetOrderbook: OrderbookResponse;
|
||||
// let zrxOrderbook: OrderbookResponse;
|
||||
// try {
|
||||
// [makerAssetOrderbook, zrxOrderbook] = await Promise.all(
|
||||
// _.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)),
|
||||
// );
|
||||
// } catch (err) {
|
||||
// throw new Error(ForwarderHelperFactoryError.StandardRelayerApiError);
|
||||
// }
|
||||
|
||||
// // validate orders and find remaining fillable from on chain state or sra api
|
||||
// let ordersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts;
|
||||
// let feeOrdersAndRemainingFillableMakerAssetAmounts: OrdersAndRemainingFillableMakerAssetAmounts;
|
||||
// if (!_.isUndefined(rpcUrl)) {
|
||||
// // if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper
|
||||
// const ordersFromSra = getOpenAsksFromOrderbook(makerAssetOrderbook);
|
||||
// const feeOrdersFromSra = getOpenAsksFromOrderbook(zrxOrderbook);
|
||||
// // TODO: try catch these requests and throw a more domain specific error
|
||||
// // TODO: optimization, reduce this to once RPC call buy combining orders into one array and then splitting up the response
|
||||
// const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all(
|
||||
// _.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => {
|
||||
// const takerAddresses = _.map(ordersToBeValidated, () => constants.NULL_ADDRESS);
|
||||
// return contractWrappers.orderValidator.getOrdersAndTradersInfoAsync(
|
||||
// ordersToBeValidated,
|
||||
// takerAddresses,
|
||||
// );
|
||||
// }),
|
||||
// );
|
||||
// // take maker asset orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts
|
||||
// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain(
|
||||
// ordersFromSra,
|
||||
// makerAssetOrdersAndTradersInfo,
|
||||
// zrxTokenAssetData,
|
||||
// );
|
||||
// // take fee orders from SRA + on chain information and find the valid orders and remaining fillable maker asset amounts
|
||||
// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain(
|
||||
// feeOrdersFromSra,
|
||||
// feeOrdersAndTradersInfo,
|
||||
// zrxTokenAssetData,
|
||||
// );
|
||||
// } else {
|
||||
// // if we don't have an rpc url, assume all orders are valid and fallback to optional fill amounts from SRA
|
||||
// // if fill amounts are not available from the SRA, assume all orders are completely fillable
|
||||
// const apiOrdersFromSra = makerAssetOrderbook.asks.records;
|
||||
// const feeApiOrdersFromSra = zrxOrderbook.asks.records;
|
||||
// // take maker asset orders from SRA and the valid orders and remaining fillable maker asset amounts
|
||||
// ordersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi(
|
||||
// apiOrdersFromSra,
|
||||
// );
|
||||
// // take fee orders from SRA and find the valid orders and remaining fillable maker asset amounts
|
||||
// feeOrdersAndRemainingFillableMakerAssetAmounts = getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi(
|
||||
// feeApiOrdersFromSra,
|
||||
// );
|
||||
// }
|
||||
// // compile final config
|
||||
// const config: ForwarderHelperImplConfig = {
|
||||
// orders: ordersAndRemainingFillableMakerAssetAmounts.orders,
|
||||
// feeOrders: feeOrdersAndRemainingFillableMakerAssetAmounts.orders,
|
||||
// remainingFillableMakerAssetAmounts:
|
||||
// ordersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts,
|
||||
// remainingFillableFeeAmounts:
|
||||
// feeOrdersAndRemainingFillableMakerAssetAmounts.remainingFillableMakerAssetAmounts,
|
||||
// };
|
||||
// const helper = new ForwarderHelperImpl(config);
|
||||
// return helper;
|
||||
// },
|
||||
// };
|
||||
|
||||
// interface OrdersAndRemainingFillableMakerAssetAmounts {
|
||||
// orders: SignedOrder[];
|
||||
// remainingFillableMakerAssetAmounts: BigNumber[];
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Given an array of APIOrder objects from a standard relayer api, return an array
|
||||
// * of fillable orders with their corresponding remainingFillableMakerAssetAmounts
|
||||
// */
|
||||
// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromApi(
|
||||
// apiOrders: APIOrder[],
|
||||
// ): OrdersAndRemainingFillableMakerAssetAmounts {
|
||||
// const result = _.reduce(
|
||||
// apiOrders,
|
||||
// (acc, apiOrder) => {
|
||||
// // get current accumulations
|
||||
// const { orders, remainingFillableMakerAssetAmounts } = acc;
|
||||
// // get order and metadata
|
||||
// const { order, metaData } = apiOrder;
|
||||
// // if the order is expired or not open, move on
|
||||
// if (orderUtils.isOrderExpired(order) || !orderUtils.isOpenOrder(order)) {
|
||||
// return acc;
|
||||
// }
|
||||
// // calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable
|
||||
// const remainingFillableTakerAssetAmount = _.get(
|
||||
// metaData,
|
||||
// 'remainingTakerAssetAmount',
|
||||
// order.takerAssetAmount,
|
||||
// );
|
||||
// const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
|
||||
// order,
|
||||
// remainingFillableTakerAssetAmount,
|
||||
// );
|
||||
// // if there is some amount of maker asset left to fill and add the order and remaining amount to the accumulations
|
||||
// // if there is not any maker asset left to fill, do not add
|
||||
// if (remainingFillableMakerAssetAmount.gt(constants.ZERO_AMOUNT)) {
|
||||
// return {
|
||||
// orders: _.concat(orders, order),
|
||||
// remainingFillableMakerAssetAmounts: _.concat(
|
||||
// remainingFillableMakerAssetAmounts,
|
||||
// remainingFillableMakerAssetAmount,
|
||||
// ),
|
||||
// };
|
||||
// } else {
|
||||
// return acc;
|
||||
// }
|
||||
// },
|
||||
// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] },
|
||||
// );
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Given an array of orders and corresponding on-chain infos, return a subset of the orders
|
||||
// * that are still fillable orders with their corresponding remainingFillableMakerAssetAmounts
|
||||
// */
|
||||
// function getValidOrdersAndRemainingFillableMakerAssetAmountsFromOnChain(
|
||||
// inputOrders: SignedOrder[],
|
||||
// ordersAndTradersInfo: OrderAndTraderInfo[],
|
||||
// zrxAssetData: string,
|
||||
// ): OrdersAndRemainingFillableMakerAssetAmounts {
|
||||
// // iterate through the input orders and find the ones that are still fillable
|
||||
// // for the orders that are still fillable, calculate the remaining fillable maker asset amount
|
||||
// const result = _.reduce(
|
||||
// inputOrders,
|
||||
// (acc, order, index) => {
|
||||
// // get current accumulations
|
||||
// const { orders, remainingFillableMakerAssetAmounts } = acc;
|
||||
// // get corresponding on-chain state for the order
|
||||
// const { orderInfo, traderInfo } = ordersAndTradersInfo[index];
|
||||
// // if the order IS NOT fillable, do not add anything to the accumulations and continue iterating
|
||||
// if (orderInfo.orderStatus !== OrderStatus.FILLABLE) {
|
||||
// return acc;
|
||||
// }
|
||||
// // if the order IS fillable, add the order and calculate the remaining fillable amount
|
||||
// const transferrableAssetAmount = BigNumber.min([traderInfo.makerAllowance, traderInfo.makerBalance]);
|
||||
// const transferrableFeeAssetAmount = BigNumber.min([
|
||||
// traderInfo.makerZrxAllowance,
|
||||
// traderInfo.makerZrxBalance,
|
||||
// ]);
|
||||
// const remainingTakerAssetAmount = order.takerAssetAmount.minus(orderInfo.orderTakerAssetFilledAmount);
|
||||
// const remainingMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
|
||||
// order,
|
||||
// remainingTakerAssetAmount,
|
||||
// );
|
||||
// const remainingFillableCalculator = new RemainingFillableCalculator(
|
||||
// order.makerFee,
|
||||
// order.makerAssetAmount,
|
||||
// order.makerAssetData === zrxAssetData,
|
||||
// transferrableAssetAmount,
|
||||
// transferrableFeeAssetAmount,
|
||||
// remainingMakerAssetAmount,
|
||||
// );
|
||||
// const remainingFillableAmount = remainingFillableCalculator.computeRemainingFillable();
|
||||
// return {
|
||||
// orders: _.concat(orders, order),
|
||||
// remainingFillableMakerAssetAmounts: _.concat(
|
||||
// remainingFillableMakerAssetAmounts,
|
||||
// remainingFillableAmount,
|
||||
// ),
|
||||
// };
|
||||
// },
|
||||
// { orders: [] as SignedOrder[], remainingFillableMakerAssetAmounts: [] as BigNumber[] },
|
||||
// );
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// function getOpenAsksFromOrderbook(orderbookResponse: OrderbookResponse): SignedOrder[] {
|
||||
// const asks = _.map(orderbookResponse.asks.records, apiOrder => apiOrder.order);
|
||||
// const result = _.filter(asks, ask => orderUtils.isOpenOrder(ask));
|
||||
// return result;
|
||||
// }
|
||||
@@ -0,0 +1,80 @@
|
||||
import { APIOrder, HttpClient, OrderbookResponse } from '@0xproject/connect';
|
||||
import { SignedOrder } from '@0xproject/types';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { constants } from '../constants';
|
||||
import {
|
||||
AssetBuyerError,
|
||||
OrderFetcher,
|
||||
OrderFetcherRequest,
|
||||
OrderFetcherResponse,
|
||||
SignedOrderWithRemainingFillableMakerAssetAmount,
|
||||
} from '../types';
|
||||
import { assert } from '../utils/assert';
|
||||
import { orderUtils } from '../utils/order_utils';
|
||||
|
||||
export class StandardRelayerAPIOrderFetcher implements OrderFetcher {
|
||||
public readonly apiUrl: string;
|
||||
private _sraClient: HttpClient;
|
||||
/**
|
||||
* Given an array of APIOrder objects from a standard relayer api, return an array
|
||||
* of SignedOrderWithRemainingFillableMakerAssetAmounts
|
||||
*/
|
||||
private static _getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
|
||||
apiOrders: APIOrder[],
|
||||
): SignedOrderWithRemainingFillableMakerAssetAmount[] {
|
||||
const result = _.map(apiOrders, apiOrder => {
|
||||
const { order, metaData } = apiOrder;
|
||||
// calculate remainingFillableMakerAssetAmount from api metadata, else assume order is completely fillable
|
||||
const remainingFillableTakerAssetAmount = _.get(
|
||||
metaData,
|
||||
'remainingTakerAssetAmount',
|
||||
order.takerAssetAmount,
|
||||
);
|
||||
const remainingFillableMakerAssetAmount = orderUtils.calculateRemainingMakerAssetAmount(
|
||||
order,
|
||||
remainingFillableTakerAssetAmount,
|
||||
);
|
||||
const newOrder = {
|
||||
...order,
|
||||
remainingFillableMakerAssetAmount,
|
||||
};
|
||||
return newOrder;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Instantiates a new StandardRelayerAPIOrderFetcher instance
|
||||
* @param apiUrl The relayer API base HTTP url you would like to interact with.
|
||||
* @return An instance of StandardRelayerAPIOrderFetcher
|
||||
*/
|
||||
constructor(apiUrl: string) {
|
||||
assert.isWebUri('apiUrl', apiUrl);
|
||||
this.apiUrl = apiUrl;
|
||||
this._sraClient = new HttpClient(apiUrl);
|
||||
}
|
||||
/**
|
||||
* Given an object that conforms to OrderFetcherRequest, return the corresponding OrderFetcherResponse that satisfies the request.
|
||||
* @param orderFetchRequest An instance of OrderFetcherRequest. See type for more information.
|
||||
* @return An instance of OrderFetcherResponse. See type for more information.
|
||||
*/
|
||||
public async fetchOrdersAsync(orderFetchRequest: OrderFetcherRequest): Promise<OrderFetcherResponse> {
|
||||
const { makerAssetData, takerAssetData, networkId } = orderFetchRequest;
|
||||
const orderbookRequest = { baseAssetData: makerAssetData, quoteAssetData: takerAssetData };
|
||||
const requestOpts = { networkId };
|
||||
let orderbook: OrderbookResponse;
|
||||
try {
|
||||
orderbook = await this._sraClient.getOrderbookAsync(orderbookRequest, requestOpts);
|
||||
} catch (err) {
|
||||
throw new Error(AssetBuyerError.StandardRelayerApiError);
|
||||
}
|
||||
const apiOrders = orderbook.asks.records;
|
||||
const orders = StandardRelayerAPIOrderFetcher._getSignedOrderWithRemainingFillableMakerAssetAmountFromApi(
|
||||
apiOrders,
|
||||
);
|
||||
return {
|
||||
orders,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -165,6 +165,10 @@ function unbundleOrdersWithAmounts(
|
||||
const { remainingFillableMakerAssetAmount, ...order } = orderWithAmount;
|
||||
// if we are still missing a remainingFillableMakerAssetAmount, assume the order is completely fillable
|
||||
const newRemainingAmount = remainingFillableMakerAssetAmount || order.makerAssetAmount;
|
||||
// if remaining amount is less than or equal to zero, do not add it
|
||||
if (newRemainingAmount.lte(constants.ZERO_AMOUNT)) {
|
||||
return acc;
|
||||
}
|
||||
const newAcc = {
|
||||
orders: _.concat(orders, order),
|
||||
remainingFillableMakerAssetAmounts: _.concat(remainingFillableMakerAssetAmounts, newRemainingAmount),
|
||||
|
||||
Reference in New Issue
Block a user