feat: v4 final (#136)

* v4 FillQuoteTransformer (#104)

* Update FQT to support v4 orders

* `@0x/contracts-zero-ex`: Tweak FQT
`@0x/contracts-zero-ex`: Drop `ERC20BridgeTransfer` event and add `PartialQuoteFill` event.

* `@0x/contracts-utils`: Add `LibSafeMathV06.downcastToUint128()`

* `@0x/protocol-utils`: Update transformer utils for V4 FQT

* `@0x/contracts-zero-ex`: Fixing FQT tests...

* `@0x/contracts-zero-ex`: rename FQT bridge event

* `@0x/contracts-zero-ex`: Un-`only` tests

* `@0x/migrations`: Update `BridgeAdapter` deployment

* `@0x/contracts-integrations`: Delete `mtx_tests`

* `@0x/protocol-utils`: Address review comments

* `@0x/contracts-zero-ex`: Address review comments

* `@0x/migrations`: Update migrations

Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>

* v4: Asset-swapper (main branch) (#113)

* refactor quote_requestor

* WIP v4/asset-swapper: Clean up SwapQuoter and remove @0x/orderbook

* Start replacing SignedOrder everywhere

* wip: new order type

* wip

* remove order-utils from most places

* hack: Play around with VerboseX types (#119)

* hack: Play around with VerboseX types

* More hacks

* Fix up the bridgeData encodings

* Rework Orderbook return type

* feat: Don't charge a protocol fee for RFQ orders WIP (#121)

* fix simple build errors

* simplify types a little

* remove SwapQuoteCalculator: unnecessary abstraction

* Fix all ./src build errors; make types consistent

* export more types for use in 0x API; modify Orderbook interface

* stop overriding APIOrder

* feat: RFQ v4 + consolidated bridge encoders (#125)

* feat: check if taker address is contract

* Rework bridge data

* Worst case adjustments

* RFQT v4

* Future/v4 validate orders (#126)

* RFQT v4

* v4 validate native orders

* use default invalid signature

* refactor rfqt validations in swap quoter

* fix types

* fix RFQT unlisted api key

* remove priceAwareRFQFlag

* adjust maker/taker amounts

* update JSON schemas

* filter zero fillable orders

Co-authored-by: xianny <xianny@gmail.com>

* fix type export

Co-authored-by: xianny <xianny@gmail.com>

* remove order-utils as much as possible

* work on tests compile

* Comment out quote reporter test

* updated tests

* restore order-utils accidental changes

* some lints

* Remove old fill_test

* ts lint disable for now

* update quote report

* Re-enable quote report tests

* make fill data required field

* fix lint

* type guards

* force fillData as required

* fix lint

* fix naming

* exports

* adjust MultiBridge by slippage

* cleanups (checkpoint 1)

* cleanup types (checkpoint #2)

* remove unused deps

* `@0x/contract-addresses`: Deploy new FQT (#129)

Co-authored-by: Lawrence Forman <me@merklejerk.com>

* commit bump to republish

* DRY up the rfqt mocker

* fix: Balancer load top pools (#131)

* fix: Balancer load top 250 pools

* refetch top pools on an interval

Co-authored-by: Jacob Evans <jacob@dekz.net>
Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>

* Update post rebase

* prettier

* Remove test helpers exported in asset-swapper

* Clean up from review comments

* prettier

* lint

* recreate rfqt mocker

* change merge and INVALID_SIGNATURE

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: Xianny <8582774+xianny@users.noreply.github.com>
Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
This commit is contained in:
Jacob Evans
2021-02-10 19:20:15 +10:00
committed by GitHub
parent 501b7b9b65
commit 3f4bb933d1
115 changed files with 5654 additions and 9332 deletions

View File

@@ -57,4 +57,76 @@ blockchainTests.resets('BalanceChecker contract', env => {
}
});
});
describe('getMinOfBalancesOrAllowances', () => {
it('returns the balance if the allowance can cover it', async () => {
const makerToken = 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 accounts = await web3Wrapper.getAvailableAddressesAsync();
const owner = accounts[0];
const owner2 = accounts[1];
const allowanceTarget = '0xdef1c0ded9bec7f1a1670819833240f027b25eff';
await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner });
await makerToken.approve(allowanceTarget, new BigNumber(150)).awaitTransactionSuccessAsync({ from: owner });
await makerToken.mint(new BigNumber(150)).awaitTransactionSuccessAsync({ from: owner2 });
await makerToken
.approve(allowanceTarget, new BigNumber(200))
.awaitTransactionSuccessAsync({ from: owner2 });
const testResults = await contract
.getMinOfBalancesOrAllowances(
[owner, owner2],
[makerToken.address, makerToken.address],
allowanceTarget,
)
.callAsync();
expect(testResults).to.eql([new BigNumber(100), new BigNumber(150)]);
});
it('returns the allowance if the allowance < balance', async () => {
const makerToken = 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 accounts = await web3Wrapper.getAvailableAddressesAsync();
const owner = accounts[0];
const owner2 = accounts[1];
const allowanceTarget = '0xdef1c0ded9bec7f1a1670819833240f027b25eff';
await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner });
await makerToken.approve(allowanceTarget, new BigNumber(50)).awaitTransactionSuccessAsync({ from: owner });
await makerToken.mint(new BigNumber(100)).awaitTransactionSuccessAsync({ from: owner2 });
await makerToken.approve(allowanceTarget, new BigNumber(75)).awaitTransactionSuccessAsync({ from: owner2 });
const testResults = await contract
.getMinOfBalancesOrAllowances(
[owner, owner2],
[makerToken.address, makerToken.address],
allowanceTarget,
)
.callAsync();
expect(testResults).to.eql([new BigNumber(50), new BigNumber(75)]);
});
});
});

View File

@@ -6,11 +6,12 @@ import {
getRandomPortion,
randomAddress,
} from '@0x/contracts-test-utils';
import { Order } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import { SignatureType } from '@0x/protocol-utils';
import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils';
import * as _ from 'lodash';
import { SamplerCallResult } from '../../src/types';
import { FillQuoteTransformerOrderType, LimitOrderFields } from '../../src';
import { SamplerCallResult, SignedNativeOrder } from '../../src/types';
import { artifacts } from '../artifacts';
import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '../wrappers';
@@ -30,7 +31,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const ETH2DAI_SALT = '0xb713b61bb9bb2958a0f5d1534b21e94fc68c4c0c034b0902ed844f2f6cd1b4f7';
const UNISWAP_BASE_SALT = '0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab';
const UNISWAP_V2_SALT = '0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1';
const ERC20_PROXY_ID = '0xf47261b0';
let UNISWAP_V2_ROUTER = '';
const INVALID_TOKEN_PAIR_ERROR = 'ERC20BridgeSampler/INVALID_TOKEN_PAIR';
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
@@ -44,6 +45,7 @@ blockchainTests('erc20-bridge-sampler', env => {
env.txDefaults,
{},
);
UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync();
});
function getPackedHash(...args: string[]): string {
@@ -207,23 +209,19 @@ blockchainTests('erc20-bridge-sampler', env => {
return sold;
}
function getDeterministicFillableTakerAssetAmount(order: Order): BigNumber {
const hash = getPackedHash(hexUtils.leftPad(order.salt));
return new BigNumber(hash).mod(order.takerAssetAmount);
function getDeterministicFillableTakerAssetAmount(order: SignedNativeOrder): BigNumber {
const hash = getPackedHash(hexUtils.leftPad(order.order.salt));
return new BigNumber(hash).mod(order.order.takerAmount);
}
function getDeterministicFillableMakerAssetAmount(order: Order): BigNumber {
function getDeterministicFillableMakerAssetAmount(order: SignedNativeOrder): BigNumber {
const takerAmount = getDeterministicFillableTakerAssetAmount(order);
return order.makerAssetAmount
return order.order.makerAmount
.times(takerAmount)
.div(order.takerAssetAmount)
.div(order.order.takerAmount)
.integerValue(BigNumber.ROUND_UP);
}
function getERC20AssetData(tokenAddress: string): string {
return hexUtils.concat(ERC20_PROXY_ID, hexUtils.leftPad(tokenAddress));
}
function getSampleAmounts(tokenAddress: string, count?: number): BigNumber[] {
const tokenDecimals = getDeterministicTokenDecimals(tokenAddress);
const _upperLimit = getRandomPortion(getRandomInteger(1000, 50000).times(10 ** tokenDecimals));
@@ -232,28 +230,30 @@ blockchainTests('erc20-bridge-sampler', env => {
return _.times(_count, i => d.times((i + 1) / _count).integerValue());
}
function createOrder(makerToken: string, takerToken: string): Order {
function createOrder(makerToken: string, takerToken: string): SignedNativeOrder {
return {
chainId: 1337,
exchangeAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
senderAddress: randomAddress(),
feeRecipientAddress: randomAddress(),
makerAssetAmount: getRandomInteger(1, 1e18),
takerAssetAmount: getRandomInteger(1, 1e18),
makerFee: getRandomInteger(1, 1e18),
takerFee: getRandomInteger(1, 1e18),
makerAssetData: getERC20AssetData(makerToken),
takerAssetData: getERC20AssetData(takerToken),
makerFeeAssetData: getERC20AssetData(randomAddress()),
takerFeeAssetData: getERC20AssetData(randomAddress()),
salt: new BigNumber(hexUtils.random()),
expirationTimeSeconds: getRandomInteger(0, 2 ** 32),
order: {
chainId: 1337,
verifyingContract: randomAddress(),
maker: randomAddress(),
taker: randomAddress(),
pool: NULL_BYTES,
sender: NULL_ADDRESS,
feeRecipient: randomAddress(),
makerAmount: getRandomInteger(1, 1e18),
takerAmount: getRandomInteger(1, 1e18),
takerTokenFeeAmount: getRandomInteger(1, 1e18),
makerToken,
takerToken,
salt: new BigNumber(hexUtils.random()),
expiry: getRandomInteger(0, 2 ** 32),
},
signature: { v: 1, r: NULL_BYTES, s: NULL_BYTES, signatureType: SignatureType.EthSign },
type: FillQuoteTransformerOrderType.Limit,
};
}
function createOrders(makerToken: string, takerToken: string, count?: number): Order[] {
function createOrders(makerToken: string, takerToken: string, count?: number): SignedNativeOrder[] {
return _.times(count || _.random(1, 16), () => createOrder(makerToken, takerToken));
}
@@ -291,16 +291,20 @@ blockchainTests('erc20-bridge-sampler', env => {
describe('getOrderFillableTakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
const actual = await testContract
.getOrderFillableTakerAssetAmounts(orders, signatures, NULL_ADDRESS)
.getLimitOrderFillableTakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders.map(o => o.order as LimitOrderFields),
orders.map(o => o.signature),
NULL_ADDRESS,
)
.callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.getOrderFillableTakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
const actual = await testContract.getLimitOrderFillableTakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
expect(actual).to.deep.eq([]);
});
});
@@ -308,16 +312,20 @@ blockchainTests('erc20-bridge-sampler', env => {
describe('getOrderFillableMakerAssetAmounts()', () => {
it('returns the expected amount for each order', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableMakerAssetAmount);
const actual = await testContract
.getOrderFillableMakerAssetAmounts(orders, signatures, NULL_ADDRESS)
.getLimitOrderFillableMakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders.map(o => o.order as LimitOrderFields),
orders.map(o => o.signature),
NULL_ADDRESS,
)
.callAsync();
expect(actual).to.deep.eq(expected);
});
it('returns empty for no orders', async () => {
const actual = await testContract.getOrderFillableMakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
const actual = await testContract.getLimitOrderFillableMakerAssetAmounts([], [], NULL_ADDRESS).callAsync();
expect(actual).to.deep.eq([]);
});
});
@@ -858,7 +866,9 @@ blockchainTests('erc20-bridge-sampler', env => {
}
it('can return no quotes', async () => {
const quotes = await testContract.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], []).callAsync();
const quotes = await testContract
.sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@@ -866,7 +876,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = predictSellQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -876,7 +886,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleSellsFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -886,7 +896,11 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(TAKER_TOKEN);
const expectedQuotes = predictSellQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
const quotes = await testContract
.sampleSellsFromUniswapV2([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts)
.sampleSellsFromUniswapV2(
UNISWAP_V2_ROUTER,
[TAKER_TOKEN, intermediateToken, MAKER_TOKEN],
sampleAmounts,
)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -898,7 +912,9 @@ blockchainTests('erc20-bridge-sampler', env => {
}
it('can return no quotes', async () => {
const quotes = await testContract.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], []).callAsync();
const quotes = await testContract
.sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], [])
.callAsync();
expect(quotes).to.deep.eq([]);
});
@@ -906,7 +922,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -916,7 +932,7 @@ blockchainTests('erc20-bridge-sampler', env => {
const expectedQuotes = _.times(sampleAmounts.length, () => constants.ZERO_AMOUNT);
await enableFailTriggerAsync();
const quotes = await testContract
.sampleBuysFromUniswapV2([TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, [TAKER_TOKEN, MAKER_TOKEN], sampleAmounts)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -926,7 +942,11 @@ blockchainTests('erc20-bridge-sampler', env => {
const sampleAmounts = getSampleAmounts(MAKER_TOKEN);
const expectedQuotes = predictBuyQuotes([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts);
const quotes = await testContract
.sampleBuysFromUniswapV2([TAKER_TOKEN, intermediateToken, MAKER_TOKEN], sampleAmounts)
.sampleBuysFromUniswapV2(
UNISWAP_V2_ROUTER,
[TAKER_TOKEN, intermediateToken, MAKER_TOKEN],
sampleAmounts,
)
.callAsync();
expect(quotes).to.deep.eq(expectedQuotes);
});
@@ -935,17 +955,21 @@ blockchainTests('erc20-bridge-sampler', env => {
describe('batchCall()', () => {
it('can call one function', async () => {
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN);
const signatures: string[] = _.times(orders.length, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
const calls = [
testContract
.getOrderFillableTakerAssetAmounts(orders, signatures, NULL_ADDRESS)
.getLimitOrderFillableTakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders.map(o => o.order as LimitOrderFields),
orders.map(o => o.signature),
NULL_ADDRESS,
)
.getABIEncodedTransactionData(),
];
const r = await testContract.batchCall(calls).callAsync();
expect(r).to.be.length(1);
const actual = testContract.getABIDecodedReturnData<BigNumber[]>(
'getOrderFillableTakerAssetAmounts',
'getLimitOrderFillableTakerAssetAmounts',
r[0].data,
);
expect(actual).to.deep.eq(expected);
@@ -954,40 +978,53 @@ blockchainTests('erc20-bridge-sampler', env => {
it('can call two functions', async () => {
const numOrders = _.random(1, 10);
const orders = _.times(2, () => createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders));
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
const expecteds = [
orders[0].map(getDeterministicFillableTakerAssetAmount),
orders[1].map(getDeterministicFillableMakerAssetAmount),
];
const calls = [
testContract
.getOrderFillableTakerAssetAmounts(orders[0], signatures, NULL_ADDRESS)
.getLimitOrderFillableTakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders[0].map(o => o.order as LimitOrderFields),
orders[0].map(o => o.signature),
NULL_ADDRESS,
)
.getABIEncodedTransactionData(),
testContract
.getOrderFillableMakerAssetAmounts(orders[1], signatures, NULL_ADDRESS)
.getLimitOrderFillableMakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders[1].map(o => o.order as LimitOrderFields),
orders[1].map(o => o.signature),
NULL_ADDRESS,
)
.getABIEncodedTransactionData(),
];
const r = await testContract.batchCall(calls).callAsync();
expect(r).to.be.length(2);
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0].data)).to.deep.eq(
expecteds[0],
);
expect(testContract.getABIDecodedReturnData('getOrderFillableMakerAssetAmounts', r[1].data)).to.deep.eq(
expecteds[1],
);
expect(
testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data),
).to.deep.eq(expecteds[0]);
expect(
testContract.getABIDecodedReturnData('getLimitOrderFillableMakerAssetAmounts', r[1].data),
).to.deep.eq(expecteds[1]);
});
it('can make recursive calls', async () => {
const numOrders = _.random(1, 10);
const orders = createOrders(MAKER_TOKEN, TAKER_TOKEN, numOrders);
const signatures: string[] = _.times(numOrders, i => hexUtils.random());
const expected = orders.map(getDeterministicFillableTakerAssetAmount);
let r = await testContract
.batchCall([
testContract
.batchCall([
testContract
.getOrderFillableTakerAssetAmounts(orders, signatures, NULL_ADDRESS)
.getLimitOrderFillableTakerAssetAmounts(
// tslint:disable-next-line:no-unnecessary-type-assertion
orders.map(o => o.order as LimitOrderFields),
orders.map(o => o.signature),
NULL_ADDRESS,
)
.getABIEncodedTransactionData(),
])
.getABIEncodedTransactionData(),
@@ -996,9 +1033,9 @@ blockchainTests('erc20-bridge-sampler', env => {
expect(r).to.be.length(1);
r = testContract.getABIDecodedReturnData<SamplerCallResult[]>('batchCall', r[0].data);
expect(r).to.be.length(1);
expect(testContract.getABIDecodedReturnData('getOrderFillableTakerAssetAmounts', r[0].data)).to.deep.eq(
expected,
);
expect(
testContract.getABIDecodedReturnData('getLimitOrderFillableTakerAssetAmounts', r[0].data),
).to.deep.eq(expected);
});
});
@@ -1014,12 +1051,12 @@ blockchainTests('erc20-bridge-sampler', env => {
const sellAmount = _.last(getSampleAmounts(TAKER_TOKEN))!;
const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN];
const uniswapV2FirstHop = testContract
.sampleSellsFromUniswapV2(uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
.sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN];
const uniswapV2SecondHop = testContract
.sampleSellsFromUniswapV2(uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
.sampleSellsFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const eth2DaiFirstHop = testContract
@@ -1065,12 +1102,12 @@ blockchainTests('erc20-bridge-sampler', env => {
const buyAmount = _.last(getSampleAmounts(MAKER_TOKEN))!;
const uniswapV2FirstHopPath = [TAKER_TOKEN, INTERMEDIATE_TOKEN];
const uniswapV2FirstHop = testContract
.sampleBuysFromUniswapV2(uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
.sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2FirstHopPath, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const uniswapV2SecondHopPath = [INTERMEDIATE_TOKEN, randomAddress(), MAKER_TOKEN];
const uniswapV2SecondHop = testContract
.sampleBuysFromUniswapV2(uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
.sampleBuysFromUniswapV2(UNISWAP_V2_ROUTER, uniswapV2SecondHopPath, [constants.ZERO_AMOUNT])
.getABIEncodedTransactionData();
const eth2DaiFirstHop = testContract

View File

@@ -7,10 +7,12 @@ import {
getRandomInteger,
randomAddress,
} from '@0x/contracts-test-utils';
import { Order } from '@0x/types';
import { SignatureType } from '@0x/protocol-utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { LimitOrderFields } from '../../src';
import { NULL_ADDRESS } from '../../src/utils/market_operation_utils/constants';
import { artifacts } from '../artifacts';
import { TestNativeOrderSamplerContract } from '../wrappers';
@@ -18,15 +20,12 @@ const { NULL_BYTES, ZERO_AMOUNT } = constants;
// tslint:disable: custom-no-magic-numbers
blockchainTests.resets('NativeOrderSampler contract', env => {
// TODO jacob
blockchainTests.resets.skip('NativeOrderSampler contract', env => {
let testContract: TestNativeOrderSamplerContract;
let makerToken: string;
let takerToken: string;
let feeToken: string;
let erc20Proxy: string;
const ERC20_PROXY_ID = '0xf47261b0';
const VALID_SIGNATURE = '0x01';
const INVALID_SIGNATURE = '0x00';
const VALID_SIGNATURE = { v: 1, r: '0x01', s: '0x01', signatureType: SignatureType.EthSign };
before(async () => {
testContract = await TestNativeOrderSamplerContract.deployFrom0xArtifactAsync(
@@ -35,9 +34,8 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
env.txDefaults,
{},
);
erc20Proxy = await testContract.getAssetProxy(ERC20_PROXY_ID).callAsync();
const NUM_TOKENS = new BigNumber(3);
[makerToken, takerToken, feeToken] = await testContract.createTokens(NUM_TOKENS).callAsync();
[makerToken, takerToken] = await testContract.createTokens(NUM_TOKENS).callAsync();
await testContract.createTokens(NUM_TOKENS).awaitTransactionSuccessAsync();
});
@@ -51,10 +49,10 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
orderTakerAssetFilledAmount: BigNumber;
}
function getOrderInfo(order: Order): OrderInfo {
function getOrderInfo(order: LimitOrderFields): OrderInfo {
const hash = getPackedHash(hexUtils.leftPad(order.salt));
const orderStatus = order.salt.mod(255).eq(0) ? 3 : 5;
const filledAmount = order.expirationTimeSeconds;
const filledAmount = order.expiry;
return {
orderStatus,
orderHash: hash,
@@ -70,61 +68,46 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
return new BigNumber(hexUtils.concat(hexUtils.slice(hexUtils.random(), 0, -1), '0xff'));
}
function getOrderFillableTakerAmount(order: Order): BigNumber {
return order.takerAssetAmount.minus(getOrderInfo(order).orderTakerAssetFilledAmount);
function getLimitOrderFillableTakerAmount(order: LimitOrderFields): BigNumber {
return order.takerAmount.minus(getOrderInfo(order).orderTakerAssetFilledAmount);
}
function getERC20AssetData(tokenAddress: string): string {
return hexUtils.concat(ERC20_PROXY_ID, hexUtils.leftPad(tokenAddress));
}
function createOrder(fields: Partial<Order> = {}, filledTakerAssetAmount: BigNumber = ZERO_AMOUNT): Order {
function createOrder(
fields: Partial<LimitOrderFields> = {},
filledTakerAssetAmount: BigNumber = ZERO_AMOUNT,
): LimitOrderFields {
return {
chainId: 1337,
exchangeAddress: randomAddress(),
makerAddress: randomAddress(),
takerAddress: randomAddress(),
senderAddress: randomAddress(),
feeRecipientAddress: randomAddress(),
makerAssetAmount: getRandomInteger(1e18, 10e18),
takerAssetAmount: getRandomInteger(1e18, 10e18),
makerFee: getRandomInteger(1e18, 10e18),
takerFee: getRandomInteger(1e18, 10e18),
makerAssetData: getERC20AssetData(makerToken),
takerAssetData: getERC20AssetData(takerToken),
makerFeeAssetData: getERC20AssetData(feeToken),
takerFeeAssetData: getERC20AssetData(randomAddress()),
verifyingContract: randomAddress(),
maker: randomAddress(),
taker: randomAddress(),
pool: NULL_BYTES,
sender: NULL_ADDRESS,
feeRecipient: randomAddress(),
makerAmount: getRandomInteger(1, 1e18),
takerAmount: getRandomInteger(1, 1e18),
takerTokenFeeAmount: getRandomInteger(1, 1e18),
makerToken,
takerToken,
salt: createFillableOrderSalt(),
// Expiration time will be used to determine filled amount.
expirationTimeSeconds: filledTakerAssetAmount,
expiry: filledTakerAssetAmount,
...fields,
};
}
async function fundMakerAsync(
order: Order,
assetData: string,
order: LimitOrderFields,
balanceScaling: number = 1,
allowanceScaling: number = 1,
): Promise<void> {
let token;
let amount;
if (assetData === order.makerAssetData) {
token = makerToken;
amount =
order.makerAssetData === order.makerFeeAssetData
? order.makerAssetAmount.plus(order.makerFee)
: order.makerAssetAmount;
} else {
token = feeToken;
amount = order.makerFee;
}
amount = amount.times(getOrderFillableTakerAmount(order).div(BigNumber.max(1, order.takerAssetAmount)));
const token = makerToken;
let amount = order.makerAmount;
amount = amount.times(getLimitOrderFillableTakerAmount(order).div(BigNumber.max(1, order.takerAmount)));
await testContract
.setTokenBalanceAndAllowance(
token,
order.makerAddress,
erc20Proxy,
order.maker,
testContract.address,
amount.times(balanceScaling).integerValue(),
amount.times(allowanceScaling).integerValue(),
)
@@ -132,7 +115,7 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
}
describe('getTokenDecimals()', () => {
it('correctly returns the token balances', async () => {
it('correctly returns the token decimals', async () => {
const newMakerToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
env.provider,
@@ -154,130 +137,44 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
const [makerDecimals, takerDecimals] = await testContract
.getTokenDecimals(newMakerToken.address, newTakerToken.address)
.getTokenDecimals([newMakerToken.address, newTakerToken.address])
.callAsync();
expect(makerDecimals.toString()).to.eql('18');
expect(takerDecimals.toString()).to.eql('6');
});
});
describe('getOrderFillableTakerAmount()', () => {
describe('getLimitOrderFillableTakerAmount()', () => {
it('returns the full amount for a fully funded order', async () => {
const order = createOrder();
const expected = getOrderFillableTakerAmount(order);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
const expected = getLimitOrderFillableTakerAmount(order);
await fundMakerAsync(order);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(actual).to.bignumber.eq(expected);
});
it('returns the full amount for a fully funded order without maker fees', async () => {
const order = createOrder({ makerFee: ZERO_AMOUNT });
const expected = getOrderFillableTakerAmount(order);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(actual).to.bignumber.eq(expected);
});
it('returns the full amount for a fully funded order without maker fee asset data', async () => {
const order = createOrder({ makerFeeAssetData: NULL_BYTES });
const expected = getOrderFillableTakerAmount(order);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(actual).to.bignumber.eq(expected);
});
it('returns the full amount for a fully funded order with maker fees denominated in the maker asset', async () => {
const order = createOrder({ makerFeeAssetData: getERC20AssetData(makerToken) });
const expected = getOrderFillableTakerAmount(order);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(actual).to.bignumber.eq(expected);
});
it('returns partial amount with insufficient maker asset balance', async () => {
const order = createOrder();
const expected = getOrderFillableTakerAmount(order)
const expected = getLimitOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData, 0.5);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
it('returns partial amount with insufficient maker asset allowance', async () => {
const order = createOrder();
const expected = getOrderFillableTakerAmount(order)
const expected = getLimitOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData, 1, 0.5);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order, 1, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
it('returns partial amount with insufficient maker fee asset balance', async () => {
const order = createOrder();
const expected = getOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
it('returns partial amount with insufficient maker fee asset allowance', async () => {
const order = createOrder();
const expected = getOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData, 1, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
it('returns partial amount with insufficient maker asset balance (maker asset fees)', async () => {
const order = createOrder({ makerFeeAssetData: getERC20AssetData(makerToken) });
const expected = getOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
it('returns partial amount with insufficient maker asset allowance (maker asset fees)', async () => {
const order = createOrder({ makerFeeAssetData: getERC20AssetData(makerToken) });
const expected = getOrderFillableTakerAmount(order)
.times(0.5)
.integerValue(BigNumber.ROUND_DOWN);
await fundMakerAsync(order, order.makerAssetData, 1, 0.5);
const actual = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
assertIntegerRoughlyEquals(actual, expected, 100);
});
@@ -287,10 +184,9 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
...createOrder(),
salt: createUnfillableOrderSalt(),
};
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order);
const fillableTakerAmount = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
});
@@ -298,12 +194,11 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
it('returns zero for an order with zero maker asset amount', async () => {
const order = {
...createOrder(),
makerAssetAmount: ZERO_AMOUNT,
makerAmount: ZERO_AMOUNT,
};
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order);
const fillableTakerAmount = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
});
@@ -311,32 +206,24 @@ blockchainTests.resets('NativeOrderSampler contract', env => {
it('returns zero for an order with zero taker asset amount', async () => {
const order = {
...createOrder(),
takerAssetAmount: ZERO_AMOUNT,
takerAmount: ZERO_AMOUNT,
};
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order);
const fillableTakerAmount = await testContract
.getOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
.callAsync();
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
});
it('returns zero for an order with an empty signature', async () => {
const order = createOrder();
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
await fundMakerAsync(order);
const fillableTakerAmount = await testContract
.getOrderFillableTakerAmount(order, NULL_BYTES, testContract.address)
.callAsync();
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
});
it('returns zero for an order with an invalid signature', async () => {
const order = createOrder();
await fundMakerAsync(order, order.makerAssetData);
await fundMakerAsync(order, order.makerFeeAssetData);
const fillableTakerAmount = await testContract
.getOrderFillableTakerAmount(order, INVALID_SIGNATURE, testContract.address)
.getLimitOrderFillableTakerAmount(
order,
{ ...VALID_SIGNATURE, r: NULL_BYTES, s: NULL_BYTES },
testContract.address,
)
.callAsync();
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
});