* 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>
232 lines
8.9 KiB
TypeScript
232 lines
8.9 KiB
TypeScript
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
|
|
import {
|
|
assertIntegerRoughlyEquals,
|
|
blockchainTests,
|
|
constants,
|
|
expect,
|
|
getRandomInteger,
|
|
randomAddress,
|
|
} from '@0x/contracts-test-utils';
|
|
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';
|
|
|
|
const { NULL_BYTES, ZERO_AMOUNT } = constants;
|
|
|
|
// tslint:disable: custom-no-magic-numbers
|
|
|
|
// TODO jacob
|
|
blockchainTests.resets.skip('NativeOrderSampler contract', env => {
|
|
let testContract: TestNativeOrderSamplerContract;
|
|
let makerToken: string;
|
|
let takerToken: string;
|
|
const VALID_SIGNATURE = { v: 1, r: '0x01', s: '0x01', signatureType: SignatureType.EthSign };
|
|
|
|
before(async () => {
|
|
testContract = await TestNativeOrderSamplerContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestNativeOrderSampler,
|
|
env.provider,
|
|
env.txDefaults,
|
|
{},
|
|
);
|
|
const NUM_TOKENS = new BigNumber(3);
|
|
[makerToken, takerToken] = await testContract.createTokens(NUM_TOKENS).callAsync();
|
|
await testContract.createTokens(NUM_TOKENS).awaitTransactionSuccessAsync();
|
|
});
|
|
|
|
function getPackedHash(...args: string[]): string {
|
|
return hexUtils.hash(hexUtils.concat(...args.map(a => hexUtils.toHex(a))));
|
|
}
|
|
|
|
interface OrderInfo {
|
|
orderHash: string;
|
|
orderStatus: number;
|
|
orderTakerAssetFilledAmount: BigNumber;
|
|
}
|
|
|
|
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.expiry;
|
|
return {
|
|
orderStatus,
|
|
orderHash: hash,
|
|
orderTakerAssetFilledAmount: filledAmount,
|
|
};
|
|
}
|
|
|
|
function createFillableOrderSalt(): BigNumber {
|
|
return new BigNumber(hexUtils.concat(hexUtils.slice(hexUtils.random(), 0, -1), '0x01'));
|
|
}
|
|
|
|
function createUnfillableOrderSalt(): BigNumber {
|
|
return new BigNumber(hexUtils.concat(hexUtils.slice(hexUtils.random(), 0, -1), '0xff'));
|
|
}
|
|
|
|
function getLimitOrderFillableTakerAmount(order: LimitOrderFields): BigNumber {
|
|
return order.takerAmount.minus(getOrderInfo(order).orderTakerAssetFilledAmount);
|
|
}
|
|
|
|
function createOrder(
|
|
fields: Partial<LimitOrderFields> = {},
|
|
filledTakerAssetAmount: BigNumber = ZERO_AMOUNT,
|
|
): LimitOrderFields {
|
|
return {
|
|
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: createFillableOrderSalt(),
|
|
expiry: filledTakerAssetAmount,
|
|
...fields,
|
|
};
|
|
}
|
|
|
|
async function fundMakerAsync(
|
|
order: LimitOrderFields,
|
|
balanceScaling: number = 1,
|
|
allowanceScaling: number = 1,
|
|
): Promise<void> {
|
|
const token = makerToken;
|
|
let amount = order.makerAmount;
|
|
amount = amount.times(getLimitOrderFillableTakerAmount(order).div(BigNumber.max(1, order.takerAmount)));
|
|
await testContract
|
|
.setTokenBalanceAndAllowance(
|
|
token,
|
|
order.maker,
|
|
testContract.address,
|
|
amount.times(balanceScaling).integerValue(),
|
|
amount.times(allowanceScaling).integerValue(),
|
|
)
|
|
.awaitTransactionSuccessAsync();
|
|
}
|
|
|
|
describe('getTokenDecimals()', () => {
|
|
it('correctly returns the token decimals', 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('getLimitOrderFillableTakerAmount()', () => {
|
|
it('returns the full amount for a fully funded order', async () => {
|
|
const order = createOrder();
|
|
const expected = getLimitOrderFillableTakerAmount(order);
|
|
await fundMakerAsync(order);
|
|
const actual = await testContract
|
|
.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 = getLimitOrderFillableTakerAmount(order)
|
|
.times(0.5)
|
|
.integerValue(BigNumber.ROUND_DOWN);
|
|
await fundMakerAsync(order, 0.5);
|
|
const actual = await testContract
|
|
.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 = getLimitOrderFillableTakerAmount(order)
|
|
.times(0.5)
|
|
.integerValue(BigNumber.ROUND_DOWN);
|
|
await fundMakerAsync(order, 1, 0.5);
|
|
const actual = await testContract
|
|
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
|
|
.callAsync();
|
|
assertIntegerRoughlyEquals(actual, expected, 100);
|
|
});
|
|
|
|
it('returns zero for an that is not fillable', async () => {
|
|
const order = {
|
|
...createOrder(),
|
|
salt: createUnfillableOrderSalt(),
|
|
};
|
|
await fundMakerAsync(order);
|
|
const fillableTakerAmount = await testContract
|
|
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
|
|
.callAsync();
|
|
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
|
|
});
|
|
|
|
it('returns zero for an order with zero maker asset amount', async () => {
|
|
const order = {
|
|
...createOrder(),
|
|
makerAmount: ZERO_AMOUNT,
|
|
};
|
|
await fundMakerAsync(order);
|
|
const fillableTakerAmount = await testContract
|
|
.getLimitOrderFillableTakerAmount(order, VALID_SIGNATURE, testContract.address)
|
|
.callAsync();
|
|
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
|
|
});
|
|
|
|
it('returns zero for an order with zero taker asset amount', async () => {
|
|
const order = {
|
|
...createOrder(),
|
|
takerAmount: ZERO_AMOUNT,
|
|
};
|
|
await fundMakerAsync(order);
|
|
const fillableTakerAmount = await testContract
|
|
.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);
|
|
const fillableTakerAmount = await testContract
|
|
.getLimitOrderFillableTakerAmount(
|
|
order,
|
|
{ ...VALID_SIGNATURE, r: NULL_BYTES, s: NULL_BYTES },
|
|
testContract.address,
|
|
)
|
|
.callAsync();
|
|
expect(fillableTakerAmount).to.bignumber.eq(ZERO_AMOUNT);
|
|
});
|
|
});
|
|
});
|