Merge pull request #2491 from 0xProject/fix/instant/support-erc721
[FIX] Instant + Asset-swapper support for ERC721
This commit is contained in:
@@ -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",
|
"version": "4.3.2",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -234,6 +234,7 @@ export enum SwapQuoterError {
|
|||||||
InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY',
|
InsufficientAssetLiquidity = 'INSUFFICIENT_ASSET_LIQUIDITY',
|
||||||
AssetUnavailable = 'ASSET_UNAVAILABLE',
|
AssetUnavailable = 'ASSET_UNAVAILABLE',
|
||||||
NoGasPriceProvidedOrEstimated = 'NO_GAS_PRICE_PROVIDED_OR_ESTIMATED',
|
NoGasPriceProvidedOrEstimated = 'NO_GAS_PRICE_PROVIDED_OR_ESTIMATED',
|
||||||
|
AssetDataUnsupported = 'ASSET_DATA_UNSUPPORTED',
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { ContractAddresses } from '@0x/contract-addresses';
|
import { ContractAddresses } from '@0x/contract-addresses';
|
||||||
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
import { assetDataUtils, generatePseudoRandomSalt } from '@0x/order-utils';
|
||||||
|
import { SignedOrder } from '@0x/types';
|
||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { constants } from '../../constants';
|
import { constants } from '../../constants';
|
||||||
@@ -20,6 +21,22 @@ const { INFINITE_TIMESTAMP_SEC, WALLET_SIGNATURE } = marketOperationUtilConstant
|
|||||||
export class CreateOrderUtils {
|
export class CreateOrderUtils {
|
||||||
private readonly _contractAddress: ContractAddresses;
|
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) {
|
constructor(contractAddress: ContractAddresses) {
|
||||||
this._contractAddress = contractAddress;
|
this._contractAddress = contractAddress;
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { orderCalculationUtils } from '@0x/order-utils';
|
import { assetDataUtils, orderCalculationUtils } from '@0x/order-utils';
|
||||||
import { SignedOrder } from '@0x/types';
|
import { AssetProxyId, SignedOrder } from '@0x/types';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
@@ -14,10 +14,12 @@ import {
|
|||||||
SwapQuoteBase,
|
SwapQuoteBase,
|
||||||
SwapQuoteInfo,
|
SwapQuoteInfo,
|
||||||
SwapQuoteOrdersBreakdown,
|
SwapQuoteOrdersBreakdown,
|
||||||
|
SwapQuoterError,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
import { fillableAmountsUtils } from './fillable_amounts_utils';
|
||||||
import { MarketOperationUtils } from './market_operation_utils';
|
import { MarketOperationUtils } from './market_operation_utils';
|
||||||
|
import { CreateOrderUtils } from './market_operation_utils/create_order';
|
||||||
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
|
import { ERC20BridgeSource, OptimizedMarketOrder } from './market_operation_utils/types';
|
||||||
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
import { ProtocolFeeUtils } from './protocol_fee_utils';
|
||||||
import { utils } from './utils';
|
import { utils } from './utils';
|
||||||
@@ -126,6 +128,10 @@ export class SwapQuoteCalculator {
|
|||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
opts: CalculateSwapQuoteOpts,
|
opts: CalculateSwapQuoteOpts,
|
||||||
): Promise<SwapQuote> {
|
): 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
|
// 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();
|
const slippageBufferAmount = assetFillAmount.multipliedBy(slippagePercentage).integerValue();
|
||||||
@@ -137,18 +143,30 @@ export class SwapQuoteCalculator {
|
|||||||
...opts,
|
...opts,
|
||||||
fees: _.mapValues(opts.fees, (v, k) => v.times(gasPrice)),
|
fees: _.mapValues(opts.fees, (v, k) => v.times(gasPrice)),
|
||||||
};
|
};
|
||||||
if (operation === MarketOperation.Buy) {
|
|
||||||
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
const firstOrderMakerAssetData = !!prunedOrders[0]
|
||||||
prunedOrders,
|
? assetDataUtils.decodeAssetDataOrThrow(prunedOrders[0].makerAssetData)
|
||||||
assetFillAmount.plus(slippageBufferAmount),
|
: { assetProxyId: '' };
|
||||||
_opts,
|
|
||||||
|
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 {
|
} else {
|
||||||
resultOrders = await this._marketOperationUtils.getMarketSellOrdersAsync(
|
if (operation === MarketOperation.Buy) {
|
||||||
prunedOrders,
|
resultOrders = await this._marketOperationUtils.getMarketBuyOrdersAsync(
|
||||||
assetFillAmount.plus(slippageBufferAmount),
|
prunedOrders,
|
||||||
_opts,
|
assetFillAmount.plus(slippageBufferAmount),
|
||||||
);
|
_opts,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resultOrders = await this._marketOperationUtils.getMarketSellOrdersAsync(
|
||||||
|
prunedOrders,
|
||||||
|
assetFillAmount.plus(slippageBufferAmount),
|
||||||
|
_opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { assetDataUtils } from '@0x/order-utils';
|
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 { BigNumber, NULL_BYTES } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
|
|
||||||
@@ -7,6 +7,21 @@ import { constants } from '../constants';
|
|||||||
|
|
||||||
// tslint:disable:no-unnecessary-type-assertion
|
// tslint:disable:no-unnecessary-type-assertion
|
||||||
export const utils = {
|
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 {
|
numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber {
|
||||||
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(
|
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(
|
||||||
percentage,
|
percentage,
|
||||||
|
@@ -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",
|
"version": "4.1.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@@ -72,7 +72,9 @@ export class InstantHeading extends React.PureComponent<InstantHeadingProps, {}>
|
|||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
borderRadius="50%"
|
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>
|
</Container>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
|
Reference in New Issue
Block a user