Added fix for decimals
This commit is contained in:
parent
1588c1f362
commit
83c942ad8c
@ -20,6 +20,7 @@ pragma solidity ^0.5.9;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||
import "@0x/contracts-exchange/contracts/src/interfaces/IExchange.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
|
||||
@ -36,6 +37,19 @@ contract NativeOrderSampler {
|
||||
/// @dev Gas limit for calls to `getOrderFillableTakerAmount()`.
|
||||
uint256 constant internal DEFAULT_CALL_GAS = 200e3; // 200k
|
||||
|
||||
function getTokenDecimals(
|
||||
address makerTokenAddress,
|
||||
address takerTokenAddress
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256, uint256)
|
||||
{
|
||||
uint256 fromTokenDecimals = LibERC20Token.decimals(makerTokenAddress);
|
||||
uint256 toTokenDecimals = LibERC20Token.decimals(takerTokenAddress);
|
||||
return (fromTokenDecimals, toTokenDecimals);
|
||||
}
|
||||
|
||||
/// @dev Queries the fillable taker asset amounts of native orders.
|
||||
/// Effectively ignores orders that have empty signatures or
|
||||
/// maker/taker asset amounts (returning 0).
|
||||
|
@ -382,3 +382,5 @@ export interface SamplerOverrides {
|
||||
overrides: GethCallOverrides;
|
||||
block: BlockParam;
|
||||
}
|
||||
|
||||
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
@ -28,6 +28,7 @@ import {
|
||||
import { findOptimalPathAsync } from './path_optimizer';
|
||||
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
||||
import { SourceFilters } from './source_filters';
|
||||
import { Omit } from '../../types';
|
||||
import {
|
||||
AggregationError,
|
||||
DexSample,
|
||||
@ -41,6 +42,7 @@ import {
|
||||
OrderDomain,
|
||||
TokenAdjacencyGraph,
|
||||
} from './types';
|
||||
import { Web3Wrapper } from '@0x/dev-utils';
|
||||
|
||||
// tslint:disable:boolean-naming
|
||||
|
||||
@ -153,6 +155,7 @@ export class MarketOperationUtils {
|
||||
|
||||
// Call the sampler contract.
|
||||
const samplerPromise = this._sampler.executeAsync(
|
||||
this._sampler.getTokenDecimals(makerToken, takerToken),
|
||||
// Get native order fillable amounts.
|
||||
this._sampler.getOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchange),
|
||||
// Get ETH -> maker token price.
|
||||
@ -205,11 +208,12 @@ export class MarketOperationUtils {
|
||||
: Promise.resolve([]);
|
||||
|
||||
const [
|
||||
[orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
||||
[tokenDecimals, orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
||||
offChainBalancerQuotes,
|
||||
offChainBancorQuotes,
|
||||
] = await Promise.all([samplerPromise, offChainBalancerPromise, offChainBancorPromise]);
|
||||
|
||||
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
||||
return {
|
||||
side: MarketOperation.Sell,
|
||||
inputAmount: takerAmount,
|
||||
@ -223,6 +227,8 @@ export class MarketOperationUtils {
|
||||
rfqtIndicativeQuotes: [],
|
||||
twoHopQuotes,
|
||||
quoteSourceFilters,
|
||||
makerTokenDecimals: makerTokenDecimals.toNumber(),
|
||||
takerTokenDecimals: takerTokenDecimals.toNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -259,6 +265,7 @@ export class MarketOperationUtils {
|
||||
|
||||
// Call the sampler contract.
|
||||
const samplerPromise = this._sampler.executeAsync(
|
||||
this._sampler.getTokenDecimals(makerToken, takerToken),
|
||||
// Get native order fillable amounts.
|
||||
this._sampler.getOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchange),
|
||||
// Get ETH -> makerToken token price.
|
||||
@ -306,13 +313,14 @@ export class MarketOperationUtils {
|
||||
: Promise.resolve([]);
|
||||
|
||||
const [
|
||||
[orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
||||
[tokenDecimals, orderFillableAmounts, ethToMakerAssetRate, ethToTakerAssetRate, dexQuotes, twoHopQuotes],
|
||||
offChainBalancerQuotes,
|
||||
] = await Promise.all([samplerPromise, offChainBalancerPromise]);
|
||||
// Attach the MultiBridge address to the sample fillData
|
||||
(dexQuotes.find(quotes => quotes[0] && quotes[0].source === ERC20BridgeSource.MultiBridge) || []).forEach(
|
||||
q => (q.fillData = { poolAddress: this._multiBridge }),
|
||||
);
|
||||
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
|
||||
return {
|
||||
side: MarketOperation.Buy,
|
||||
inputAmount: makerAmount,
|
||||
@ -326,6 +334,8 @@ export class MarketOperationUtils {
|
||||
rfqtIndicativeQuotes: [],
|
||||
twoHopQuotes,
|
||||
quoteSourceFilters,
|
||||
makerTokenDecimals: makerTokenDecimals.toNumber(),
|
||||
takerTokenDecimals: takerTokenDecimals.toNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -462,7 +472,7 @@ export class MarketOperationUtils {
|
||||
}
|
||||
|
||||
public async _generateOptimizedOrdersAsync(
|
||||
marketSideLiquidity: MarketSideLiquidity,
|
||||
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
||||
opts: GenerateOptimizedOrdersOpts,
|
||||
): Promise<OptimizerResult> {
|
||||
const {
|
||||
@ -596,8 +606,10 @@ export class MarketOperationUtils {
|
||||
if (optimizerResult) {
|
||||
const totalMakerAmount = BigNumber.sum(...optimizerResult.optimizedOrders.map(order => order.makerAssetAmount));
|
||||
const totalTakerAmount = BigNumber.sum(...optimizerResult.optimizedOrders.map(order => order.takerAssetAmount));
|
||||
if (totalTakerAmount.gt(0)) {
|
||||
comparisonPrice = totalMakerAmount.div(totalTakerAmount);
|
||||
if (totalMakerAmount.gt(0)) {
|
||||
const totalMakerAmountUnitAmount = Web3Wrapper.toUnitAmount(totalMakerAmount, marketSideLiquidity.makerTokenDecimals);
|
||||
const totalTakerAmountUnitAmount = Web3Wrapper.toUnitAmount(totalTakerAmount, marketSideLiquidity.takerTokenDecimals);
|
||||
comparisonPrice = totalTakerAmountUnitAmount.div(totalMakerAmountUnitAmount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import * as _ from 'lodash';
|
||||
import { ZERO_AMOUNT } from './constants';
|
||||
import { getTwoHopAdjustedRate } from './fills';
|
||||
import { DexSample, FeeSchedule, MarketSideLiquidity, MultiHopFillData, TokenAdjacencyGraph } from './types';
|
||||
import { Omit } from '../../types';
|
||||
|
||||
/**
|
||||
* Given a token pair, returns the intermediate tokens to consider for two-hop routes.
|
||||
@ -34,7 +35,7 @@ export function getIntermediateTokens(
|
||||
* Returns the best two-hop quote and the fee-adjusted rate of that quote.
|
||||
*/
|
||||
export function getBestTwoHopQuote(
|
||||
marketSideLiquidity: MarketSideLiquidity,
|
||||
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
||||
feeSchedule?: FeeSchedule,
|
||||
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
|
||||
const { side, inputAmount, ethToOutputRate, twoHopQuotes } = marketSideLiquidity;
|
||||
|
@ -88,6 +88,15 @@ export class SamplerOperations {
|
||||
return this._bancorService;
|
||||
}
|
||||
|
||||
public getTokenDecimals(makerTokenAddress: string, takerTokenAddress: string): BatchedOperation<BigNumber[]> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.Native,
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.getTokenDecimals,
|
||||
params: [makerTokenAddress, takerTokenAddress],
|
||||
});
|
||||
}
|
||||
|
||||
public getOrderFillableTakerAmounts(orders: SignedOrder[], exchangeAddress: string): BatchedOperation<BigNumber[]> {
|
||||
return new SamplerContractOperation({
|
||||
source: ERC20BridgeSource.Native,
|
||||
|
@ -351,6 +351,8 @@ export interface MarketSideLiquidity {
|
||||
rfqtIndicativeQuotes: RFQTIndicativeQuote[];
|
||||
twoHopQuotes: Array<DexSample<MultiHopFillData>>;
|
||||
quoteSourceFilters: SourceFilters;
|
||||
makerTokenDecimals: number;
|
||||
takerTokenDecimals: number;
|
||||
}
|
||||
|
||||
export interface TokenAdjacencyGraph {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
||||
import {
|
||||
assertIntegerRoughlyEquals,
|
||||
blockchainTests,
|
||||
@ -130,6 +131,36 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
|
||||
.awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
describe('getTokenDecimals()', () => {
|
||||
it('correctly returns the token balances', async () => {
|
||||
const newMakerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
new BigNumber(18),
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
const newTakerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
|
||||
erc20Artifacts.DummyERC20Token,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
constants.DUMMY_TOKEN_NAME,
|
||||
constants.DUMMY_TOKEN_SYMBOL,
|
||||
new BigNumber(6),
|
||||
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
|
||||
);
|
||||
const [makerDecimals, takerDecimals] = await testContract
|
||||
.getTokenDecimals(newMakerToken.address, newTakerToken.address)
|
||||
.callAsync();
|
||||
expect(makerDecimals.toString()).to.eql('18');
|
||||
expect(takerDecimals.toString()).to.eql('6');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOrderFillableTakerAmount()', () => {
|
||||
it('returns the full amount for a fully funded order', async () => {
|
||||
const order = createOrder();
|
||||
|
@ -385,6 +385,10 @@ describe('MarketOperationUtils tests', () => {
|
||||
};
|
||||
|
||||
const DEFAULT_OPS = {
|
||||
getTokenDecimals(_makerAddress: string, _takerAddress: string): BigNumber[] {
|
||||
const result = new BigNumber(18);
|
||||
return [result, result];
|
||||
},
|
||||
getOrderFillableTakerAmounts(orders: SignedOrder[]): BigNumber[] {
|
||||
return orders.map(o => o.takerAssetAmount);
|
||||
},
|
||||
@ -737,7 +741,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
signedOrder: createOrder({
|
||||
makerAssetData: MAKER_ASSET_DATA,
|
||||
takerAssetData: TAKER_ASSET_DATA,
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(321, 18),
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(321, 6),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
|
||||
}),
|
||||
},
|
||||
@ -764,7 +768,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
return {
|
||||
dexQuotes: [],
|
||||
ethToInputRate: Web3Wrapper.toBaseUnitAmount(1, 18),
|
||||
ethToOutputRate: Web3Wrapper.toBaseUnitAmount(1, 18),
|
||||
ethToOutputRate: Web3Wrapper.toBaseUnitAmount(1, 6),
|
||||
inputAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
|
||||
inputToken: MAKER_TOKEN,
|
||||
outputToken: TAKER_TOKEN,
|
||||
@ -772,7 +776,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
createOrder({
|
||||
makerAssetData: MAKER_ASSET_DATA,
|
||||
takerAssetData: TAKER_ASSET_DATA,
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(320, 18),
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(320, 6),
|
||||
takerAssetAmount: Web3Wrapper.toBaseUnitAmount(1, 18),
|
||||
}),
|
||||
],
|
||||
@ -781,6 +785,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
side: MarketOperation.Sell,
|
||||
twoHopQuotes: [],
|
||||
quoteSourceFilters: new SourceFilters(),
|
||||
makerTokenDecimals: 6,
|
||||
takerTokenDecimals: 18,
|
||||
};
|
||||
})
|
||||
const result = await mockedMarketOpUtils.object.getMarketSellOrdersAsync(ORDERS, Web3Wrapper.toBaseUnitAmount(1, 18), {
|
||||
@ -796,8 +802,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
}
|
||||
});
|
||||
expect(result.optimizedOrders.length).to.eql(1);
|
||||
expect(requestedComparisonPrice!.toString()).to.eql("320");
|
||||
expect(result.optimizedOrders[0].makerAssetAmount.toString()).to.eql('321000000000000000000');
|
||||
expect(requestedComparisonPrice!.toString()).to.eql("0.003125");
|
||||
expect(result.optimizedOrders[0].makerAssetAmount.toString()).to.eql('321000000');
|
||||
expect(result.optimizedOrders[0].takerAssetAmount.toString()).to.eql('1000000000000000000');
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user