Add missing comments

This commit is contained in:
Fabio Berger
2018-08-03 21:27:01 +02:00
parent d9f09b5e1e
commit 343cd05363
9 changed files with 229 additions and 40 deletions

View File

@@ -31,14 +31,7 @@
"contracts_v2": "DummyERC20Token",
"postpublish": {
"assets": [],
"shouldPublishDocs": true,
"omitExports": [
"AbstractBalanceAndProxyAllowanceFetcher",
"AbstractOrderFilledCancelledFetcher",
"BalanceAndProxyAllowanceLazyStore",
"OrderFilledCancelledLazyStore",
"ExchangeTransferSimulator"
]
"shouldPublishDocs": true
}
},
"license": "Apache-2.0",

View File

@@ -1,6 +1,23 @@
import { BigNumber } from '@0xproject/utils';
/**l
* An abstract class to be implemented in order to use OrderStateUtils. The class that
* implements this interface must be capable of fetching the balance and proxyAllowance
* for an Ethereum address and assetData
*/
export abstract class AbstractBalanceAndProxyAllowanceFetcher {
/**
* Get balance of assetData for userAddress
* @param assetData AssetData for which to fetch the balance
* @param userAddress Ethereum address for which to fetch the balance
* @return Balance amount in base units
*/
public abstract async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber>;
/**
* Get the 0x asset proxy allowance of assetData for userAddress
* @param assetData AssetData for which to fetch the allowance
* @param userAddress Ethereum address for which to fetch the allowance
* @return Allowance amount in base units
*/
public abstract async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber>;
}

View File

@@ -1,7 +1,22 @@
import { BigNumber } from '@0xproject/utils';
/**l
* An abstract class to be implemented in order to use OrderStateUtils. The class that
* implements this interface must be capable of fetching the amount filled of an order
* and whether it's been cancelled.
*/
export abstract class AbstractOrderFilledCancelledFetcher {
/**
* Get the amount of the order's takerToken amount already filled
* @param orderHash OrderHash of order we are interested in
* @return FilledTakerAmount
*/
public abstract async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber>;
/**
* Whether an order is cancelled
* @param orderHash OrderHash of order we are interested in
* @return Whether or not the order is cancelled
*/
public abstract async isOrderCancelledAsync(orderHash: string): Promise<boolean>;
public abstract getZRXAssetData(): string;
}

View File

@@ -40,15 +40,37 @@ export const EIP712Utils = {
const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]);
return messageBuff;
},
/**
* Pad an address to 32 bytes
* @param address Address to pad
* @return padded address
*/
pad32Address(address: string): Buffer {
const addressBuffer = ethUtil.toBuffer(address);
const addressPadded = EIP712Utils.pad32Buffer(addressBuffer);
return addressPadded;
},
/**
* Pad an buffer to 32 bytes
* @param buffer Address to pad
* @return padded buffer
*/
pad32Buffer(buffer: Buffer): Buffer {
const bufferPadded = ethUtil.setLengthLeft(buffer, EIP712_VALUE_LENGTH);
return bufferPadded;
},
/**
* Hash together a EIP712 schema with the corresponding data
* @param schema EIP712-compliant schema
* @param data Data the complies to the schema
* @return A buffer containing the SHA256 hash of the schema and encoded data
*/
structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer {
const encodedData = EIP712Utils._encodeData(schema, data);
const schemaHash = EIP712Utils.compileSchema(schema);
const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]);
return hashBuffer;
},
_getDomainSeparatorSchemaBuffer(): Buffer {
return EIP712Utils.compileSchema(EIP712_DOMAIN_SCHEMA);
},
@@ -84,10 +106,4 @@ export const EIP712Utils = {
}
return encodedValues;
},
structHash(schema: EIP712Schema, data: { [key: string]: any }): Buffer {
const encodedData = EIP712Utils._encodeData(schema, data);
const schemaHash = EIP712Utils.compileSchema(schema);
const hashBuffer = crypto.solSHA3([schemaHash, ...encodedData]);
return hashBuffer;
},
};

View File

@@ -33,6 +33,10 @@ const ERR_MSG_MAPPING = {
},
};
/**
* An exchange transfer simulator which simulates asset transfers exactly how the
* 0x exchange contract would do them.
*/
export class ExchangeTransferSimulator {
private readonly _store: AbstractBalanceAndProxyAllowanceLazyStore;
private static _throwValidationError(
@@ -43,6 +47,11 @@ export class ExchangeTransferSimulator {
const errMsg = ERR_MSG_MAPPING[failureReason][tradeSide][transferType];
throw new Error(errMsg);
}
/**
* Instantiate a ExchangeTransferSimulator
* @param store A class that implements AbstractBalanceAndProxyAllowanceLazyStore
* @return an instance of ExchangeTransferSimulator
*/
constructor(store: AbstractBalanceAndProxyAllowanceLazyStore) {
this._store = store;
}

View File

@@ -91,6 +91,14 @@ export class OrderStateUtils {
throw new Error(ExchangeContractErrs.OrderFillRoundingError);
}
}
/**
* Instantiate OrderStateUtils
* @param balanceAndProxyAllowanceFetcher A class that is capable of fetching balances
* and proxyAllowances for Ethereum addresses. It must implement AbstractBalanceAndProxyAllowanceFetcher
* @param orderFilledCancelledFetcher A class that is capable of fetching whether an order
* is cancelled and how much of it has been filled. It must implement AbstractOrderFilledCancelledFetcher
* @return Instance of OrderStateUtils
*/
constructor(
balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher,
orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher,
@@ -98,6 +106,14 @@ export class OrderStateUtils {
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
}
/**
* Get the orderState for an "open" order (i.e where takerAddress=NULL_ADDRESS)
* This method will only check the maker's balance/allowance to calculate the
* OrderState.
* @param signedOrder The order of interest
* @return State relevant to the signedOrder, as well as whether the signedOrder is "valid".
* Validity is defined as a non-zero amount of the order can still be filled.
*/
public async getOpenOrderStateAsync(signedOrder: SignedOrder): Promise<OrderState> {
const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
@@ -127,6 +143,11 @@ export class OrderStateUtils {
return orderState;
}
}
/**
* Get state relevant to an order (i.e makerBalance, makerAllowance, filledTakerAssetAmount, etc...
* @param signedOrder Order of interest
* @return An instance of OrderRelevantState
*/
public async getOpenOrderRelevantStateAsync(signedOrder: SignedOrder): Promise<OrderRelevantState> {
const isMaker = true;
const sidedOrderRelevantState = await this._getSidedOrderRelevantStateAsync(
@@ -149,6 +170,12 @@ export class OrderStateUtils {
};
return orderRelevantState;
}
/**
* Get the max amount of the supplied order's takerAmount that could still be filled
* @param signedOrder Order of interest
* @param takerAddress Hypothetical taker of the order
* @return fillableTakerAssetAmount
*/
public async getMaxFillableTakerAssetAmountAsync(
signedOrder: SignedOrder,
takerAddress: string,
@@ -181,32 +208,6 @@ export class OrderStateUtils {
return fillableTakerAssetAmount;
}
public async getMaxFillableTakerAssetAmountForFailingOrderAsync(
signedOrder: SignedOrder,
takerAddress: string,
): Promise<BigNumber> {
// Get min of taker balance & allowance
const takerAssetBalanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(
signedOrder.takerAssetData,
takerAddress,
);
const takerAssetAllowanceOfTaker = await this._balanceAndProxyAllowanceFetcher.getProxyAllowanceAsync(
signedOrder.takerAssetData,
takerAddress,
);
const minTakerAssetAmount = BigNumber.min([takerAssetBalanceOfTaker, takerAssetAllowanceOfTaker]);
// get remainingFillAmount
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const filledTakerAssetAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
const remainingFillTakerAssetAmount = signedOrder.takerAssetAmount.minus(filledTakerAssetAmount);
if (minTakerAssetAmount.gte(remainingFillTakerAssetAmount)) {
return remainingFillTakerAssetAmount;
} else {
return minTakerAssetAmount;
}
}
private async _getSidedOrderRelevantStateAsync(
isMakerSide: boolean,
signedOrder: SignedOrder,

View File

@@ -12,8 +12,18 @@ import { orderHashUtils } from './order_hash';
import { signatureUtils } from './signature_utils';
import { utils } from './utils';
/**
* A utility class for validating orders
*/
export class OrderValidationUtils {
private readonly _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher;
/**
* A Typescript implementation mirroring the implementation of isRoundingError in the
* Exchange smart contract
* @param numerator Numerator value. When used to check an order, pass in `takerAssetFilledAmount`
* @param denominator Denominator value. When used to check an order, pass in `order.takerAssetAmount`
* @param target Target value. When used to check an order, pass in `order.makerAssetAmount`
*/
public static isRoundingError(numerator: BigNumber, denominator: BigNumber, target: BigNumber): boolean {
// Solidity's mulmod() in JS
// Source: https://solidity.readthedocs.io/en/latest/units-and-global-variables.html#mathematical-and-cryptographic-functions
@@ -31,6 +41,15 @@ export class OrderValidationUtils {
const isError = errPercentageTimes1000000.gt(1000);
return isError;
}
/**
* Validate that the maker & taker have sufficient balances/allowances
* to fill the supplied order to the fillTakerAssetAmount amount
* @param exchangeTradeEmulator ExchangeTradeEmulator to use
* @param signedOrder SignedOrder to test
* @param fillTakerAssetAmount Amount of takerAsset to fill the signedOrder
* @param senderAddress Sender of the fillOrder tx
* @param zrxAssetData AssetData for the ZRX token
*/
public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
signedOrder: SignedOrder,
@@ -104,9 +123,22 @@ export class OrderValidationUtils {
throw new Error(RevertReason.OrderUnfillable);
}
}
/**
* Instantiate OrderValidationUtils
* @param orderFilledCancelledFetcher A module that implements the AbstractOrderFilledCancelledFetcher
* @return An instance of OrderValidationUtils
*/
constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) {
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
}
/**
* Validate if the supplied order is fillable, and throw if it isn't
* @param exchangeTradeEmulator ExchangeTradeEmulator instance
* @param signedOrder SignedOrder of interest
* @param zrxAssetData ZRX assetData
* @param expectedFillTakerTokenAmount If supplied, this call will make sure this amount is fillable.
* If it isn't supplied, we check if the order is fillable for a non-zero amount
*/
public async validateOrderFillableOrThrowAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
signedOrder: SignedOrder,
@@ -132,6 +164,15 @@ export class OrderValidationUtils {
zrxAssetData,
);
}
/**
* Validate a call to FillOrder and throw if it wouldn't succeed
* @param exchangeTradeEmulator ExchangeTradeEmulator to use
* @param provider Web3 provider to use for JSON RPC requests
* @param signedOrder SignedOrder of interest
* @param fillTakerAssetAmount Amount we'd like to fill the order for
* @param takerAddress The taker of the order
* @param zrxAssetData ZRX asset data
*/
public async validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
provider: Provider,
@@ -187,6 +228,15 @@ export class OrderValidationUtils {
}
return filledTakerTokenAmount;
}
/**
* Validate a call to fillOrKillOrder and throw if it would fail
* @param exchangeTradeEmulator ExchangeTradeEmulator to use
* @param provider Web3 provider to use for JSON RPC requests
* @param signedOrder SignedOrder of interest
* @param fillTakerAssetAmount Amount we'd like to fill the order for
* @param takerAddress The taker of the order
* @param zrxAssetData ZRX asset data
*/
public async validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator: ExchangeTransferSimulator,
provider: Provider,

View File

@@ -21,11 +21,21 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
[userAddress: string]: BigNumber;
};
};
/**
* Instantiates a BalanceAndProxyAllowanceLazyStore
* @param balanceAndProxyAllowanceFetcher Class the implements the AbstractBalanceAndProxyAllowanceFetcher
* @return Instance of BalanceAndProxyAllowanceLazyStore
*/
constructor(balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher) {
this._balanceAndProxyAllowanceFetcher = balanceAndProxyAllowanceFetcher;
this._balance = {};
this._proxyAllowance = {};
}
/**
* Get a users balance of an asset
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
if (_.isUndefined(this._balance[assetData]) || _.isUndefined(this._balance[assetData][userAddress])) {
const balance = await this._balanceAndProxyAllowanceFetcher.getBalanceAsync(assetData, userAddress);
@@ -34,12 +44,22 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
const cachedBalance = this._balance[assetData][userAddress];
return cachedBalance;
}
/**
* Set the balance of an asset for a user
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public setBalance(assetData: string, userAddress: string, balance: BigNumber): void {
if (_.isUndefined(this._balance[assetData])) {
this._balance[assetData] = {};
}
this._balance[assetData][userAddress] = balance;
}
/**
* Clear the balance of an asset for a user
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public deleteBalance(assetData: string, userAddress: string): void {
if (!_.isUndefined(this._balance[assetData])) {
delete this._balance[assetData][userAddress];
@@ -48,6 +68,11 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
/**
* Get the 0x asset proxy allowance
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
if (
_.isUndefined(this._proxyAllowance[assetData]) ||
@@ -62,12 +87,22 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
const cachedProxyAllowance = this._proxyAllowance[assetData][userAddress];
return cachedProxyAllowance;
}
/**
* Set the 0x asset proxy allowance
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public setProxyAllowance(assetData: string, userAddress: string, proxyAllowance: BigNumber): void {
if (_.isUndefined(this._proxyAllowance[assetData])) {
this._proxyAllowance[assetData] = {};
}
this._proxyAllowance[assetData][userAddress] = proxyAllowance;
}
/**
* Clear the 0x asset proxy allowance
* @param assetData AssetData of interest
* @param userAddress Ethereum address of interest
*/
public deleteProxyAllowance(assetData: string, userAddress: string): void {
if (!_.isUndefined(this._proxyAllowance[assetData])) {
delete this._proxyAllowance[assetData][userAddress];
@@ -76,6 +111,11 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
/**
* Clear all ERC721 0x proxy allowances a user has on all items of a specific ERC721 contract
* @param tokenAddress ERc721 token address
* @param userAddress Owner Ethereum address
*/
public deleteAllERC721ProxyAllowance(tokenAddress: string, userAddress: string): void {
for (const assetData in this._proxyAllowance) {
if (this._proxyAllowance.hasOwnProperty(assetData)) {
@@ -90,6 +130,9 @@ export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProx
}
}
}
/**
* Delete all balances & allowances
*/
public deleteAll(): void {
this._balance = {};
this._proxyAllowance = {};

View File

@@ -15,11 +15,21 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
private _isCancelled: {
[orderHash: string]: boolean;
};
/**
* Instantiate a OrderFilledCancelledLazyStore
* @param orderFilledCancelledFetcher Class instance that implements the AbstractOrderFilledCancelledFetcher
* @returns An instance of OrderFilledCancelledLazyStore
*/
constructor(orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher) {
this._orderFilledCancelledFetcher = orderFilledCancelledFetcher;
this._filledTakerAmount = {};
this._isCancelled = {};
}
/**
* Get the filledTakerAssetAmount of an order
* @param orderHash OrderHash from order of interest
* @return filledTakerAssetAmount
*/
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
if (_.isUndefined(this._filledTakerAmount[orderHash])) {
const filledTakerAmount = await this._orderFilledCancelledFetcher.getFilledTakerAmountAsync(orderHash);
@@ -28,12 +38,26 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
const cachedFilledTakerAmount = this._filledTakerAmount[orderHash];
return cachedFilledTakerAmount;
}
/**
* Set the filledTakerAssetAmount of an order
* @param orderHash OrderHash from order of interest
* @param filledTakerAmount Desired filledTakerAssetAmount
*/
public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void {
this._filledTakerAmount[orderHash] = filledTakerAmount;
}
/**
* Clear the filledTakerAssetAmount of an order
* @param orderHash OrderHash from order of interest
*/
public deleteFilledTakerAmount(orderHash: string): void {
delete this._filledTakerAmount[orderHash];
}
/**
* Check if an order has been cancelled
* @param orderHash OrderHash from order of interest
* @return Whether the order has been cancelled
*/
public async getIsCancelledAsync(orderHash: string): Promise<boolean> {
if (_.isUndefined(this._isCancelled[orderHash])) {
const isCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash);
@@ -42,22 +66,43 @@ export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancell
const cachedIsCancelled = this._isCancelled[orderHash]; // tslint:disable-line:boolean-naming
return cachedIsCancelled;
}
/**
* Set whether an order has been cancelled or not
* @param orderHash OrderHash from order of interest
* @param isCancelled Whether this order should be cancelled or not
*/
public setIsCancelled(orderHash: string, isCancelled: boolean): void {
this._isCancelled[orderHash] = isCancelled;
}
/**
* Clear whether the order has been cancelled if already set
* @param orderHash OrderHash from order of interest
*/
public deleteIsCancelled(orderHash: string): void {
delete this._isCancelled[orderHash];
}
/**
* Clear all filled/cancelled state
*/
public deleteAll(): void {
this.deleteAllFilled();
this.deleteAllIsCancelled();
}
/**
* Clear all cancelled state
*/
public deleteAllIsCancelled(): void {
this._isCancelled = {};
}
/**
* Clear all filled state
*/
public deleteAllFilled(): void {
this._filledTakerAmount = {};
}
/**
* Get the ZRX assetData
*/
public getZRXAssetData(): string {
const zrxAssetData = this._orderFilledCancelledFetcher.getZRXAssetData();
return zrxAssetData;