193 lines
8.0 KiB
TypeScript
193 lines
8.0 KiB
TypeScript
import { ExchangeRevertErrors } from '@0x/contracts-exchange';
|
|
import {
|
|
blockchainTests,
|
|
constants,
|
|
expect,
|
|
getRandomInteger,
|
|
orderHashUtils,
|
|
randomAddress,
|
|
} from '@0x/contracts-test-utils';
|
|
import { assetDataUtils } from '@0x/order-utils';
|
|
import { SignedOrder } from '@0x/types';
|
|
import { BigNumber, StringRevertError } from '@0x/utils';
|
|
|
|
import {
|
|
decodeChainlinkStopLimitData,
|
|
decodeStopLimitStaticCallData,
|
|
encodeChainlinkStopLimitData,
|
|
encodeStopLimitStaticCallData,
|
|
} from '../../src/chainlink_utils';
|
|
|
|
import { artifacts } from '../artifacts';
|
|
import { Actor } from '../framework/actors/base';
|
|
import { Maker } from '../framework/actors/maker';
|
|
import { Taker } from '../framework/actors/taker';
|
|
import { BlockchainBalanceStore } from '../framework/balances/blockchain_balance_store';
|
|
import { LocalBalanceStore } from '../framework/balances/local_balance_store';
|
|
import { DeploymentManager } from '../framework/deployment_manager';
|
|
import { ChainlinkStopLimitContract, TestChainlinkAggregatorContract } from '../wrappers';
|
|
|
|
blockchainTests.resets('Chainlink stop-limit order tests', env => {
|
|
let deployment: DeploymentManager;
|
|
let balanceStore: BlockchainBalanceStore;
|
|
let initialBalances: LocalBalanceStore;
|
|
|
|
let chainLinkAggregator: TestChainlinkAggregatorContract;
|
|
let chainlinkStopLimit: ChainlinkStopLimitContract;
|
|
|
|
let maker: Maker;
|
|
let taker: Taker;
|
|
|
|
let order: SignedOrder;
|
|
|
|
const minPrice = new BigNumber(42);
|
|
const maxPrice = new BigNumber(1337);
|
|
|
|
before(async () => {
|
|
deployment = await DeploymentManager.deployAsync(env, {
|
|
numErc20TokensToDeploy: 2,
|
|
numErc721TokensToDeploy: 0,
|
|
numErc1155TokensToDeploy: 0,
|
|
});
|
|
const [makerToken, takerToken] = deployment.tokens.erc20;
|
|
|
|
chainlinkStopLimit = await ChainlinkStopLimitContract.deployFrom0xArtifactAsync(
|
|
artifacts.ChainlinkStopLimit,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
chainLinkAggregator = await TestChainlinkAggregatorContract.deployFrom0xArtifactAsync(
|
|
artifacts.TestChainlinkAggregator,
|
|
env.provider,
|
|
env.txDefaults,
|
|
artifacts,
|
|
);
|
|
|
|
const makerAssetData = assetDataUtils.encodeMultiAssetData(
|
|
[new BigNumber(1), new BigNumber(1)],
|
|
[
|
|
assetDataUtils.encodeERC20AssetData(makerToken.address),
|
|
encodeStopLimitStaticCallData(chainlinkStopLimit.address, {
|
|
oracle: chainLinkAggregator.address,
|
|
minPrice,
|
|
maxPrice,
|
|
}),
|
|
],
|
|
);
|
|
|
|
const orderConfig = {
|
|
feeRecipientAddress: constants.NULL_ADDRESS,
|
|
makerAssetData,
|
|
takerAssetData: assetDataUtils.encodeERC20AssetData(takerToken.address),
|
|
makerFeeAssetData: constants.NULL_BYTES,
|
|
takerFeeAssetData: constants.NULL_BYTES,
|
|
makerFee: constants.ZERO_AMOUNT,
|
|
takerFee: constants.ZERO_AMOUNT,
|
|
};
|
|
|
|
maker = new Maker({
|
|
name: 'Maker',
|
|
deployment,
|
|
orderConfig,
|
|
});
|
|
taker = new Taker({ name: 'Taker', deployment });
|
|
|
|
// Set balances and allowances
|
|
await maker.configureERC20TokenAsync(makerToken);
|
|
await taker.configureERC20TokenAsync(takerToken);
|
|
|
|
order = await maker.signOrderAsync();
|
|
|
|
// Set up balance stores
|
|
const tokenOwners = {
|
|
Maker: maker.address,
|
|
Taker: taker.address,
|
|
};
|
|
const tokenContracts = {
|
|
erc20: { makerToken, takerToken },
|
|
};
|
|
balanceStore = new BlockchainBalanceStore(tokenOwners, tokenContracts);
|
|
await balanceStore.updateBalancesAsync();
|
|
initialBalances = LocalBalanceStore.create(balanceStore);
|
|
});
|
|
|
|
after(async () => {
|
|
Actor.reset();
|
|
});
|
|
|
|
describe('filling stop-limit orders', () => {
|
|
it('fillOrder reverts if price < minPrice', async () => {
|
|
await chainLinkAggregator.setPrice(minPrice.minus(1)).awaitTransactionSuccessAsync();
|
|
const tx = taker.fillOrderAsync(order, order.takerAssetAmount);
|
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
|
orderHashUtils.getOrderHashHex(order),
|
|
order.makerAssetData,
|
|
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').encode(),
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fillOrder reverts price > maxPrice', async () => {
|
|
await chainLinkAggregator.setPrice(maxPrice.plus(1)).awaitTransactionSuccessAsync();
|
|
const tx = taker.fillOrderAsync(order, order.takerAssetAmount);
|
|
const expectedError = new ExchangeRevertErrors.AssetProxyTransferError(
|
|
orderHashUtils.getOrderHashHex(order),
|
|
order.makerAssetData,
|
|
new StringRevertError('ChainlinkStopLimit/OUT_OF_PRICE_RANGE').encode(),
|
|
);
|
|
return expect(tx).to.revertWith(expectedError);
|
|
});
|
|
it('fillOrder succeeds if price = minPrice', async () => {
|
|
await chainLinkAggregator.setPrice(minPrice).awaitTransactionSuccessAsync();
|
|
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
|
|
const expectedBalances = LocalBalanceStore.create(initialBalances);
|
|
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
|
|
await balanceStore.updateBalancesAsync();
|
|
balanceStore.assertEquals(expectedBalances);
|
|
});
|
|
it('fillOrder succeeds if price = maxPrice', async () => {
|
|
await chainLinkAggregator.setPrice(maxPrice).awaitTransactionSuccessAsync();
|
|
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
|
|
const expectedBalances = LocalBalanceStore.create(initialBalances);
|
|
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
|
|
await balanceStore.updateBalancesAsync();
|
|
balanceStore.assertEquals(expectedBalances);
|
|
});
|
|
it('fillOrder succeeds if minPrice < price < maxPrice', async () => {
|
|
await chainLinkAggregator
|
|
.setPrice(minPrice.plus(maxPrice).dividedToIntegerBy(2))
|
|
.awaitTransactionSuccessAsync();
|
|
const receipt = await taker.fillOrderAsync(order, order.takerAssetAmount);
|
|
const expectedBalances = LocalBalanceStore.create(initialBalances);
|
|
expectedBalances.simulateFills([order], taker.address, receipt, deployment, DeploymentManager.protocolFee);
|
|
await balanceStore.updateBalancesAsync();
|
|
balanceStore.assertEquals(expectedBalances);
|
|
});
|
|
});
|
|
|
|
describe('Data encoding/decoding tools', () => {
|
|
const MAX_INT256 = new BigNumber(2).exponentiatedBy(255).minus(1);
|
|
|
|
it('correctly decodes chainlink stop-limit params', async () => {
|
|
const params = {
|
|
oracle: randomAddress(),
|
|
minPrice: getRandomInteger(0, MAX_INT256),
|
|
maxPrice: getRandomInteger(0, MAX_INT256),
|
|
};
|
|
const encoded = encodeChainlinkStopLimitData(params);
|
|
const decoded = decodeChainlinkStopLimitData(encoded);
|
|
expect(decoded).to.deep.equal(params);
|
|
});
|
|
it('correctly decodes stop-limit assetData', async () => {
|
|
const params = {
|
|
oracle: randomAddress(),
|
|
minPrice: getRandomInteger(0, MAX_INT256),
|
|
maxPrice: getRandomInteger(0, MAX_INT256),
|
|
};
|
|
const encoded = encodeStopLimitStaticCallData(chainlinkStopLimit.address, params);
|
|
const decoded = decodeStopLimitStaticCallData(encoded);
|
|
expect(decoded).to.deep.equal(params);
|
|
});
|
|
});
|
|
});
|