Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

[asset-buyer] Create new AssetBuyer class #1037

Merged
merged 43 commits into from
Sep 24, 2018

Conversation

BMillman19
Copy link
Contributor

@BMillman19 BMillman19 commented Aug 28, 2018

Description

  • AssetBuyer class

Testing instructions

TBD

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • Prefix PR title with [WIP] if necessary.
  • Prefix PR title with bracketed package name(s) corresponding to the changed package(s). For example: [sol-cov] Fixed bug.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • Add new entries to the relevant CHANGELOG.jsons.

@coveralls
Copy link

coveralls commented Aug 28, 2018

Coverage Status

Coverage increased (+0.005%) to 85.179% when pulling d8d1c98 on feature/forwarder-helper/sra-and-rpc into 8bce407 on development.

@BMillman19 BMillman19 requested review from abandeali1 and dekz August 28, 2018 22:25
*/
async getForwarderHelperForMakerAssetDataAsync(
makerAssetData: string,
takerAddress: string,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this since we do not actually need any on chain info for the taker

const [makerAssetOrderbook, zrxOrderbook] = await Promise.all(
_.map(orderbookRequests, request => sraClient.getOrderbookAsync(request, requestOpts)),
);
// validate orders and find remaining fillable from on chain state or sra api
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we potentially want to filter out orders where the taker is not null address (i.e. not an open order)

}
providerEngine.start();
// create contract wrappers given provider and networkId
const contractWrappers = new ContractWrappers(providerEngine, { networkId });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might be able to extract out the contractWrappers creation into a different factory to make this function a bit shorter, we will also want to use the wrappers to validate orders that don't come from an SRA endpoint

{ baseAssetData: zrxTokenAssetData, quoteAssetData: etherTokenAssetData },
];
const requestOpts = { networkId };
// TODO: try catch these requests and throw a more domain specific error
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

STANDARD_RELAYER_API_ERROR

// if we do have an rpc url, get on-chain orders and traders info via the OrderValidatorWrapper
const ordersFromSra = _.map(makerAssetOrderbook.asks.records, apiOrder => apiOrder.order);
const feeOrdersFromSra = _.map(zrxOrderbook.asks.records, apiOrder => apiOrder.order);
// TODO: try catch these requests and throw a more domain specific error
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ETHEREUM_NODE_RPC_ERROR

// TODO: try catch these requests and throw a more domain specific error
const [makerAssetOrdersAndTradersInfo, feeOrdersAndTradersInfo] = await Promise.all(
_.map([ordersFromSra, feeOrdersFromSra], ordersToBeValidated => {
const takerAddresses = _.map(ordersToBeValidated, () => takerAddress);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change this to NULL_ADDRESS in conjunction w/ removing takerAddress from the function args

): OrdersAndRemainingFillableMakerAssetAmounts {
const result = _.reduce(
apiOrders,
(acc, apiOrder, index) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index is not used here

if (orderUtils.isOrderExpired(order)) {
return acc;
}
// calculate remainingFillableMakerAssetAmount from api metadata
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment about trying to grab remaining taker amount from SRA metadata, else, assume order is completely fillable

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 and continue iterating
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"do not add anything to the accumulations"

},
calculateRemainingMakerAssetAmount(order: SignedOrder, remainingTakerAssetAmount: BigNumber): BigNumber {
const result = remainingTakerAssetAmount.eq(0)
? new BigNumber(0)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants.ZERO_AMOUNT


export const forwarderHelperFactory = {
/**
* Given an array of orders and an array of feeOrders
* Given an array of orders and an array of feeOrders, get a ForwarderHelper
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add checks here for orders all having the same makerAsset and takerAsset

@BMillman19 BMillman19 force-pushed the feature/forwarder-helper/sra-and-rpc branch from 118a8be to 44aa369 Compare September 14, 2018 11:43
@BMillman19 BMillman19 force-pushed the feature/forwarder-helper/sra-and-rpc branch from 44aa369 to d57619b Compare September 15, 2018 12:26
@BMillman19 BMillman19 changed the title [WIP][forwarder-helper]Add getForwarderHelperForMakerAssetDataAsync method to forwarderHelperFactory [WIP][asset-buyer] Create new AssetBuyer class Sep 15, 2018
Copy link
Contributor

@fragosti fragosti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to add StandardRelayerApiAssetBuyerManager

const SLIPPAGE_PERCENTAGE = 0.2; // 20% slippage protection, possibly move this into request interface
const DEFAULT_ORDER_REFRESH_INTERVAL_MS = 10000; // 10 seconds
const DEFAULT_FEE_PERCENTAGE = 0;
const ETHER_TOKEN_DECIMALS = 18;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to constants file

* Defaults to 10000ms (10s).
* @return An instance of AssetBuyer
*/
constructor(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have a factory method for providing your own orders but not your own fee orders (and passing in an sraUrl instead?)

feeOrders: SignedOrderWithRemainingFillableMakerAssetAmount[];
remainingFillableMakerAssetAmounts: BigNumber[];
remainingFillableFeeAmounts: BigNumber[];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need remainingFillableMakerAssetAmounts and remainingFillableFeeAmounts now that we have SignedOrderWithRemainingFillableMakerAssetAmount ?

Seems like we should choose one format over the other.

orders: SignedOrderWithRemainingFillableMakerAssetAmount[],
): SignedOrderWithRemainingFillableMakerAssetAmount[] {
const result = _.filter(orders, order => {
return orderUtils.isOpenOrder(order) && orderUtils.isOrderExpired(order);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably be!orderUtils.isOrderExpired


Provides convenience objects to help work with the Forwarder Contract
Convenience package for buying assets
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please provide a more elaborate description, this is too vague.

private _lastRefreshTimeIfExists?: number;
private _currentOrdersAndFillableAmountsIfExists?: AssetBuyerOrdersAndFillableAmounts;
/**
* Instantiates a new AssetBuyer instance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve this comment and the subsequent method comment, they are identical but these methods are slightly different.

return assetBuyer;
}
/**
* Instantiates a new AssetBuyer instance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto above.

orderRefreshIntervalMs,
);
return assetBuyer;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather then have the above methods be static methods on AssetBuyer, I think it would be clearer to have then as part of a AssetBuyerFactory module. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm personally a fan of static init methods on the class itself but open to changing it if we've established a strong convention of using factories.

}
/**
* Get a BuyQuote containing all information relevant to fulfilling a buy.
* Pass the BuyQuote to executeBuyQuoteAsync to execute the buy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity: You can then pass the `BuyQuote` to `executeBuyQuoteAsync` to execute the buy.

orderValidator?: OrderValidatorWrapper,
): Promise<AssetBuyerOrdersAndFillableAmounts> {
// drop orders that are expired or not open
const filteredTargetOrders = filterOutExpiredAndNonOpenOrders(targetOrderFetcherResponse.orders);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an "expiryBuffer" that users can configure? They might want to be overly cautious, since attempting to fill and expired order will result in them losing gas costs. Since blocks are mined every ~12 seconds, it's hard to predict if an order with 3sec left has a high chance of making it into the next block. Chances are slim but not impossible.

let unsortedFeeOrders = filteredFeeOrders;
// if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts
if (!_.isUndefined(orderValidator)) {
// TODO: critical
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add author.

// if an orderValidator is provided, use on chain information to calculate remaining fillable makerAsset amounts
if (!_.isUndefined(orderValidator)) {
// TODO: critical
// try catch these requests and throw a more domain specific error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the comment means that we should be catching the various potential errors from the smart contract wrappers and returning more useful/asset-buyer specific error messages to the users of asset-buyer. However, I reprioritized this to "improvement"

(acc, orderWithAmount) => {
const { orders, remainingFillableMakerAssetAmounts } = acc;
const { remainingFillableMakerAssetAmount, ...order } = orderWithAmount;
// if we are still missing a remainingFillableMakerAssetAmount, assume the order is completely fillable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which edge-cases would the remainingFillableMakerAssetAmount still be missing at this point?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In practice it wouldn't be, but the type system wants this check since the type can be undefined.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, then this comment feels misleading. I'm not a big fan of _.reduce, you could also write this as a simply _.each loop.

"version": "1.1.0",
"changes": [
{
"note": "Add ObjectMap type",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙏

@fragosti fragosti removed the WIP label Sep 21, 2018
@fragosti fragosti changed the title [WIP][asset-buyer] Create new AssetBuyer class [asset-buyer] Create new AssetBuyer class Sep 21, 2018
Copy link
Contributor

@fabioberger fabioberger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the remaining changes, then LGTM

## @0xproject/asset-buyer

Convenience package for buying assets represented on the Ethereum blockchain using 0x. In its simplest form, the package helps in the usage of the [0x forwarder contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md), which allows users to execute Wrapped Ether based 0x orders without having to set allowances, wrap Ether or buy ZRX, meaning they can buy tokens with Ether alone. Given some liquidity (0x signed orders), it helps estimate the Ether cost of buying a certain asset (giving a range) and then buying that asset.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's link Wrapped Ether to https://weth.io/


In its more advanced and useful form, it integrates with the [Standard Relayer API](https://github.com/0xProject/standard-relayer-api) and takes care of sourcing liquidity for you given an SRA compliant endpoint. The final result is a library that tells you what assets are available, provides an Ether based quote for any asset desired, and allows you to buy that asset using Ether alone.

### Read the [Documentation](https://0xproject.com/docs/asset-buyer).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will there really be a documentation page for this package? If "eventually" is the answer, let's remove this line for now.

"@0xproject/typescript-typings": "^2.0.1",
"@0xproject/utils": "^1.0.9",
"@types/node": "*",
"@0xproject/assert": "^1.0.8",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There has been a publish in the mean-time. This should be ^1.0.9 and others are probably also out-of-date.

@fragosti
Copy link
Contributor

Ok we have determined the test-publish job has been failing because of memory issues. So merging.

@fragosti fragosti merged commit b830c28 into development Sep 24, 2018
@fragosti fragosti deleted the feature/forwarder-helper/sra-and-rpc branch September 24, 2018 15:48
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants