Enable some new linter rules and fix the issues

This commit is contained in:
Leonid Logvinov 2017-11-22 15:43:17 -06:00
parent 87d34f9c7f
commit db2917b01c
No known key found for this signature in database
GPG Key ID: 0DD294BFDE8C95D4
12 changed files with 201 additions and 167 deletions

View File

@ -559,7 +559,7 @@ export class ExchangeWrapper extends ContractWrapper {
if (shouldValidate) { if (shouldValidate) {
const orderHash = utils.getOrderHashHex(order); const orderHash = utils.getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
this._orderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order, cancelTakerTokenAmount, unavailableTakerTokenAmount); order, cancelTakerTokenAmount, unavailableTakerTokenAmount);
} }
@ -613,7 +613,7 @@ export class ExchangeWrapper extends ContractWrapper {
for (const orderCancellationRequest of orderCancellationRequests) { for (const orderCancellationRequest of orderCancellationRequests) {
const orderHash = utils.getOrderHashHex(orderCancellationRequest.order); const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
this._orderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
orderCancellationRequest.order, orderCancellationRequest.takerTokenCancelAmount, orderCancellationRequest.order, orderCancellationRequest.takerTokenCancelAmount,
unavailableTakerTokenAmount, unavailableTakerTokenAmount,
); );
@ -767,7 +767,7 @@ export class ExchangeWrapper extends ContractWrapper {
assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount); assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
const orderHash = utils.getOrderHashHex(order); const orderHash = utils.getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
this._orderValidationUtils.validateCancelOrderThrowIfInvalid( OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order, cancelTakerTokenAmount, unavailableTakerTokenAmount); order, cancelTakerTokenAmount, unavailableTakerTokenAmount);
} }
/** /**
@ -884,4 +884,4 @@ export class ExchangeWrapper extends ContractWrapper {
const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase(); const tokenTransferProxyAddressLowerCase = tokenTransferProxyAddress.toLowerCase();
return tokenTransferProxyAddressLowerCase; return tokenTransferProxyAddressLowerCase;
} }
} } // tslint:disable:max-file-line-count

View File

@ -14,6 +14,18 @@ import {ContractWrapper} from './contract_wrapper';
export class TokenRegistryWrapper extends ContractWrapper { export class TokenRegistryWrapper extends ContractWrapper {
private _tokenRegistryContractIfExists?: TokenRegistryContract; private _tokenRegistryContractIfExists?: TokenRegistryContract;
private _contractAddressIfExists?: string; private _contractAddressIfExists?: string;
private static _createTokenFromMetadata(metadata: TokenMetadata): Token|undefined {
if (metadata[0] === constants.NULL_ADDRESS) {
return undefined;
}
const token = {
address: metadata[0],
name: metadata[1],
symbol: metadata[2],
decimals: metadata[3].toNumber(),
};
return token;
}
constructor(web3Wrapper: Web3Wrapper, contractAddressIfExists?: string) { constructor(web3Wrapper: Web3Wrapper, contractAddressIfExists?: string) {
super(web3Wrapper); super(web3Wrapper);
this._contractAddressIfExists = contractAddressIfExists; this._contractAddressIfExists = contractAddressIfExists;
@ -51,7 +63,7 @@ export class TokenRegistryWrapper extends ContractWrapper {
const tokenRegistryContract = await this._getTokenRegistryContractAsync(); const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address); const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address);
const token = this._createTokenFromMetadata(metadata); const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
return token; return token;
} }
public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string|undefined> { public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string|undefined> {
@ -76,14 +88,14 @@ export class TokenRegistryWrapper extends ContractWrapper {
assert.isString('symbol', symbol); assert.isString('symbol', symbol);
const tokenRegistryContract = await this._getTokenRegistryContractAsync(); const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol); const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol);
const token = this._createTokenFromMetadata(metadata); const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
return token; return token;
} }
public async getTokenByNameIfExistsAsync(name: string): Promise<Token|undefined> { public async getTokenByNameIfExistsAsync(name: string): Promise<Token|undefined> {
assert.isString('name', name); assert.isString('name', name);
const tokenRegistryContract = await this._getTokenRegistryContractAsync(); const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenByName.callAsync(name); const metadata = await tokenRegistryContract.getTokenByName.callAsync(name);
const token = this._createTokenFromMetadata(metadata); const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
return token; return token;
} }
/** /**
@ -103,18 +115,6 @@ export class TokenRegistryWrapper extends ContractWrapper {
return this._contractAddressIfExists; return this._contractAddressIfExists;
} }
} }
private _createTokenFromMetadata(metadata: TokenMetadata): Token|undefined {
if (metadata[0] === constants.NULL_ADDRESS) {
return undefined;
}
const token = {
address: metadata[0],
name: metadata[1],
symbol: metadata[2],
decimals: metadata[3].toNumber(),
};
return token;
}
private _invalidateContractInstance(): void { private _invalidateContractInstance(): void {
delete this._tokenRegistryContractIfExists; delete this._tokenRegistryContractIfExists;
} }

View File

@ -38,7 +38,7 @@ import {ExpirationWatcher} from './expiration_watcher';
interface DependentOrderHashes { interface DependentOrderHashes {
[makerAddress: string]: { [makerAddress: string]: {
[makerToken: string]: Set<string>, [makerToken: string]: Set<string>;
}; };
} }

View File

@ -12,13 +12,13 @@ export class BalanceAndProxyAllowanceLazyStore {
private token: TokenWrapper; private token: TokenWrapper;
private balance: { private balance: {
[tokenAddress: string]: { [tokenAddress: string]: {
[userAddress: string]: BigNumber, [userAddress: string]: BigNumber;
}, };
}; };
private proxyAllowance: { private proxyAllowance: {
[tokenAddress: string]: { [tokenAddress: string]: {
[userAddress: string]: BigNumber, [userAddress: string]: BigNumber;
}, };
}; };
constructor(token: TokenWrapper) { constructor(token: TokenWrapper) {
this.token = token; this.token = token;

View File

@ -11,10 +11,10 @@ import {BlockParamLiteral} from '../types';
export class OrderFilledCancelledLazyStore { export class OrderFilledCancelledLazyStore {
private exchange: ExchangeWrapper; private exchange: ExchangeWrapper;
private filledTakerAmount: { private filledTakerAmount: {
[orderHash: string]: BigNumber, [orderHash: string]: BigNumber;
}; };
private cancelledTakerAmount: { private cancelledTakerAmount: {
[orderHash: string]: BigNumber, [orderHash: string]: BigNumber;
}; };
constructor(exchange: ExchangeWrapper) { constructor(exchange: ExchangeWrapper) {
this.exchange = exchange; this.exchange = exchange;

View File

@ -6,6 +6,7 @@ import {JSONRPCPayload} from '../types';
* Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js * Source: https://github.com/MetaMask/provider-engine/blob/master/subproviders/subprovider.js
*/ */
export class EmptyWalletSubProvider { export class EmptyWalletSubProvider {
// tslint:disable-next-line:prefer-function-over-method
public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) { public handleRequest(payload: JSONRPCPayload, next: () => void, end: (err: Error|null, result: any) => void) {
switch (payload.method) { switch (payload.method) {
case 'eth_accounts': case 'eth_accounts':
@ -18,6 +19,7 @@ export class EmptyWalletSubProvider {
} }
} }
// Required to implement this method despite not needing it for this subprovider // Required to implement this method despite not needing it for this subprovider
// tslint:disable-next-line:prefer-function-over-method
public setEngine(engine: any) { public setEngine(engine: any) {
// noop // noop
} }

View File

@ -8,6 +8,15 @@ import {AbiType, ContractEventArgs, DecodedLogArgs, LogWithDecodedArgs, RawLog,
export class AbiDecoder { export class AbiDecoder {
private savedABIs: Web3.AbiDefinition[] = []; private savedABIs: Web3.AbiDefinition[] = [];
private methodIds: {[signatureHash: string]: Web3.EventAbi} = {}; private methodIds: {[signatureHash: string]: Web3.EventAbi} = {};
private static padZeros(address: string) {
let formatted = address;
if (_.startsWith(formatted, '0x')) {
formatted = formatted.slice(2);
}
formatted = _.padStart(formatted, 40, '0');
return `0x${formatted}`;
}
constructor(abiArrays: Web3.AbiDefinition[][]) { constructor(abiArrays: Web3.AbiDefinition[][]) {
_.map(abiArrays, this.addABI.bind(this)); _.map(abiArrays, this.addABI.bind(this));
} }
@ -32,7 +41,7 @@ export class AbiDecoder {
// Indexed parameters are stored in topics. Non-indexed ones in decodedData // Indexed parameters are stored in topics. Non-indexed ones in decodedData
let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++]; let value = param.indexed ? log.topics[topicsIndex++] : decodedData[dataIndex++];
if (param.type === SolidityTypes.Address) { if (param.type === SolidityTypes.Address) {
value = this.padZeros(new BigNumber(value).toString(16)); value = AbiDecoder.padZeros(new BigNumber(value).toString(16));
} else if (param.type === SolidityTypes.Uint256 || } else if (param.type === SolidityTypes.Uint256 ||
param.type === SolidityTypes.Uint8 || param.type === SolidityTypes.Uint8 ||
param.type === SolidityTypes.Uint) { param.type === SolidityTypes.Uint) {
@ -57,13 +66,4 @@ export class AbiDecoder {
}); });
this.savedABIs = this.savedABIs.concat(abiArray); this.savedABIs = this.savedABIs.concat(abiArray);
} }
private padZeros(address: string) {
let formatted = address;
if (_.startsWith(formatted, '0x')) {
formatted = formatted.slice(2);
}
formatted = _.padStart(formatted, 40, '0');
return `0x${formatted}`;
}
} }

View File

@ -36,6 +36,11 @@ const ERR_MSG_MAPPING = {
export class ExchangeTransferSimulator { export class ExchangeTransferSimulator {
private store: BalanceAndProxyAllowanceLazyStore; private store: BalanceAndProxyAllowanceLazyStore;
private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber; private UNLIMITED_ALLOWANCE_IN_BASE_UNITS: BigNumber;
private static throwValidationError(failureReason: FailureReason, tradeSide: TradeSide,
transferType: TransferType): never {
const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
throw new Error(errMsg);
}
constructor(token: TokenWrapper) { constructor(token: TokenWrapper) {
this.store = new BalanceAndProxyAllowanceLazyStore(token); this.store = new BalanceAndProxyAllowanceLazyStore(token);
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS = token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
@ -55,10 +60,10 @@ export class ExchangeTransferSimulator {
const balance = await this.store.getBalanceAsync(tokenAddress, from); const balance = await this.store.getBalanceAsync(tokenAddress, from);
const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, from); const proxyAllowance = await this.store.getProxyAllowanceAsync(tokenAddress, from);
if (proxyAllowance.lessThan(amountInBaseUnits)) { if (proxyAllowance.lessThan(amountInBaseUnits)) {
this.throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType); ExchangeTransferSimulator.throwValidationError(FailureReason.ProxyAllowance, tradeSide, transferType);
} }
if (balance.lessThan(amountInBaseUnits)) { if (balance.lessThan(amountInBaseUnits)) {
this.throwValidationError(FailureReason.Balance, tradeSide, transferType); ExchangeTransferSimulator.throwValidationError(FailureReason.Balance, tradeSide, transferType);
} }
await this.decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits); await this.decreaseProxyAllowanceAsync(tokenAddress, from, amountInBaseUnits);
await this.decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits); await this.decreaseBalanceAsync(tokenAddress, from, amountInBaseUnits);
@ -81,9 +86,4 @@ export class ExchangeTransferSimulator {
const balance = await this.store.getBalanceAsync(tokenAddress, userAddress); const balance = await this.store.getBalanceAsync(tokenAddress, userAddress);
this.store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits)); this.store.setBalance(tokenAddress, userAddress, balance.minus(amountInBaseUnits));
} }
private throwValidationError(failureReason: FailureReason, tradeSide: TradeSide,
transferType: TransferType): never {
const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
throw new Error(errMsg);
}
} }

View File

@ -25,6 +25,39 @@ const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001;
export class OrderStateUtils { export class OrderStateUtils {
private balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore; private balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore;
private orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore; private orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore;
private static validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
orderRelevantState.filledTakerTokenAmount,
);
const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
if (availableTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
}
if (orderRelevantState.makerBalance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
}
if (orderRelevantState.makerProxyAllowance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
}
if (!signedOrder.makerFee.eq(0)) {
if (orderRelevantState.makerFeeBalance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
}
if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
}
}
const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
.dividedBy(signedOrder.makerTokenAmount);
if (orderRelevantState.remainingFillableTakerTokenAmount
.lessThan(minFillableTakerTokenAmountWithinNoRoundingErrorRange)) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError);
}
// TODO Add linear function solver when maker token is ZRX #badass
// Return the max amount that's fillable
}
constructor(balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore, constructor(balanceAndProxyAllowanceLazyStore: BalanceAndProxyAllowanceLazyStore,
orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore) { orderFilledCancelledLazyStore: OrderFilledCancelledLazyStore) {
this.balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore; this.balanceAndProxyAllowanceLazyStore = balanceAndProxyAllowanceLazyStore;
@ -34,7 +67,7 @@ export class OrderStateUtils {
const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder); const orderRelevantState = await this.getOrderRelevantStateAsync(signedOrder);
const orderHash = ZeroEx.getOrderHashHex(signedOrder); const orderHash = ZeroEx.getOrderHashHex(signedOrder);
try { try {
this.validateIfOrderIsValid(signedOrder, orderRelevantState); OrderStateUtils.validateIfOrderIsValid(signedOrder, orderRelevantState);
const orderState: OrderStateValid = { const orderState: OrderStateValid = {
isValid: true, isValid: true,
orderHash, orderHash,
@ -103,37 +136,4 @@ export class OrderStateUtils {
}; };
return orderRelevantState; return orderRelevantState;
} }
private validateIfOrderIsValid(signedOrder: SignedOrder, orderRelevantState: OrderRelevantState): void {
const unavailableTakerTokenAmount = orderRelevantState.cancelledTakerTokenAmount.add(
orderRelevantState.filledTakerTokenAmount,
);
const availableTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
if (availableTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
}
if (orderRelevantState.makerBalance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerBalance);
}
if (orderRelevantState.makerProxyAllowance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerAllowance);
}
if (!signedOrder.makerFee.eq(0)) {
if (orderRelevantState.makerFeeBalance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeBalance);
}
if (orderRelevantState.makerFeeProxyAllowance.eq(0)) {
throw new Error(ExchangeContractErrs.InsufficientMakerFeeAllowance);
}
}
const minFillableTakerTokenAmountWithinNoRoundingErrorRange = signedOrder.takerTokenAmount
.dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR)
.dividedBy(signedOrder.makerTokenAmount);
if (orderRelevantState.remainingFillableTakerTokenAmount
.lessThan(minFillableTakerTokenAmountWithinNoRoundingErrorRange)) {
throw new Error(ExchangeContractErrs.OrderFillRoundingError);
}
// TODO Add linear function solver when maker token is ZRX #badass
// Return the max amount that's fillable
}
} }

View File

@ -13,6 +13,77 @@ import {ExchangeTransferSimulator} from './exchange_transfer_simulator';
export class OrderValidationUtils { export class OrderValidationUtils {
private tokenWrapper: TokenWrapper; private tokenWrapper: TokenWrapper;
private exchangeWrapper: ExchangeWrapper; private exchangeWrapper: ExchangeWrapper;
public static validateCancelOrderThrowIfInvalid(
order: Order, cancelTakerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber,
): void {
if (cancelTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
}
if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderCancelExpired);
}
}
public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber, senderAddress: string, zrxTokenAddress: string,
): Promise<void> {
const fillMakerTokenAmount = OrderValidationUtils.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.makerTokenAmount,
);
await exchangeTradeEmulator.transferFromAsync(
signedOrder.makerTokenAddress, signedOrder.maker, senderAddress, fillMakerTokenAmount,
TradeSide.Maker, TransferType.Trade,
);
await exchangeTradeEmulator.transferFromAsync(
signedOrder.takerTokenAddress, senderAddress, signedOrder.maker, fillTakerTokenAmount,
TradeSide.Taker, TransferType.Trade,
);
const makerFeeAmount = OrderValidationUtils.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.makerFee,
);
await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, TradeSide.Maker,
TransferType.Fee,
);
const takerFeeAmount = OrderValidationUtils.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.takerFee,
);
await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, senderAddress, signedOrder.feeRecipient, takerFeeAmount, TradeSide.Taker,
TransferType.Fee,
);
}
private static validateRemainingFillAmountNotZeroOrThrow(
takerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber,
) {
if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
}
}
private static validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderFillExpired);
}
}
private static getPartialAmount(numerator: BigNumber, denominator: BigNumber,
target: BigNumber): BigNumber {
const fillMakerTokenAmount = numerator
.mul(target)
.div(denominator)
.round(0);
return fillMakerTokenAmount;
}
constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) { constructor(tokenWrapper: TokenWrapper, exchangeWrapper: ExchangeWrapper) {
this.tokenWrapper = tokenWrapper; this.tokenWrapper = tokenWrapper;
this.exchangeWrapper = exchangeWrapper; this.exchangeWrapper = exchangeWrapper;
@ -22,15 +93,15 @@ export class OrderValidationUtils {
expectedFillTakerTokenAmount?: BigNumber): Promise<void> { expectedFillTakerTokenAmount?: BigNumber): Promise<void> {
const orderHash = utils.getOrderHashHex(signedOrder); const orderHash = utils.getOrderHashHex(signedOrder);
const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
this.validateRemainingFillAmountNotZeroOrThrow( OrderValidationUtils.validateRemainingFillAmountNotZeroOrThrow(
signedOrder.takerTokenAmount, unavailableTakerTokenAmount, signedOrder.takerTokenAmount, unavailableTakerTokenAmount,
); );
this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); OrderValidationUtils.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
if (!_.isUndefined(expectedFillTakerTokenAmount)) { if (!_.isUndefined(expectedFillTakerTokenAmount)) {
fillTakerTokenAmount = expectedFillTakerTokenAmount; fillTakerTokenAmount = expectedFillTakerTokenAmount;
} }
const fillMakerTokenAmount = this.getPartialAmount( const fillMakerTokenAmount = OrderValidationUtils.getPartialAmount(
fillTakerTokenAmount, fillTakerTokenAmount,
signedOrder.takerTokenAmount, signedOrder.takerTokenAmount,
signedOrder.makerTokenAmount, signedOrder.makerTokenAmount,
@ -39,7 +110,7 @@ export class OrderValidationUtils {
signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount, signedOrder.makerTokenAddress, signedOrder.maker, signedOrder.taker, fillMakerTokenAmount,
TradeSide.Maker, TransferType.Trade, TradeSide.Maker, TransferType.Trade,
); );
const makerFeeAmount = this.getPartialAmount( const makerFeeAmount = OrderValidationUtils.getPartialAmount(
fillTakerTokenAmount, fillTakerTokenAmount,
signedOrder.takerTokenAmount, signedOrder.takerTokenAmount,
signedOrder.makerFee, signedOrder.makerFee,
@ -61,18 +132,18 @@ export class OrderValidationUtils {
throw new Error(ZeroExError.InvalidSignature); throw new Error(ZeroExError.InvalidSignature);
} }
const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); const unavailableTakerTokenAmount = await this.exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
this.validateRemainingFillAmountNotZeroOrThrow( OrderValidationUtils.validateRemainingFillAmountNotZeroOrThrow(
signedOrder.takerTokenAmount, unavailableTakerTokenAmount, signedOrder.takerTokenAmount, unavailableTakerTokenAmount,
); );
if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) {
throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker);
} }
this.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); OrderValidationUtils.validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec);
const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount);
const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) ? const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) ?
remainingTakerTokenAmount : remainingTakerTokenAmount :
fillTakerTokenAmount; fillTakerTokenAmount;
await this.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator, signedOrder, filledTakerTokenAmount, takerAddress, zrxTokenAddress, exchangeTradeEmulator, signedOrder, filledTakerTokenAmount, takerAddress, zrxTokenAddress,
); );
@ -94,74 +165,4 @@ export class OrderValidationUtils {
throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount);
} }
} }
public validateCancelOrderThrowIfInvalid(
order: Order, cancelTakerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber,
): void {
if (cancelTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderCancelAmountZero);
}
if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled);
}
const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderCancelExpired);
}
}
public async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator, signedOrder: SignedOrder,
fillTakerTokenAmount: BigNumber, senderAddress: string, zrxTokenAddress: string): Promise<void> {
const fillMakerTokenAmount = this.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.makerTokenAmount,
);
await exchangeTradeEmulator.transferFromAsync(
signedOrder.makerTokenAddress, signedOrder.maker, senderAddress, fillMakerTokenAmount,
TradeSide.Maker, TransferType.Trade,
);
await exchangeTradeEmulator.transferFromAsync(
signedOrder.takerTokenAddress, senderAddress, signedOrder.maker, fillTakerTokenAmount,
TradeSide.Taker, TransferType.Trade,
);
const makerFeeAmount = this.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.makerFee,
);
await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, signedOrder.maker, signedOrder.feeRecipient, makerFeeAmount, TradeSide.Maker,
TransferType.Fee,
);
const takerFeeAmount = this.getPartialAmount(
fillTakerTokenAmount,
signedOrder.takerTokenAmount,
signedOrder.takerFee,
);
await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress, senderAddress, signedOrder.feeRecipient, takerFeeAmount, TradeSide.Taker,
TransferType.Fee,
);
}
private validateRemainingFillAmountNotZeroOrThrow(
takerTokenAmount: BigNumber, unavailableTakerTokenAmount: BigNumber,
) {
if (takerTokenAmount.eq(unavailableTakerTokenAmount)) {
throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero);
}
}
private validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber) {
const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec();
if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) {
throw new Error(ExchangeContractErrs.OrderFillExpired);
}
}
private getPartialAmount(numerator: BigNumber, denominator: BigNumber,
target: BigNumber): BigNumber {
const fillMakerTokenAmount = numerator
.mul(target)
.div(denominator)
.round(0);
return fillMakerTokenAmount;
}
} }

View File

@ -114,7 +114,7 @@ describe('OrderValidation', () => {
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount, makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, fillableAmount,
); );
// 27 <--> 28 // 27 <--> 28
signedOrder.ecSignature.v = 27 + (28 - signedOrder.ecSignature.v); signedOrder.ecSignature.v = (28 - signedOrder.ecSignature.v) + 27;
return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync( return expect(zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(
signedOrder, fillableAmount, takerAddress, signedOrder, fillableAmount, takerAddress,
)).to.be.rejectedWith(ZeroExError.InvalidSignature); )).to.be.rejectedWith(ZeroExError.InvalidSignature);
@ -230,7 +230,7 @@ describe('OrderValidation', () => {
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, takerAddress, fillableAmount, feeRecipient, makerAddress, takerAddress, fillableAmount, feeRecipient,
); );
await orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
@ -266,7 +266,7 @@ describe('OrderValidation', () => {
makerTokenAddress, takerTokenAddress, makerFee, takerFee, makerTokenAddress, takerTokenAddress, makerFee, takerFee,
makerAddress, ZeroEx.NULL_ADDRESS, fillableAmount, feeRecipient, makerAddress, ZeroEx.NULL_ADDRESS, fillableAmount, feeRecipient,
); );
await orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator, signedOrder, fillableAmount, takerAddress, zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
@ -301,7 +301,7 @@ describe('OrderValidation', () => {
const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync(
makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAmount, takerTokenAmount, makerTokenAddress, takerTokenAddress, makerAddress, takerAddress, makerTokenAmount, takerTokenAmount,
); );
await orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, takerTokenAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator, signedOrder, takerTokenAmount, takerAddress, zrxTokenAddress,
); );
expect(transferFromAsync.callCount).to.be.equal(4); expect(transferFromAsync.callCount).to.be.equal(4);
@ -316,7 +316,7 @@ describe('OrderValidation', () => {
fillableAmount, ZeroEx.NULL_ADDRESS, fillableAmount, ZeroEx.NULL_ADDRESS,
); );
const fillTakerTokenAmount = fillableAmount.div(2).round(0); const fillTakerTokenAmount = fillableAmount.div(2).round(0);
await orderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTransferSimulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress, exchangeTransferSimulator, signedOrder, fillTakerTokenAmount, takerAddress, zrxTokenAddress,
); );
const makerPartialFee = makerFee.div(2); const makerPartialFee = makerFee.div(2);

View File

@ -4,21 +4,24 @@
"tslint-react" "tslint-react"
], ],
"rules": { "rules": {
"adjacent-overload-signatures": true,
"arrow-parens": [true, "ban-single-arg-parens"], "arrow-parens": [true, "ban-single-arg-parens"],
"arrow-return-shorthand": true, "arrow-return-shorthand": true,
"await-promise": true, "await-promise": true,
"ordered-imports": [ "binary-expression-operand-order": true,
true,
{
"grouped-imports": true
}
],
"quotemark": [true, "single", "avoid-escape", "jsx-double"],
"callable-types": true, "callable-types": true,
"class-name": true,
"curly": true,
"eofline": true,
"encoding": true,
"import-spacing": true,
"indent": [true, "spaces", 4],
"interface-name": false, "interface-name": false,
"interface-over-type-literal": true, "interface-over-type-literal": true,
"object-literal-sort-keys": false, "linebreak-style": [true, "LF"],
"max-classes-per-file": false, "max-classes-per-file": false,
"max-classes-per-file": [true, 1],
"max-file-line-count": [true, 500],
"max-line-length": [true, 120], "max-line-length": [true, 120],
"member-access": true, "member-access": true,
"member-ordering": [true, "member-ordering": [true,
@ -26,19 +29,47 @@
"static-before-instance", "static-before-instance",
"variables-before-functions" "variables-before-functions"
], ],
"newline-before-return": false,
"new-parens": true,
"no-angle-bracket-type-assertion": true, "no-angle-bracket-type-assertion": true,
"no-boolean-literal-compare": true,
"no-default-export": true, "no-default-export": true,
"no-empty-interface": false, "no-empty-interface": false,
"no-floating-promises": true, "no-floating-promises": true,
"no-non-null-assertion": true, "no-non-null-assertion": true,
"no-parameter-reassignment": true, "no-parameter-reassignment": true,
"no-redundant-jsdoc": true,
"no-return-await": true, "no-return-await": true,
"no-string-throw": true, "no-string-throw": true,
"no-submodule-imports": false, "no-submodule-imports": false,
"no-unnecessary-type-assertion": true, "no-unnecessary-type-assertion": true,
"no-implicit-dependencies": [true, "dev"], "no-implicit-dependencies": [true, "dev"],
"number-literal-format": true,
"object-literal-sort-keys": false,
"ordered-imports": [
true,
{
"grouped-imports": true
}
],
"prefer-const": true, "prefer-const": true,
"prefer-for-of": true,
"prefer-function-over-method": true,
"promise-function-async": true, "promise-function-async": true,
"quotemark": [true, "single", "avoid-escape", "jsx-double"],
"semicolon": [true, "always"],
"space-before-function-paren": [
true,
{
"anonymous": "never",
"named": "never",
"method": "never",
"constructor": "never",
"asyncArrow": "always"
}
],
"space-within-parens": false,
"type-literal-delimiter": true,
"variable-name": [true, "variable-name": [true,
"ban-keywords", "ban-keywords",
"allow-pascal-case" "allow-pascal-case"