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

Commit

Permalink
Merge pull request #2491 from 0xProject/fix/instant/support-erc721
Browse files Browse the repository at this point in the history
[FIX] Instant + Asset-swapper support for ERC721
  • Loading branch information
dave4506 authored Mar 1, 2020
2 parents 3b446e8 + da17b49 commit cded58c
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 14 deletions.
13 changes: 13 additions & 0 deletions packages/asset-swapper/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
[
{
"version": "4.4.0",
"changes": [
{
"note": "Add support for ERC721 assets",
"pr": 2491
},
{
"note": "Add destroy for gas heartbeat",
"pr": 2492
}
]
},
{
"version": "4.3.2",
"changes": [
Expand Down
1 change: 1 addition & 0 deletions packages/asset-swapper/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ export enum SwapQuoterError {
InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY',
AssetUnavailable = 'ASSET_UNAVAILABLE',
NoGasPriceProvidedOrEstimated = 'NO_GAS_PRICE_PROVIDED_OR_ESTIMATED',
AssetDataUnsupported = 'ASSET_DATA_UNSUPPORTED',
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { AbiEncoder, BigNumber } from '@0x/utils';

import { constants } from '../../constants';
Expand All @@ -20,6 +21,22 @@ const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstant
export class CreateOrderUtils {
private readonly _contractAddress: ContractAddresses;

// utility function for asset-swapper to ignore market operation utils for specific asset types
public static convertNativeOrderToFullyFillableOptimizedOrders(order: SignedOrder): OptimizedMarketOrder {
return {
...order,
fillableMakerAssetAmount: order.makerAssetAmount,
fillableTakerAssetAmount: order.takerAssetAmount,
fillableTakerFeeAmount: order.takerFee,
fill: {
source: ERC20BridgeSource.Native,
totalMakerAssetAmount: order.makerAssetAmount,
totalTakerAssetAmount: order.takerAssetAmount,
subFills: [],
},
};
}

constructor(contractAddress: ContractAddresses) {
this._contractAddress = contractAddress;
}
Expand Down
42 changes: 30 additions & 12 deletions packages/asset-swapper/src/utils/swap_quote_calculator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { orderCalculationUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { assetDataUtils, orderCalculationUtils } from '@0x/order-utils';
import { AssetProxyId, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';

Expand All @@ -14,10 +14,12 @@ import {
SwapQuoteBase,
SwapQuoteInfo,
SwapQuoteOrdersBreakdown,
SwapQuoterError,
} from '../types';

import { fillableAmountsUtils } from './fillable_amounts_utils';
import { MarketOperationUtils } from './market_operation_utils';
import { CreateOrderUtils } from './market_operation_utils/create_order';
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
import { ProtocolFeeUtils } from './protocol_fee_utils';
import { utils } from './utils';
Expand Down Expand Up @@ -126,6 +128,10 @@ export class SwapQuoteCalculator {
operation: MarketOperation,
opts: CalculateSwapQuoteOpts,
): Promise<SwapQuote> {
// checks if maker asset is ERC721 or ERC20 and taker asset is ERC20
if (!utils.isSupportedAssetDataInOrders(prunedOrders)) {
throw Error(SwapQuoterError.AssetDataUnsupported);
}
// 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();
Expand All @@ -137,18 +143,30 @@ export class SwapQuoteCalculator {
...opts,
fees: _.mapValues(opts.fees, (v, k) => v.times(gasPrice)),
};
if (operation === MarketOperation.Buy) {
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
prunedOrders,
assetFillAmount.plus(slippageBufferAmount),
_opts,

const firstOrderMakerAssetData = !!prunedOrders[0]
? assetDataUtils.decodeAssetDataOrThrow(prunedOrders[0].makerAssetData)
: { assetProxyId: '' };

if (firstOrderMakerAssetData.assetProxyId === AssetProxyId.ERC721) {
// HACK: to conform ERC721 orders to the output of market operation utils, assumes complete fillable
resultOrders = prunedOrders.map(o =>
CreateOrderUtils.convertNativeOrderToFullyFillableOptimizedOrders(o),
);
} else {
resultOrders = await this._marketOperationUtils.getMarketSellOrdersAsync(
prunedOrders,
assetFillAmount.plus(slippageBufferAmount),
_opts,
);
if (operation === MarketOperation.Buy) {
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
prunedOrders,
assetFillAmount.plus(slippageBufferAmount),
_opts,
);
} else {
resultOrders = await this._marketOperationUtils.getMarketSellOrdersAsync(
prunedOrders,
assetFillAmount.plus(slippageBufferAmount),
_opts,
);
}
}
}

Expand Down
17 changes: 16 additions & 1 deletion packages/asset-swapper/src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
import { assetDataUtils } from '@0x/order-utils';
import { AssetData, ERC20AssetData, ERC20BridgeAssetData, Order } from '@0x/types';
import { AssetData, AssetProxyId, ERC20AssetData, ERC20BridgeAssetData, Order, SignedOrder } from '@0x/types';
import { BigNumber, NULL_BYTES } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';

import { constants } from '../constants';

// tslint:disable:no-unnecessary-type-assertion
export const utils = {
isSupportedAssetDataInOrders(orders: SignedOrder[]): boolean {
const firstOrderMakerAssetData = !!orders[0]
? assetDataUtils.decodeAssetDataOrThrow(orders[0].makerAssetData)
: { assetProxyId: '' };
return orders.every(o => {
const takerAssetData = assetDataUtils.decodeAssetDataOrThrow(o.takerAssetData);
const makerAssetData = assetDataUtils.decodeAssetDataOrThrow(o.makerAssetData);
return (
(makerAssetData.assetProxyId === AssetProxyId.ERC20 ||
makerAssetData.assetProxyId === AssetProxyId.ERC721) &&
takerAssetData.assetProxyId === AssetProxyId.ERC20 &&
firstOrderMakerAssetData.assetProxyId === makerAssetData.assetProxyId
); // checks that all native order maker assets are of the same type
});
},
numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber {
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(
percentage,
Expand Down
13 changes: 13 additions & 0 deletions packages/instant/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
[
{
"version": "4.2.0",
"changes": [
{
"note": "Clean up heartbeat functions on close",
"pr": 2492
},
{
"note": "Fix ERC721 asset support",
"pr": 2491
}
]
},
{
"version": "4.1.0",
"changes": [
Expand Down
4 changes: 3 additions & 1 deletion packages/instant/src/components/instant_heading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export class InstantHeading extends React.PureComponent<InstantHeadingProps, {}>
overflow="hidden"
borderRadius="50%"
>
<Image src={asset.metaData.imageUrl} height="100%" objectFit="cover" />
<Flex justify="center" align="center" height="100%">
<Image src={asset.metaData.imageUrl} height="100%" objectFit="cover" />
</Flex>
</Container>
</Flex>
</Container>
Expand Down

0 comments on commit cded58c

Please sign in to comment.