2019-07-23 15:04:46 -07:00

187 lines
9.0 KiB
TypeScript

import {
addressUtils,
chaiSetup,
constants,
OrderFactory,
provider,
txDefaults,
web3Wrapper,
} from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils, orderHashUtils } from '@0x/order-utils';
import { constants as orderConstants } from '@0x/order-utils/lib/src/constants';
import { SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import * as ethUtil from 'ethereumjs-util';
import { TestLibsContract } from '../generated-wrappers/test_libs';
import { artifacts } from '../src/artifacts';
import { stringifySchema } from './utils';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('Exchange libs', () => {
let chainId: number;
let signedOrder: SignedOrder;
let orderFactory: OrderFactory;
let libs: TestLibsContract;
let libsAlternateChain: TestLibsContract;
before(async () => {
await blockchainLifecycle.startAsync();
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
before(async () => {
const accounts = await web3Wrapper.getAvailableAddressesAsync();
const makerAddress = accounts[0];
chainId = await providerUtils.getChainIdAsync(provider);
libs = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
provider,
txDefaults,
new BigNumber(chainId),
);
// Deploy a version with a different chain ID.
const alternateChainId = chainId + 1;
libsAlternateChain = await TestLibsContract.deployFrom0xArtifactAsync(
artifacts.TestLibs,
provider,
txDefaults,
new BigNumber(alternateChainId),
);
const defaultOrderParams = {
...constants.STATIC_ORDER_PARAMS,
exchangeAddress: libs.address,
chainId,
makerAddress,
feeRecipientAddress: addressUtils.generatePseudoRandomAddress(),
makerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
takerAssetData: assetDataUtils.encodeERC20AssetData(addressUtils.generatePseudoRandomAddress()),
};
const privateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddress)];
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
// Note(albrow): These tests are designed to be supplemental to the
// combinatorial tests in test/exchange/internal. They test specific edge
// cases that are not covered by the combinatorial tests.
describe('LibMath', () => {
describe('isRoundingError', () => {
it('should return true if there is a rounding error of 0.1%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(999);
const target = new BigNumber(50);
// rounding error = ((20*50/999) - floor(20*50/999)) / (20*50/999) = 0.1%
const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
it('should return false if there is a rounding of 0.09%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9991);
const target = new BigNumber(500);
// rounding error = ((20*500/9991) - floor(20*500/9991)) / (20*500/9991) = 0.09%
const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(9989);
const target = new BigNumber(500);
// rounding error = ((20*500/9989) - floor(20*500/9989)) / (20*500/9989) = 0.011%
const isRoundingError = await libs.publicIsRoundingErrorFloor.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
});
describe('isRoundingErrorCeil', () => {
it('should return true if there is a rounding error of 0.1%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(1001);
const target = new BigNumber(50);
// rounding error = (ceil(20*50/1001) - (20*50/1001)) / (20*50/1001) = 0.1%
const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
it('should return false if there is a rounding of 0.09%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(10009);
const target = new BigNumber(500);
// rounding error = (ceil(20*500/10009) - (20*500/10009)) / (20*500/10009) = 0.09%
const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.false();
});
it('should return true if there is a rounding error of 0.11%', async () => {
const numerator = new BigNumber(20);
const denominator = new BigNumber(10011);
const target = new BigNumber(500);
// rounding error = (ceil(20*500/10011) - (20*500/10011)) / (20*500/10011) = 0.11%
const isRoundingError = await libs.publicIsRoundingErrorCeil.callAsync(numerator, denominator, target);
expect(isRoundingError).to.be.true();
});
});
});
describe('LibOrder', () => {
describe('getOrderHash', () => {
it('should output the correct orderHash', async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
const orderHashHex = await libs.publicGetOrderHash.callAsync(signedOrder);
expect(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(orderHashHex);
});
it('orderHash should differ if chainId is different', async () => {
signedOrder = await orderFactory.newSignedOrderAsync();
const orderHashHex1 = await libsAlternateChain.publicGetOrderHash.callAsync(signedOrder);
const orderHashHex2 = await libs.publicGetOrderHash.callAsync(signedOrder);
expect(orderHashHex1).to.be.not.equal(orderHashHex2);
});
});
});
describe('LibEIP712', () => {
it('should return the correct domain separator schema hash', async () => {
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
const actualSchemaHash = await libs.getDomainSeparatorSchemaHash.callAsync();
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
});
it('should return the correct order schema hash', async () => {
const schema = stringifySchema(orderConstants.EXCHANGE_ORDER_SCHEMA);
const expectedSchemaHash = ethUtil.bufferToHex(ethUtil.sha3(Buffer.from(schema)));
const actualSchemaHash = await libs.getOrderSchemaHash.callAsync();
expect(actualSchemaHash).to.be.equal(expectedSchemaHash);
});
it('should return the correct domain separator', async () => {
const schema = stringifySchema(orderConstants.DEFAULT_DOMAIN_SCHEMA);
const schemaHash = ethUtil.sha3(Buffer.from(schema));
const payload = Buffer.concat([
schemaHash,
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_NAME)),
ethUtil.sha3(Buffer.from(orderConstants.EXCHANGE_DOMAIN_VERSION)),
ethUtil.setLengthLeft(ethUtil.toBuffer(chainId), 32),
ethUtil.setLengthLeft(ethUtil.toBuffer(libs.address), 32),
]);
const expectedDomain = ethUtil.bufferToHex(ethUtil.sha3(payload));
const actualDomain = await libs.getDomainSeparator.callAsync();
expect(actualDomain).to.be.equal(expectedDomain);
});
it('should return a different domain separator if chainId is different', async () => {
const domain1 = await libsAlternateChain.getDomainSeparator.callAsync();
const domain2 = await libs.getDomainSeparator.callAsync();
expect(domain1).to.be.not.equal(domain2);
});
});
});