From f4a2e227e1a7224fbbe9c99d9aa033d176a9c4de Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 29 Jul 2018 21:58:39 +0200 Subject: Remove all in-package monorepo-scripts by adding doc gen/upload and aggregate release note publishing to publish script --- packages/order-utils/src/monorepo_scripts/postpublish.ts | 8 -------- packages/order-utils/src/monorepo_scripts/stage_docs.ts | 8 -------- 2 files changed, 16 deletions(-) delete mode 100644 packages/order-utils/src/monorepo_scripts/postpublish.ts delete mode 100644 packages/order-utils/src/monorepo_scripts/stage_docs.ts (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/monorepo_scripts/postpublish.ts b/packages/order-utils/src/monorepo_scripts/postpublish.ts deleted file mode 100644 index dcb99d0f7..000000000 --- a/packages/order-utils/src/monorepo_scripts/postpublish.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { postpublishUtils } from '@0xproject/monorepo-scripts'; - -import * as packageJSON from '../package.json'; -import * as tsConfigJSON from '../tsconfig.json'; - -const cwd = `${__dirname}/..`; -// tslint:disable-next-line:no-floating-promises -postpublishUtils.runAsync(packageJSON, tsConfigJSON, cwd); diff --git a/packages/order-utils/src/monorepo_scripts/stage_docs.ts b/packages/order-utils/src/monorepo_scripts/stage_docs.ts deleted file mode 100644 index e732ac8eb..000000000 --- a/packages/order-utils/src/monorepo_scripts/stage_docs.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { postpublishUtils } from '@0xproject/monorepo-scripts'; - -import * as packageJSON from '../package.json'; -import * as tsConfigJSON from '../tsconfig.json'; - -const cwd = `${__dirname}/..`; -// tslint:disable-next-line:no-floating-promises -postpublishUtils.publishDocsToStagingAsync(packageJSON, tsConfigJSON, cwd); -- cgit v1.2.3 From 4579e1637d9f4d0d7badb23336af6785330c6ecc Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sun, 29 Jul 2018 21:59:09 +0200 Subject: Add missing exports discovered by generating compact typedoc JSON --- packages/order-utils/src/index.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 76be63bb8..da53f620a 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -13,7 +13,16 @@ export { orderFactory } from './order_factory'; export { constants } from './constants'; export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; -export { OrderError, MessagePrefixType, MessagePrefixOpts, EIP712Parameter, EIP712Schema, EIP712Types } from './types'; +export { + OrderError, + MessagePrefixType, + MessagePrefixOpts, + EIP712Parameter, + EIP712Schema, + EIP712Types, + TradeSide, + TransferType, +} from './types'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; @@ -24,3 +33,5 @@ export { assetDataUtils } from './asset_data_utils'; export { EIP712Utils } from './eip712_utils'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; +export { Provider } from 'ethereum-types'; +export { SignedOrder, Order, ECSignature, ERC20AssetData, ERC721AssetData, AssetProxyId } from '@0xproject/types'; -- cgit v1.2.3 From d85ce6ac758a9a726c298b972a7c557e01f2ab2f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 3 Aug 2018 17:15:14 +0200 Subject: Make signature_util into an object literal so related functions are rendered together in the docs --- packages/order-utils/src/index.ts | 12 +- packages/order-utils/src/order_factory.ts | 9 +- packages/order-utils/src/order_validation_utils.ts | 4 +- packages/order-utils/src/signature_utils.ts | 559 +++++++++++---------- 4 files changed, 299 insertions(+), 285 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index da53f620a..5852a01b9 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -1,15 +1,5 @@ export { orderHashUtils } from './order_hash'; -export { - isValidSignatureAsync, - isValidPresignedSignatureAsync, - isValidWalletSignatureAsync, - isValidValidatorSignatureAsync, - isValidECSignature, - ecSignOrderHashAsync, - addSignedMessagePrefix, - parseECSignature, -} from './signature_utils'; -export { orderFactory } from './order_factory'; +export { signatureUtils } from './signature_utils'; export { constants } from './constants'; export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts index 803cb82b1..14122f353 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -6,7 +6,7 @@ import * as _ from 'lodash'; import { orderHashUtils } from './order_hash'; import { generatePseudoRandomSalt } from './salt'; -import { ecSignOrderHashAsync } from './signature_utils'; +import { signatureUtils } from './signature_utils'; import { MessagePrefixType } from './types'; export const orderFactory = { @@ -49,7 +49,12 @@ export const orderFactory = { prefixType: MessagePrefixType.EthSign, shouldAddPrefixBeforeCallingEthSign: false, }; - const ecSignature = await ecSignOrderHashAsync(provider, orderHash, makerAddress, messagePrefixOpts); + const ecSignature = await signatureUtils.ecSignOrderHashAsync( + provider, + orderHash, + makerAddress, + messagePrefixOpts, + ); const signature = getVRSHexString(ecSignature); const signedOrder: SignedOrder = _.assign(order, { signature }); return signedOrder; diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 67d747081..ccc6e653f 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -9,7 +9,7 @@ import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_f import { constants } from './constants'; import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; import { orderHashUtils } from './order_hash'; -import { isValidSignatureAsync } from './signature_utils'; +import { signatureUtils } from './signature_utils'; import { utils } from './utils'; export class OrderValidationUtils { @@ -147,7 +147,7 @@ export class OrderValidationUtils { throw new Error(RevertReason.InvalidTakerAmount); } const orderHash = orderHashUtils.getOrderHashHex(signedOrder); - const isValid = await isValidSignatureAsync( + const isValid = await signatureUtils.isValidSignatureAsync( provider, orderHash, signedOrder.signature, diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 26fb24705..2dc465256 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -13,288 +13,307 @@ import { IWalletContract } from './generated_contract_wrappers/i_wallet'; import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types'; import { utils } from './utils'; -/** - * Verifies that the provided signature is valid according to the 0x Protocol smart contracts - * @param data The hex encoded data signed by the supplied signature. - * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType]. - * E.g [vrs][SignatureType.EIP712] - * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. - * @return Whether the signature is valid for the supplied signerAddress and data. - */ -export async function isValidSignatureAsync( - provider: Provider, - data: string, - signature: string, - signerAddress: string, -): Promise { - assert.isWeb3Provider('provider', provider); - assert.isHexString('data', data); - assert.isHexString('signature', signature); - assert.isETHAddressHex('signerAddress', signerAddress); - const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature); - if (_.isUndefined(signatureTypeIndexIfExists)) { - throw new Error(`Unrecognized signatureType in signature: ${signature}`); - } - - switch (signatureTypeIndexIfExists) { - case SignatureType.Illegal: - case SignatureType.Invalid: - return false; - - case SignatureType.EIP712: { - const ecSignature = parseECSignature(signature); - return isValidECSignature(data, ecSignature, signerAddress); - } - - case SignatureType.EthSign: { - const ecSignature = parseECSignature(signature); - const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.EthSign); - return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); +export const signatureUtils = { + /** + * Verifies that the provided signature is valid according to the 0x Protocol smart contracts + * @param provider Web3 provider to use for all JSON RPC requests + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType]. + * E.g [vrs][SignatureType.EIP712] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the signature is valid for the supplied signerAddress and data. + */ + async isValidSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, + ): Promise { + assert.isWeb3Provider('provider', provider); + assert.isHexString('data', data); + assert.isHexString('signature', signature); + assert.isETHAddressHex('signerAddress', signerAddress); + const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature); + if (_.isUndefined(signatureTypeIndexIfExists)) { + throw new Error(`Unrecognized signatureType in signature: ${signature}`); } - case SignatureType.Caller: - // HACK: We currently do not "validate" the caller signature type. - // It can only be validated during Exchange contract execution. - throw new Error('Caller signature type cannot be validated off-chain'); - - case SignatureType.Wallet: { - const isValid = await isValidWalletSignatureAsync(provider, data, signature, signerAddress); - return isValid; - } - - case SignatureType.Validator: { - const isValid = await isValidValidatorSignatureAsync(provider, data, signature, signerAddress); - return isValid; + switch (signatureTypeIndexIfExists) { + case SignatureType.Illegal: + case SignatureType.Invalid: + return false; + + case SignatureType.EIP712: { + const ecSignature = signatureUtils.parseECSignature(signature); + return signatureUtils.isValidECSignature(data, ecSignature, signerAddress); + } + + case SignatureType.EthSign: { + const ecSignature = signatureUtils.parseECSignature(signature); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, MessagePrefixType.EthSign); + return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); + } + + case SignatureType.Caller: + // HACK: We currently do not "validate" the caller signature type. + // It can only be validated during Exchange contract execution. + throw new Error('Caller signature type cannot be validated off-chain'); + + case SignatureType.Wallet: { + const isValid = await signatureUtils.isValidWalletSignatureAsync( + provider, + data, + signature, + signerAddress, + ); + return isValid; + } + + case SignatureType.Validator: { + const isValid = await signatureUtils.isValidValidatorSignatureAsync( + provider, + data, + signature, + signerAddress, + ); + return isValid; + } + + case SignatureType.PreSigned: { + return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress); + } + + case SignatureType.Trezor: { + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, MessagePrefixType.Trezor); + const ecSignature = signatureUtils.parseECSignature(signature); + return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); + } + + default: + throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); } - - case SignatureType.PreSigned: { - return isValidPresignedSignatureAsync(provider, data, signerAddress); - } - - case SignatureType.Trezor: { - const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.Trezor); - const ecSignature = parseECSignature(signature); - return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); + }, + /** + * Verifies that the provided presigned signature is valid according to the 0x Protocol smart contracts + * @param provider Web3 provider to use for all JSON RPC requests + * @param data The hex encoded data signed by the supplied signature + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress + */ + async isValidPresignedSignatureAsync(provider: Provider, data: string, signerAddress: string): Promise { + assert.isWeb3Provider('provider', provider); + assert.isHexString('data', data); + assert.isETHAddressHex('signerAddress', signerAddress); + const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider); + const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress); + return isValid; + }, + /** + * Verifies that the provided wallet signature is valid according to the 0x Protocol smart contracts + * @param provider Web3 provider to use for all JSON RPC requests + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress. + */ + async isValidWalletSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, + ): Promise { + assert.isWeb3Provider('provider', provider); + assert.isHexString('data', data); + assert.isHexString('signature', signature); + assert.isETHAddressHex('signerAddress', signerAddress); + // tslint:disable-next-line:custom-no-magic-numbers + const signatureWithoutType = signature.slice(-2); + const walletContract = new IWalletContract(artifacts.IWallet.compilerOutput.abi, signerAddress, provider); + const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType); + return isValid; + }, + /** + * Verifies that the provided validator signature is valid according to the 0x Protocol smart contracts + * @param provider Web3 provider to use for all JSON RPC requests + * @param data The hex encoded data signed by the supplied signature. + * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the data was preSigned by the supplied signerAddress. + */ + async isValidValidatorSignatureAsync( + provider: Provider, + data: string, + signature: string, + signerAddress: string, + ): Promise { + assert.isWeb3Provider('provider', provider); + assert.isHexString('data', data); + assert.isHexString('signature', signature); + assert.isETHAddressHex('signerAddress', signerAddress); + const validatorSignature = parseValidatorSignature(signature); + const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider); + const isValidatorApproved = await exchangeContract.allowedValidators.callAsync( + signerAddress, + validatorSignature.validatorAddress, + ); + if (!isValidatorApproved) { + throw new Error( + `Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`, + ); } - default: - throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); - } -} - -/** - * Verifies that the provided presigned signature is valid according to the 0x Protocol smart contracts - * @param data The hex encoded data signed by the supplied signature. - * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] - * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. - * @return Whether the data was preSigned by the supplied signerAddress. - */ -export async function isValidPresignedSignatureAsync( - provider: Provider, - data: string, - signerAddress: string, -): Promise { - assert.isWeb3Provider('provider', provider); - assert.isHexString('data', data); - assert.isETHAddressHex('signerAddress', signerAddress); - const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider); - const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress); - return isValid; -} - -/** - * Verifies that the provided wallet signature is valid according to the 0x Protocol smart contracts - * @param data The hex encoded data signed by the supplied signature. - * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] - * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. - * @return Whether the data was preSigned by the supplied signerAddress. - */ -export async function isValidWalletSignatureAsync( - provider: Provider, - data: string, - signature: string, - signerAddress: string, -): Promise { - assert.isWeb3Provider('provider', provider); - assert.isHexString('data', data); - assert.isHexString('signature', signature); - assert.isETHAddressHex('signerAddress', signerAddress); - // tslint:disable-next-line:custom-no-magic-numbers - const signatureWithoutType = signature.slice(-2); - const walletContract = new IWalletContract(artifacts.IWallet.compilerOutput.abi, signerAddress, provider); - const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType); - return isValid; -} - -/** - * Verifies that the provided validator signature is valid according to the 0x Protocol smart contracts - * @param data The hex encoded data signed by the supplied signature. - * @param signature A hex encoded presigned 0x Protocol signature made up of: [SignatureType.Presigned] - * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. - * @return Whether the data was preSigned by the supplied signerAddress. - */ -export async function isValidValidatorSignatureAsync( - provider: Provider, - data: string, - signature: string, - signerAddress: string, -): Promise { - assert.isWeb3Provider('provider', provider); - assert.isHexString('data', data); - assert.isHexString('signature', signature); - assert.isETHAddressHex('signerAddress', signerAddress); - const validatorSignature = parseValidatorSignature(signature); - const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider); - const isValidatorApproved = await exchangeContract.allowedValidators.callAsync( - signerAddress, - validatorSignature.validatorAddress, - ); - if (!isValidatorApproved) { - throw new Error(`Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`); - } - - const validatorContract = new IValidatorContract(artifacts.IValidator.compilerOutput.abi, signerAddress, provider); - const isValid = await validatorContract.isValidSignature.callAsync( - data, - signerAddress, - validatorSignature.signature, - ); - return isValid; -} - -/** - * Checks if the supplied elliptic curve signature corresponds to signing `data` with - * the private key corresponding to `signerAddress` - * @param data The hex encoded data signed by the supplied signature. - * @param signature An object containing the elliptic curve signature parameters. - * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. - * @return Whether the ECSignature is valid. - */ -export function isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean { - assert.isHexString('data', data); - assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); - assert.isETHAddressHex('signerAddress', signerAddress); - - const msgHashBuff = ethUtil.toBuffer(data); - try { - const pubKey = ethUtil.ecrecover( - msgHashBuff, - signature.v, - ethUtil.toBuffer(signature.r), - ethUtil.toBuffer(signature.s), + const validatorContract = new IValidatorContract( + artifacts.IValidator.compilerOutput.abi, + signerAddress, + provider, ); - const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); - return retrievedAddress === signerAddress; - } catch (err) { - return false; - } -} - -/** - * Signs an orderHash and returns it's elliptic curve signature. - * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 - * @param orderHash Hex encoded orderHash to sign. - * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address - * must be available via the Provider supplied to 0x.js. - * @param hashPrefixOpts Different signers add/require different prefixes be appended to the message being signed. - * Since we cannot know ahead of time which signer you are using, you must supply both a prefixType and - * whether it must be added before calling `eth_sign` (some signers add it themselves) - * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash. - */ -export async function ecSignOrderHashAsync( - provider: Provider, - orderHash: string, - signerAddress: string, - messagePrefixOpts: MessagePrefixOpts, -): Promise { - assert.isWeb3Provider('provider', provider); - assert.isHexString('orderHash', orderHash); - assert.isETHAddressHex('signerAddress', signerAddress); - const web3Wrapper = new Web3Wrapper(provider); - await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); - const normalizedSignerAddress = signerAddress.toLowerCase(); - - let msgHashHex = orderHash; - const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType); - if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) { - msgHashHex = prefixedMsgHashHex; - } - const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); - - // HACK: There is no consensus on whether the signatureHex string should be formatted as - // v + r + s OR r + s + v, and different clients (even different versions of the same client) - // return the signature params in different orders. In order to support all client implementations, - // we parse the signature in both ways, and evaluate if either one is a valid signature. - // tslint:disable-next-line:custom-no-magic-numbers - const validVParamValues = [27, 28]; - const ecSignatureVRS = parseSignatureHexAsVRS(signature); - if (_.includes(validVParamValues, ecSignatureVRS.v)) { - const isValidVRSSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureVRS, normalizedSignerAddress); - if (isValidVRSSignature) { - return ecSignatureVRS; + const isValid = await validatorContract.isValidSignature.callAsync( + data, + signerAddress, + validatorSignature.signature, + ); + return isValid; + }, + /** + * Checks if the supplied elliptic curve signature corresponds to signing `data` with + * the private key corresponding to `signerAddress` + * @param data The hex encoded data signed by the supplied signature. + * @param signature An object containing the elliptic curve signature parameters. + * @param signerAddress The hex encoded address that signed the data, producing the supplied signature. + * @return Whether the ECSignature is valid. + */ + isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean { + assert.isHexString('data', data); + assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); + assert.isETHAddressHex('signerAddress', signerAddress); + + const msgHashBuff = ethUtil.toBuffer(data); + try { + const pubKey = ethUtil.ecrecover( + msgHashBuff, + signature.v, + ethUtil.toBuffer(signature.r), + ethUtil.toBuffer(signature.s), + ); + const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); + return retrievedAddress === signerAddress; + } catch (err) { + return false; } - } - - const ecSignatureRSV = parseSignatureHexAsRSV(signature); - if (_.includes(validVParamValues, ecSignatureRSV.v)) { - const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress); - if (isValidRSVSignature) { - return ecSignatureRSV; + }, + /** + * Signs an orderHash and returns it's elliptic curve signature. + * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 + * @param provider The provider to use for JSON RPC calls + * @param orderHash Hex encoded orderHash to sign. + * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address + * must be available via the Provider supplied to 0x.js. + * @param messagePrefixOpts Different signers add/require different prefixes be appended to the message being signed. + * Since we cannot know ahead of time which signer you are using, you must supply both a prefixType and + * whether it must be added before calling `eth_sign` (some signers add it themselves) + * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash. + */ + async ecSignOrderHashAsync( + provider: Provider, + orderHash: string, + signerAddress: string, + messagePrefixOpts: MessagePrefixOpts, + ): Promise { + assert.isWeb3Provider('provider', provider); + assert.isHexString('orderHash', orderHash); + assert.isETHAddressHex('signerAddress', signerAddress); + const web3Wrapper = new Web3Wrapper(provider); + await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper); + const normalizedSignerAddress = signerAddress.toLowerCase(); + + let msgHashHex = orderHash; + const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType); + if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) { + msgHashHex = prefixedMsgHashHex; } - } - - throw new Error(OrderError.InvalidSignature); -} - -/** - * Adds the relevant prefix to the message being signed. - * @param message Message to sign - * @param messagePrefixType The type of message prefix to add. Different signers expect - * specific message prefixes. - * @return Prefixed message - */ -export function addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string { - assert.isString('message', message); - assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType); - switch (messagePrefixType) { - case MessagePrefixType.None: - return message; - - case MessagePrefixType.EthSign: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; + const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); + + // HACK: There is no consensus on whether the signatureHex string should be formatted as + // v + r + s OR r + s + v, and different clients (even different versions of the same client) + // return the signature params in different orders. In order to support all client implementations, + // we parse the signature in both ways, and evaluate if either one is a valid signature. + // tslint:disable-next-line:custom-no-magic-numbers + const validVParamValues = [27, 28]; + const ecSignatureVRS = parseSignatureHexAsVRS(signature); + if (_.includes(validVParamValues, ecSignatureVRS.v)) { + const isValidVRSSignature = signatureUtils.isValidECSignature( + prefixedMsgHashHex, + ecSignatureVRS, + normalizedSignerAddress, + ); + if (isValidVRSSignature) { + return ecSignatureVRS; + } } - case MessagePrefixType.Trezor: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; + const ecSignatureRSV = parseSignatureHexAsRSV(signature); + if (_.includes(validVParamValues, ecSignatureRSV.v)) { + const isValidRSVSignature = signatureUtils.isValidECSignature( + prefixedMsgHashHex, + ecSignatureRSV, + normalizedSignerAddress, + ); + if (isValidRSVSignature) { + return ecSignatureRSV; + } } - default: - throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`); - } -} - -/** - * Parse a 0x protocol hex-encoded signature string into it's ECSignature components - * @param signature A hex encoded ecSignature 0x Protocol signature - * @return An ECSignature object with r,s,v parameters - */ -export function parseECSignature(signature: string): ECSignature { - assert.isHexString('signature', signature); - const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; - assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); - - // tslint:disable-next-line:custom-no-magic-numbers - const vrsHex = signature.slice(0, -2); - const ecSignature = parseSignatureHexAsVRS(vrsHex); - - return ecSignature; -} + throw new Error(OrderError.InvalidSignature); + }, + /** + * Adds the relevant prefix to the message being signed. + * @param message Message to sign + * @param messagePrefixType The type of message prefix to add. Different signers expect + * specific message prefixes. + * @return Prefixed message + */ + addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string { + assert.isString('message', message); + assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType); + switch (messagePrefixType) { + case MessagePrefixType.None: + return message; + + case MessagePrefixType.EthSign: { + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; + } + + case MessagePrefixType.Trezor: { + const msgBuff = ethUtil.toBuffer(message); + const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); + const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); + return prefixedMsgHex; + } + + default: + throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`); + } + }, + /** + * Parse a 0x protocol hex-encoded signature string into it's ECSignature components + * @param signature A hex encoded ecSignature 0x Protocol signature + * @return An ECSignature object with r,s,v parameters + */ + parseECSignature(signature: string): ECSignature { + assert.isHexString('signature', signature); + const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; + assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); + + // tslint:disable-next-line:custom-no-magic-numbers + const vrsHex = signature.slice(0, -2); + const ecSignature = parseSignatureHexAsVRS(vrsHex); + + return ecSignature; + }, +}; function hashTrezorPersonalMessage(message: Buffer): Buffer { const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength)); -- cgit v1.2.3 From 1588f4ac391038b9206ba2220226f2f11fd8fe0f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 3 Aug 2018 17:51:58 +0200 Subject: Stop exporting crypto --- packages/order-utils/src/index.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 5852a01b9..c685a94e2 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -1,7 +1,6 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; export { constants } from './constants'; -export { crypto } from './crypto'; export { generatePseudoRandomSalt } from './salt'; export { OrderError, -- cgit v1.2.3 From 9337d207a1f4d5c28b42d1364a2a8bdc0dc841cf Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 3 Aug 2018 19:41:55 +0200 Subject: Stop exporting constants from order-utils --- packages/order-utils/src/index.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index c685a94e2..85d26de81 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -1,6 +1,5 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; -export { constants } from './constants'; export { generatePseudoRandomSalt } from './salt'; export { OrderError, -- cgit v1.2.3 From 343cd05363166a7a93fca361d5547b39f3e83d99 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 3 Aug 2018 21:27:01 +0200 Subject: Add missing comments --- ...abstract_balance_and_proxy_allowance_fetcher.ts | 17 +++++++ .../abstract_order_filled_cancelled_fetcher.ts | 15 ++++++ packages/order-utils/src/eip712_utils.ts | 28 +++++++++--- .../order-utils/src/exchange_transfer_simulator.ts | 9 ++++ packages/order-utils/src/order_state_utils.ts | 53 +++++++++++----------- packages/order-utils/src/order_validation_utils.ts | 50 ++++++++++++++++++++ .../balance_and_proxy_allowance_lazy_store.ts | 43 ++++++++++++++++++ .../src/store/order_filled_cancelled_lazy_store.ts | 45 ++++++++++++++++++ 8 files changed, 228 insertions(+), 32 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts index b2760d98e..7cb859ca7 100644 --- a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts +++ b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts @@ -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; + /** + * 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; } diff --git a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts index 865ea4e43..d2b01c359 100644 --- a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts +++ b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts @@ -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; + /** + * 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; public abstract getZRXAssetData(): string; } diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index 2594e6d6d..c7b20f824 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -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; - }, }; diff --git a/packages/order-utils/src/exchange_transfer_simulator.ts b/packages/order-utils/src/exchange_transfer_simulator.ts index c3a4f9c2a..81c849c64 100644 --- a/packages/order-utils/src/exchange_transfer_simulator.ts +++ b/packages/order-utils/src/exchange_transfer_simulator.ts @@ -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; } diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 189bf4180..cb08c5ae2 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -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 { 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 { 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 { - // 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, diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index ccc6e653f..972e6f6d6 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -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, diff --git a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts index 5a2c1d7ff..8a65178b0 100644 --- a/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts +++ b/packages/order-utils/src/store/balance_and_proxy_allowance_lazy_store.ts @@ -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 { 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 { 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 = {}; diff --git a/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts b/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts index 336c6d0ba..6155c2064 100644 --- a/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts +++ b/packages/order-utils/src/store/order_filled_cancelled_lazy_store.ts @@ -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 { 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 { 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; -- cgit v1.2.3 From bcc1ad20869d10fb16678619e6fa411e53a07742 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 3 Aug 2018 21:30:57 +0200 Subject: Re-order index.ts and add missing types --- packages/order-utils/src/index.ts | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 85d26de81..50a5106ca 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -1,6 +1,29 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; export { generatePseudoRandomSalt } from './salt'; +export { assetDataUtils } from './asset_data_utils'; +export { EIP712Utils } from './eip712_utils'; + +export { OrderStateUtils } from './order_state_utils'; +export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; +export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; + +export { OrderValidationUtils } from './order_validation_utils'; +export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; +export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; +export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store'; + +export { Provider } from 'ethereum-types'; +export { + SignedOrder, + Order, + OrderRelevantState, + OrderState, + ECSignature, + ERC20AssetData, + ERC721AssetData, + AssetProxyId, +} from '@0xproject/types'; export { OrderError, MessagePrefixType, @@ -11,15 +34,3 @@ export { TradeSide, TransferType, } from './types'; -export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; -export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; -export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; -export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store'; -export { RemainingFillableCalculator } from './remaining_fillable_calculator'; -export { OrderStateUtils } from './order_state_utils'; -export { assetDataUtils } from './asset_data_utils'; -export { EIP712Utils } from './eip712_utils'; -export { OrderValidationUtils } from './order_validation_utils'; -export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; -export { Provider } from 'ethereum-types'; -export { SignedOrder, Order, ECSignature, ERC20AssetData, ERC721AssetData, AssetProxyId } from '@0xproject/types'; -- cgit v1.2.3 From 7759e67a5a51a213e19083f63df13e80ea4aefa2 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Sat, 4 Aug 2018 10:09:08 +0200 Subject: Rename EIP712Utils to eip712Utils since objectLiterals shouldn't start with caps --- packages/order-utils/src/eip712_utils.ts | 20 ++++++++++---------- packages/order-utils/src/index.ts | 2 +- packages/order-utils/src/order_hash.ts | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/eip712_utils.ts b/packages/order-utils/src/eip712_utils.ts index c7b20f824..b303c93dc 100644 --- a/packages/order-utils/src/eip712_utils.ts +++ b/packages/order-utils/src/eip712_utils.ts @@ -18,14 +18,14 @@ const EIP712_DOMAIN_SCHEMA: EIP712Schema = { ], }; -export const EIP712Utils = { +export const eip712Utils = { /** * Compiles the EIP712Schema and returns the hash of the schema. * @param schema The EIP712 schema. * @return The hash of the compiled schema */ compileSchema(schema: EIP712Schema): Buffer { - const eip712Schema = EIP712Utils._encodeType(schema); + const eip712Schema = eip712Utils._encodeType(schema); const eip712SchemaHashBuffer = crypto.solSHA3([eip712Schema]); return eip712SchemaHashBuffer; }, @@ -36,7 +36,7 @@ export const EIP712Utils = { * @return The hash of an EIP712 message with domain separator prefixed */ createEIP712Message(hashStruct: Buffer, contractAddress: string): Buffer { - const domainSeparatorHashBuffer = EIP712Utils._getDomainSeparatorHashBuffer(contractAddress); + const domainSeparatorHashBuffer = eip712Utils._getDomainSeparatorHashBuffer(contractAddress); const messageBuff = crypto.solSHA3([EIP191_PREFIX, domainSeparatorHashBuffer, hashStruct]); return messageBuff; }, @@ -47,7 +47,7 @@ export const EIP712Utils = { */ pad32Address(address: string): Buffer { const addressBuffer = ethUtil.toBuffer(address); - const addressPadded = EIP712Utils.pad32Buffer(addressBuffer); + const addressPadded = eip712Utils.pad32Buffer(addressBuffer); return addressPadded; }, /** @@ -66,17 +66,17 @@ export const EIP712Utils = { * @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 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); + return eip712Utils.compileSchema(EIP712_DOMAIN_SCHEMA); }, _getDomainSeparatorHashBuffer(exchangeAddress: string): Buffer { - const domainSeparatorSchemaBuffer = EIP712Utils._getDomainSeparatorSchemaBuffer(); - const encodedData = EIP712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { + const domainSeparatorSchemaBuffer = eip712Utils._getDomainSeparatorSchemaBuffer(); + const encodedData = eip712Utils._encodeData(EIP712_DOMAIN_SCHEMA, { name: EIP712_DOMAIN_NAME, version: EIP712_DOMAIN_VERSION, verifyingContract: exchangeAddress, @@ -99,7 +99,7 @@ export const EIP712Utils = { } else if (parameter.type === EIP712Types.Uint256) { encodedValues.push(value); } else if (parameter.type === EIP712Types.Address) { - encodedValues.push(EIP712Utils.pad32Address(value)); + encodedValues.push(eip712Utils.pad32Address(value)); } else { throw new Error(`Unable to encode ${parameter.type}`); } diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 50a5106ca..f3b00a583 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -2,7 +2,7 @@ export { orderHashUtils } from './order_hash'; export { signatureUtils } from './signature_utils'; export { generatePseudoRandomSalt } from './salt'; export { assetDataUtils } from './asset_data_utils'; -export { EIP712Utils } from './eip712_utils'; +export { eip712Utils } from './eip712_utils'; export { OrderStateUtils } from './order_state_utils'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; diff --git a/packages/order-utils/src/order_hash.ts b/packages/order-utils/src/order_hash.ts index 54c500653..8e98f8767 100644 --- a/packages/order-utils/src/order_hash.ts +++ b/packages/order-utils/src/order_hash.ts @@ -3,7 +3,7 @@ import { Order, SignedOrder } from '@0xproject/types'; import * as _ from 'lodash'; import { assert } from './assert'; -import { EIP712Utils } from './eip712_utils'; +import { eip712Utils } from './eip712_utils'; import { EIP712Schema, EIP712Types } from './types'; const INVALID_TAKER_FORMAT = 'instance.takerAddress is not of a type(s) string'; @@ -69,11 +69,11 @@ export const orderHashUtils = { * @return The resulting orderHash from hashing the supplied order as a Buffer */ getOrderHashBuffer(order: SignedOrder | Order): Buffer { - const orderParamsHashBuff = EIP712Utils.structHash(EIP712_ORDER_SCHEMA, order); - const orderHashBuff = EIP712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); + const orderParamsHashBuff = eip712Utils.structHash(EIP712_ORDER_SCHEMA, order); + const orderHashBuff = eip712Utils.createEIP712Message(orderParamsHashBuff, order.exchangeAddress); return orderHashBuff; }, _getOrderSchemaBuffer(): Buffer { - return EIP712Utils.compileSchema(EIP712_ORDER_SCHEMA); + return eip712Utils.compileSchema(EIP712_ORDER_SCHEMA); }, }; -- cgit v1.2.3 From 32ab4dcac723e189ce8a1be796f0539975a48eae Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 15:52:30 -0400 Subject: Implement rate utils --- packages/order-utils/src/index.ts | 1 + packages/order-utils/src/rate_utils.ts | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 packages/order-utils/src/rate_utils.ts (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 858f500c6..a52f936b6 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -33,3 +33,4 @@ export { EIP712Utils } from './eip712_utils'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { marketUtils } from './market_utils'; +export { rateUtils } from './rate_utils'; diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts new file mode 100644 index 000000000..212431e0c --- /dev/null +++ b/packages/order-utils/src/rate_utils.ts @@ -0,0 +1,44 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; + +import { assert } from './assert'; +import { constants } from './constants'; + +export const rateUtils = { + /** + * Takes a signed order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset + * is required to cover the fees (feeRate * takerFee), adding the takerAssetAmount and dividing by makerAssetAmount + * @param signedOrder An object that conforms to the signedOrder interface + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * @return The rate (takerAsset/makerAsset) of the order adjusted for fees + */ + getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber): BigNumber { + assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + assert.isBigNumber('feeRate', feeRate); + assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); + const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); + const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); + return rate; + }, + /** + * Takes a signed fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates + * the fee adjusted rate (WETH/ZRX) by dividing the takerAssetAmount by the makerAmount minus the takerFee + * @param signedFeeOrder An object that conforms to the signedOrder interface + * @return The rate (WETH/ZRX) of the fee order adjusted for fees + */ + getFeeAdjustedRateOfFeeOrder(signedFeeOrder: SignedOrder): BigNumber { + assert.doesConformToSchema('signedFeeOrder', signedFeeOrder, schemas.signedOrderSchema); + const zrxAmountAfterFees = signedFeeOrder.makerAssetAmount.sub(signedFeeOrder.takerFee); + assert.assert( + zrxAmountAfterFees.greaterThan(constants.ZERO_AMOUNT), + `Expected takerFee: ${JSON.stringify( + signedFeeOrder.takerFee, + )} to be less than makerAssetAmount: ${JSON.stringify(signedFeeOrder.makerAssetAmount)}`, + ); + const rate = signedFeeOrder.takerAssetAmount.div(zrxAmountAfterFees); + return rate; + }, +}; -- cgit v1.2.3 From c0924d8067079f246c5cad32f7acb3d1f6cfd9b9 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 17:49:22 -0400 Subject: Implement sorting utils --- packages/order-utils/src/index.ts | 1 + packages/order-utils/src/sorting_utils.ts | 69 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 packages/order-utils/src/sorting_utils.ts (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index a52f936b6..359954641 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -34,3 +34,4 @@ export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { marketUtils } from './market_utils'; export { rateUtils } from './rate_utils'; +export { sortingUtils } from './sorting_utils'; diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts new file mode 100644 index 000000000..7474a302f --- /dev/null +++ b/packages/order-utils/src/sorting_utils.ts @@ -0,0 +1,69 @@ +import { schemas } from '@0xproject/json-schemas'; +import { SignedOrder } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +import { assert } from './assert'; +import { constants } from './constants'; +import { rateUtils } from './rate_utils'; + +export const sortingUtils = { + /** + * Takes an array of signed orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). + * Adjusts the rate of each order according to the feeRate and takerFee for that order. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * @return The input orders sorted by rate in ascending order + */ + sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('feeRate', feeRate); + assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); + const sortedOrders = sortOrders(signedOrders, rateCalculator); + return sortedOrders; + }, + /** + * Takes an array of signed fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) + * and sorts them by rate in ascending order (best rate first). Adjusts the rate according to the takerFee. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @return The input orders sorted by rate in ascending order + */ + sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { + assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder; + const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); + return sortedOrders; + }, +}; + +type RateCalculator = (signedOrder: SignedOrder) => BigNumber; + +// takes an array of orders, copies them, and sorts the copy based on the rate definition provided by rateCalculator +const sortOrders = (signedOrders: SignedOrder[], rateCalculator: RateCalculator): SignedOrder[] => { + const copiedOrders = _.cloneDeep(signedOrders); + const feeOrderComparator = getOrderComparator(rateCalculator); + copiedOrders.sort(feeOrderComparator); + return copiedOrders; +}; + +type Comparator = (first: T, second: T) => number; + +// takes a function that calculates rate for a signed order and returns a comparator that sorts based on rate +const getOrderComparator = (rateCalculator: RateCalculator): Comparator => ( + firstSignedOrder, + secondSignedOrder, +) => { + const firstOrderRate = rateCalculator(firstSignedOrder); + const secondOrderRate = rateCalculator(secondSignedOrder); + if (firstOrderRate.lt(secondOrderRate)) { + return -1; + } else if (firstOrderRate.gt(secondOrderRate)) { + return 1; + } else { + return 0; + } +}; -- cgit v1.2.3 From fcd57d2743e4b6a1363b8071696147a91d2afb00 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 18:43:31 -0400 Subject: Add tests for sortingUtils --- packages/order-utils/src/sorting_utils.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 7474a302f..2acd8180f 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -20,7 +20,6 @@ export const sortingUtils = { sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.isBigNumber('feeRate', feeRate); - assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); const sortedOrders = sortOrders(signedOrders, rateCalculator); return sortedOrders; -- cgit v1.2.3 From cbe639866ec9d6088cc7aa133033d2735524c5a0 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 18:54:17 -0400 Subject: Make feeRate optional with a default of 0 --- packages/order-utils/src/rate_utils.ts | 9 +++++++-- packages/order-utils/src/sorting_utils.ts | 6 +++++- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index 212431e0c..72d11584a 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,6 +1,7 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; @@ -12,12 +13,16 @@ export const rateUtils = { * @param signedOrder An object that conforms to the signedOrder interface * @param feeRate The market rate of ZRX denominated in takerAssetAmount * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The rate (takerAsset/makerAsset) of the order adjusted for fees */ - getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber): BigNumber { + getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.isBigNumber('feeRate', feeRate); - assert.assert(feeRate.greaterThan(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than 0`); + assert.assert( + feeRate.gte(constants.ZERO_AMOUNT), + `Expected feeRate: ${feeRate} to be greater than or equal to 0`, + ); const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 2acd8180f..8424060fc 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -15,9 +15,13 @@ export const sortingUtils = { * the makerAsset and WETH as the takerAsset. * @param feeRate The market rate of ZRX denominated in takerAssetAmount * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The input orders sorted by rate in ascending order */ - sortOrdersByFeeAdjustedRate(signedOrders: SignedOrder[], feeRate: BigNumber): SignedOrder[] { + sortOrdersByFeeAdjustedRate( + signedOrders: SignedOrder[], + feeRate: BigNumber = constants.ZERO_AMOUNT, + ): SignedOrder[] { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); assert.isBigNumber('feeRate', feeRate); const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); -- cgit v1.2.3 From b86210332f94ad90cdabcf2083ae4a99075daf33 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 19:10:57 -0400 Subject: Fix lint errors --- packages/order-utils/src/rate_utils.ts | 1 - packages/order-utils/src/sorting_utils.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index 72d11584a..e82c03873 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,7 +1,6 @@ import { schemas } from '@0xproject/json-schemas'; import { SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 8424060fc..f019aa4a8 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -37,7 +37,7 @@ export const sortingUtils = { */ sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder; + const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils); const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); return sortedOrders; }, -- cgit v1.2.3 From d8593998414aa3234533b8fa868b05495ac54457 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Aug 2018 17:45:50 -0700 Subject: Change rateUtils to use Order --- packages/order-utils/src/rate_utils.ts | 40 +++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/rate_utils.ts b/packages/order-utils/src/rate_utils.ts index e82c03873..c9ca72c59 100644 --- a/packages/order-utils/src/rate_utils.ts +++ b/packages/order-utils/src/rate_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { assert } from './assert'; @@ -7,42 +7,42 @@ import { constants } from './constants'; export const rateUtils = { /** - * Takes a signed order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset + * Takes an order and calculates the fee adjusted rate (takerAsset/makerAsset) by calculating how much takerAsset * is required to cover the fees (feeRate * takerFee), adding the takerAssetAmount and dividing by makerAssetAmount - * @param signedOrder An object that conforms to the signedOrder interface - * @param feeRate The market rate of ZRX denominated in takerAssetAmount - * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) - * Defaults to 0 + * @param order An object that conforms to the order interface + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The rate (takerAsset/makerAsset) of the order adjusted for fees */ - getFeeAdjustedRateOfOrder(signedOrder: SignedOrder, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + getFeeAdjustedRateOfOrder(order: Order, feeRate: BigNumber = constants.ZERO_AMOUNT): BigNumber { + assert.doesConformToSchema('order', order, schemas.orderSchema); assert.isBigNumber('feeRate', feeRate); assert.assert( feeRate.gte(constants.ZERO_AMOUNT), `Expected feeRate: ${feeRate} to be greater than or equal to 0`, ); - const takerAssetAmountNeededToPayForFees = signedOrder.takerFee.mul(feeRate); - const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(signedOrder.takerAssetAmount); - const rate = totalTakerAssetAmount.div(signedOrder.makerAssetAmount); + const takerAssetAmountNeededToPayForFees = order.takerFee.mul(feeRate); + const totalTakerAssetAmount = takerAssetAmountNeededToPayForFees.plus(order.takerAssetAmount); + const rate = totalTakerAssetAmount.div(order.makerAssetAmount); return rate; }, /** - * Takes a signed fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates + * Takes a fee order (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) and calculates * the fee adjusted rate (WETH/ZRX) by dividing the takerAssetAmount by the makerAmount minus the takerFee - * @param signedFeeOrder An object that conforms to the signedOrder interface + * @param feeOrder An object that conforms to the order interface * @return The rate (WETH/ZRX) of the fee order adjusted for fees */ - getFeeAdjustedRateOfFeeOrder(signedFeeOrder: SignedOrder): BigNumber { - assert.doesConformToSchema('signedFeeOrder', signedFeeOrder, schemas.signedOrderSchema); - const zrxAmountAfterFees = signedFeeOrder.makerAssetAmount.sub(signedFeeOrder.takerFee); + getFeeAdjustedRateOfFeeOrder(feeOrder: Order): BigNumber { + assert.doesConformToSchema('feeOrder', feeOrder, schemas.orderSchema); + const zrxAmountAfterFees = feeOrder.makerAssetAmount.sub(feeOrder.takerFee); assert.assert( zrxAmountAfterFees.greaterThan(constants.ZERO_AMOUNT), - `Expected takerFee: ${JSON.stringify( - signedFeeOrder.takerFee, - )} to be less than makerAssetAmount: ${JSON.stringify(signedFeeOrder.makerAssetAmount)}`, + `Expected takerFee: ${JSON.stringify(feeOrder.takerFee)} to be less than makerAssetAmount: ${JSON.stringify( + feeOrder.makerAssetAmount, + )}`, ); - const rate = signedFeeOrder.takerAssetAmount.div(zrxAmountAfterFees); + const rate = feeOrder.takerAssetAmount.div(zrxAmountAfterFees); return rate; }, }; -- cgit v1.2.3 From 99b744ba52f538752bb0966e6d8b50d9f5a2f032 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Aug 2018 18:31:41 -0700 Subject: Update sortingUtils to support Order and SignedOrder --- packages/order-utils/src/sorting_utils.ts | 70 ++++++++++++------------------- 1 file changed, 26 insertions(+), 44 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index f019aa4a8..8811bcaf8 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -9,64 +9,46 @@ import { rateUtils } from './rate_utils'; export const sortingUtils = { /** - * Takes an array of signed orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). + * Takes an array of orders and sorts them by takerAsset/makerAsset rate in ascending order (best rate first). * Adjusts the rate of each order according to the feeRate and takerFee for that order. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param feeRate The market rate of ZRX denominated in takerAssetAmount - * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) - * Defaults to 0 + * @param orders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeRate The market rate of ZRX denominated in takerAssetAmount + * (ex. feeRate is 0.1 takerAsset/ZRX if it takes 1 unit of takerAsset to buy 10 ZRX) + * Defaults to 0 * @return The input orders sorted by rate in ascending order */ - sortOrdersByFeeAdjustedRate( - signedOrders: SignedOrder[], - feeRate: BigNumber = constants.ZERO_AMOUNT, - ): SignedOrder[] { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + sortOrdersByFeeAdjustedRate(orders: T[], feeRate: BigNumber = constants.ZERO_AMOUNT): T[] { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.isBigNumber('feeRate', feeRate); - const rateCalculator = (signedOrder: SignedOrder) => rateUtils.getFeeAdjustedRateOfOrder(signedOrder, feeRate); - const sortedOrders = sortOrders(signedOrders, rateCalculator); + const rateCalculator = (order: Order) => rateUtils.getFeeAdjustedRateOfOrder(order, feeRate); + const sortedOrders = sortOrders(orders, rateCalculator); return sortedOrders; }, /** - * Takes an array of signed fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) + * Takes an array of fee orders (makerAssetData corresponds to ZRX and takerAssetData corresponds to WETH) * and sorts them by rate in ascending order (best rate first). Adjusts the rate according to the takerFee. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. + * @param feeOrders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. * @return The input orders sorted by rate in ascending order */ - sortFeeOrdersByFeeAdjustedRate(signedFeeOrders: SignedOrder[]): SignedOrder[] { - assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + sortFeeOrdersByFeeAdjustedRate(feeOrders: Order[]): Order[] { + assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils); - const sortedOrders = sortOrders(signedFeeOrders, rateCalculator); + const sortedOrders = sortOrders(feeOrders, rateCalculator); return sortedOrders; }, }; -type RateCalculator = (signedOrder: SignedOrder) => BigNumber; +type RateCalculator = (order: Order) => BigNumber; // takes an array of orders, copies them, and sorts the copy based on the rate definition provided by rateCalculator -const sortOrders = (signedOrders: SignedOrder[], rateCalculator: RateCalculator): SignedOrder[] => { - const copiedOrders = _.cloneDeep(signedOrders); - const feeOrderComparator = getOrderComparator(rateCalculator); - copiedOrders.sort(feeOrderComparator); +function sortOrders(orders: T[], rateCalculator: RateCalculator): T[] { + const copiedOrders = _.cloneDeep(orders); + copiedOrders.sort((firstOrder, secondOrder) => { + const firstOrderRate = rateCalculator(firstOrder); + const secondOrderRate = rateCalculator(secondOrder); + return firstOrderRate.comparedTo(secondOrderRate); + }); return copiedOrders; -}; - -type Comparator = (first: T, second: T) => number; - -// takes a function that calculates rate for a signed order and returns a comparator that sorts based on rate -const getOrderComparator = (rateCalculator: RateCalculator): Comparator => ( - firstSignedOrder, - secondSignedOrder, -) => { - const firstOrderRate = rateCalculator(firstSignedOrder); - const secondOrderRate = rateCalculator(secondSignedOrder); - if (firstOrderRate.lt(secondOrderRate)) { - return -1; - } else if (firstOrderRate.gt(secondOrderRate)) { - return 1; - } else { - return 0; - } -}; +} -- cgit v1.2.3 From c10c4cec1db2c5bc0b71177aea7590ee6fda8735 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Thu, 9 Aug 2018 21:14:46 -0400 Subject: Update marketUtils api --- packages/order-utils/src/market_utils.ts | 93 ++++++++++++++++++-------------- packages/order-utils/src/types.ts | 28 ++++++++++ 2 files changed, 81 insertions(+), 40 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 681059ddf..58e9ab7ba 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -5,37 +5,42 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; +import { FindFeeOrdersThatCoverFeesForTargetOrdersOpts, FindOrdersThatCoverMakerAssetFillAmountOpts } from './types'; export const marketUtils = { /** - * Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount (taking into account on-chain balances, - * allowances, and partial fills) in order to fill the input makerAssetFillAmount plus slippageBufferAmount. Iterates from first order to last. + * Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount + * in order to fill the input makerAssetFillAmount plus slippageBufferAmount. Iterates from first order to last order. * Sort the input by ascending rate in order to get the subset of orders that will cost the least ETH. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify the same makerAsset. - * All orders should specify WETH as the takerAsset. - * @param remainingFillableMakerAssetAmounts An array of BigNumbers corresponding to the signedOrders parameter. - * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups - * for these values. - * @param makerAssetFillAmount The amount of makerAsset desired to be filled. - * @param slippageBufferAmount An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills. + * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify the same makerAsset. + * All orders should specify WETH as the takerAsset. + * @param makerAssetFillAmount The amount of makerAsset desired to be filled. + * @param opts Optional arguments this function accepts. * @return Resulting orders and remaining fill amount that could not be covered by the input. */ findOrdersThatCoverMakerAssetFillAmount( signedOrders: SignedOrder[], - remainingFillableMakerAssetAmounts: BigNumber[], makerAssetFillAmount: BigNumber, - slippageBufferAmount: BigNumber = constants.ZERO_AMOUNT, + opts?: FindOrdersThatCoverMakerAssetFillAmountOpts, ): { resultOrders: SignedOrder[]; remainingFillAmount: BigNumber } { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount); + // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from signedOrders + const remainingFillableMakerAssetAmounts = _.get( + opts, + 'remainingFillableMakerAssetAmounts', + _.map(signedOrders, order => order.makerAssetAmount), + ) as BigNumber[]; _.forEach(remainingFillableMakerAssetAmounts, (amount, index) => assert.isValidBaseUnitAmount(`remainingFillableMakerAssetAmount[${index}]`, amount), ); - assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount); - assert.isValidBaseUnitAmount('slippageBufferAmount', slippageBufferAmount); assert.assert( signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedOrders.length to equal remainingFillableMakerAssetAmounts.length', + 'Expected signedOrders.length to equal opts.remainingFillableMakerAssetAmounts.length', ); + // try to get slippageBufferAmount from opts, if it's not there, default to 0 + const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber; + assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount); // calculate total amount of makerAsset needed to be filled const totalFillAmount = makerAssetFillAmount.plus(slippageBufferAmount); // iterate through the signedOrders input from left to right until we have enough makerAsset to fill totalFillAmount @@ -64,47 +69,53 @@ export const marketUtils = { return result; }, /** - * Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX (taking into account - * on-chain balances, allowances, and partial fills) in order to fill the takerFees required by signedOrders plus a - * slippageBufferAmount. Iterates from first feeOrder to last. Sort the feeOrders by ascending rate in order to get the subset of + * Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX + * in order to fill the takerFees required by signedOrders plus a slippageBufferAmount. + * Iterates from first feeOrder to last. Sort the feeOrders by ascending rate in order to get the subset of * feeOrders that will cost the least ETH. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param remainingFillableMakerAssetAmounts An array of BigNumbers corresponding to the signedOrders parameter. - * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups - * for these values. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param remainingFillableFeeAmounts An array of BigNumbers corresponding to the signedFeeOrders parameter. - * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups - * for these values. - * @param slippageBufferAmount An additional amount of fee to be covered by the result in case of trade collisions or partial fills. + * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param opts Optional arguments this function accepts. * @return Resulting orders and remaining fee amount that could not be covered by the input. */ findFeeOrdersThatCoverFeesForTargetOrders( signedOrders: SignedOrder[], - remainingFillableMakerAssetAmounts: BigNumber[], signedFeeOrders: SignedOrder[], - remainingFillableFeeAmounts: BigNumber[], - slippageBufferAmount: BigNumber = constants.ZERO_AMOUNT, + opts?: FindFeeOrdersThatCoverFeesForTargetOrdersOpts, ): { resultOrders: SignedOrder[]; remainingFeeAmount: BigNumber } { assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); + // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from signedOrders + const remainingFillableMakerAssetAmounts = _.get( + opts, + 'remainingFillableMakerAssetAmounts', + _.map(signedOrders, order => order.makerAssetAmount), + ) as BigNumber[]; _.forEach(remainingFillableMakerAssetAmounts, (amount, index) => assert.isValidBaseUnitAmount(`remainingFillableMakerAssetAmount[${index}]`, amount), ); - assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - _.forEach(remainingFillableFeeAmounts, (amount, index) => - assert.isValidBaseUnitAmount(`remainingFillableFeeAmounts[${index}]`, amount), - ); - assert.isValidBaseUnitAmount('slippageBufferAmount', slippageBufferAmount); assert.assert( signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedOrders.length to equal remainingFillableMakerAssetAmounts.length', + 'Expected signedOrders.length to equal opts.remainingFillableMakerAssetAmounts.length', + ); + // try to get remainingFillableFeeAmounts from opts, if it's not there, use makerAssetAmount values from signedFeeOrders + const remainingFillableFeeAmounts = _.get( + opts, + 'remainingFillableFeeAmounts', + _.map(signedFeeOrders, order => order.makerAssetAmount), + ) as BigNumber[]; + _.forEach(remainingFillableFeeAmounts, (amount, index) => + assert.isValidBaseUnitAmount(`remainingFillableFeeAmounts[${index}]`, amount), ); assert.assert( signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedFeeOrders.length to equal remainingFillableFeeAmounts.length', + 'Expected signedFeeOrders.length to equal opts.remainingFillableFeeAmounts.length', ); + // try to get slippageBufferAmount from opts, if it's not there, default to 0 + const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber; + assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount); // calculate total amount of ZRX needed to fill signedOrders const totalFeeAmount = _.reduce( signedOrders, @@ -119,9 +130,11 @@ export const marketUtils = { ); const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( signedFeeOrders, - remainingFillableFeeAmounts, totalFeeAmount, - slippageBufferAmount, + { + remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, + slippageBufferAmount, + }, ); return { resultOrders, diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 1fbd8cf7b..8a9a4cafe 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -41,3 +41,31 @@ export interface CreateOrderOpts { salt?: BigNumber; expirationTimeSeconds?: BigNumber; } + +/** + * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `signedOrders` parameter. + * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values. + * Defaults to `makerAssetAmount` values from the signedOrders param. + * slippageBufferAmount: An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills. + * Defaults to 0 + */ +export interface FindOrdersThatCoverMakerAssetFillAmountOpts { + remainingFillableMakerAssetAmounts?: BigNumber[]; + slippageBufferAmount?: BigNumber; +} + +/** + * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `signedOrders` parameter. + * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values. + * Defaults to `makerAssetAmount` values from the signedOrders param. + * remainingFillableFeeAmounts: An array of BigNumbers corresponding to the signedFeeOrders parameter. + * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups for these values. + * Defaults to `makerAssetAmount` values from the signedFeeOrders param. + * slippageBufferAmount: An additional amount of fee to be covered by the result in case of trade collisions or partial fills. + * Defaults to 0 + */ +export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts { + remainingFillableMakerAssetAmounts?: BigNumber[]; + remainingFillableFeeAmounts?: BigNumber[]; + slippageBufferAmount?: BigNumber; +} -- cgit v1.2.3 From 6a2634d362e50e5a611f388c0785df3209cee308 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 13 Aug 2018 21:29:35 -0700 Subject: Make marketUtils interface compatible with Order and SignedOrder --- packages/order-utils/src/market_utils.ts | 72 ++++++++++++++++---------------- packages/order-utils/src/types.ts | 12 +++--- 2 files changed, 42 insertions(+), 42 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 58e9ab7ba..a0a827546 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { SignedOrder } from '@0xproject/types'; +import { Order } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as _ from 'lodash'; @@ -12,40 +12,40 @@ export const marketUtils = { * Takes an array of orders and returns a subset of those orders that has enough makerAssetAmount * in order to fill the input makerAssetFillAmount plus slippageBufferAmount. Iterates from first order to last order. * Sort the input by ascending rate in order to get the subset of orders that will cost the least ETH. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify the same makerAsset. + * @param orders An array of objects that extend the Order interface. All orders should specify the same makerAsset. * All orders should specify WETH as the takerAsset. * @param makerAssetFillAmount The amount of makerAsset desired to be filled. * @param opts Optional arguments this function accepts. * @return Resulting orders and remaining fill amount that could not be covered by the input. */ - findOrdersThatCoverMakerAssetFillAmount( - signedOrders: SignedOrder[], + findOrdersThatCoverMakerAssetFillAmount( + orders: T[], makerAssetFillAmount: BigNumber, opts?: FindOrdersThatCoverMakerAssetFillAmountOpts, - ): { resultOrders: SignedOrder[]; remainingFillAmount: BigNumber } { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + ): { resultOrders: T[]; remainingFillAmount: BigNumber } { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount); - // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from signedOrders + // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders const remainingFillableMakerAssetAmounts = _.get( opts, 'remainingFillableMakerAssetAmounts', - _.map(signedOrders, order => order.makerAssetAmount), + _.map(orders, order => order.makerAssetAmount), ) as BigNumber[]; _.forEach(remainingFillableMakerAssetAmounts, (amount, index) => assert.isValidBaseUnitAmount(`remainingFillableMakerAssetAmount[${index}]`, amount), ); assert.assert( - signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedOrders.length to equal opts.remainingFillableMakerAssetAmounts.length', + orders.length === remainingFillableMakerAssetAmounts.length, + 'Expected orders.length to equal opts.remainingFillableMakerAssetAmounts.length', ); // try to get slippageBufferAmount from opts, if it's not there, default to 0 const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber; assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount); // calculate total amount of makerAsset needed to be filled const totalFillAmount = makerAssetFillAmount.plus(slippageBufferAmount); - // iterate through the signedOrders input from left to right until we have enough makerAsset to fill totalFillAmount + // iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount const result = _.reduce( - signedOrders, + orders, ({ resultOrders, remainingFillAmount }, order, index) => { if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) { return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT }; @@ -64,61 +64,61 @@ export const marketUtils = { }; } }, - { resultOrders: [] as SignedOrder[], remainingFillAmount: totalFillAmount }, + { resultOrders: [] as T[], remainingFillAmount: totalFillAmount }, ); return result; }, /** * Takes an array of orders and an array of feeOrders. Returns a subset of the feeOrders that has enough ZRX - * in order to fill the takerFees required by signedOrders plus a slippageBufferAmount. + * in order to fill the takerFees required by orders plus a slippageBufferAmount. * Iterates from first feeOrder to last. Sort the feeOrders by ascending rate in order to get the subset of * feeOrders that will cost the least ETH. - * @param signedOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders should specify ZRX as - * the makerAsset and WETH as the takerAsset. - * @param opts Optional arguments this function accepts. + * @param orders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param feeOrders An array of objects that extend the Order interface. All orders should specify ZRX as + * the makerAsset and WETH as the takerAsset. + * @param opts Optional arguments this function accepts. * @return Resulting orders and remaining fee amount that could not be covered by the input. */ - findFeeOrdersThatCoverFeesForTargetOrders( - signedOrders: SignedOrder[], - signedFeeOrders: SignedOrder[], + findFeeOrdersThatCoverFeesForTargetOrders( + orders: T[], + feeOrders: T[], opts?: FindFeeOrdersThatCoverFeesForTargetOrdersOpts, - ): { resultOrders: SignedOrder[]; remainingFeeAmount: BigNumber } { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); - assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema); - // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from signedOrders + ): { resultOrders: T[]; remainingFeeAmount: BigNumber } { + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); + // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders const remainingFillableMakerAssetAmounts = _.get( opts, 'remainingFillableMakerAssetAmounts', - _.map(signedOrders, order => order.makerAssetAmount), + _.map(orders, order => order.makerAssetAmount), ) as BigNumber[]; _.forEach(remainingFillableMakerAssetAmounts, (amount, index) => assert.isValidBaseUnitAmount(`remainingFillableMakerAssetAmount[${index}]`, amount), ); assert.assert( - signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedOrders.length to equal opts.remainingFillableMakerAssetAmounts.length', + orders.length === remainingFillableMakerAssetAmounts.length, + 'Expected orders.length to equal opts.remainingFillableMakerAssetAmounts.length', ); - // try to get remainingFillableFeeAmounts from opts, if it's not there, use makerAssetAmount values from signedFeeOrders + // try to get remainingFillableFeeAmounts from opts, if it's not there, use makerAssetAmount values from feeOrders const remainingFillableFeeAmounts = _.get( opts, 'remainingFillableFeeAmounts', - _.map(signedFeeOrders, order => order.makerAssetAmount), + _.map(feeOrders, order => order.makerAssetAmount), ) as BigNumber[]; _.forEach(remainingFillableFeeAmounts, (amount, index) => assert.isValidBaseUnitAmount(`remainingFillableFeeAmounts[${index}]`, amount), ); assert.assert( - signedOrders.length === remainingFillableMakerAssetAmounts.length, - 'Expected signedFeeOrders.length to equal opts.remainingFillableFeeAmounts.length', + feeOrders.length === remainingFillableFeeAmounts.length, + 'Expected feeOrders.length to equal opts.remainingFillableFeeAmounts.length', ); // try to get slippageBufferAmount from opts, if it's not there, default to 0 const slippageBufferAmount = _.get(opts, 'slippageBufferAmount', constants.ZERO_AMOUNT) as BigNumber; assert.isValidBaseUnitAmount('opts.slippageBufferAmount', slippageBufferAmount); - // calculate total amount of ZRX needed to fill signedOrders + // calculate total amount of ZRX needed to fill orders const totalFeeAmount = _.reduce( - signedOrders, + orders, (accFees, order, index) => { const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index]; const feeToFillMakerAssetAmountAvailable = makerAssetAmountAvailable @@ -129,7 +129,7 @@ export const marketUtils = { constants.ZERO_AMOUNT, ); const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - signedFeeOrders, + feeOrders, totalFeeAmount, { remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 8a9a4cafe..2e9c79d80 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -43,9 +43,9 @@ export interface CreateOrderOpts { } /** - * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `signedOrders` parameter. + * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter. * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values. - * Defaults to `makerAssetAmount` values from the signedOrders param. + * Defaults to `makerAssetAmount` values from the orders param. * slippageBufferAmount: An additional amount of makerAsset to be covered by the result in case of trade collisions or partial fills. * Defaults to 0 */ @@ -55,12 +55,12 @@ export interface FindOrdersThatCoverMakerAssetFillAmountOpts { } /** - * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `signedOrders` parameter. + * remainingFillableMakerAssetAmount: An array of BigNumbers corresponding to the `orders` parameter. * You can use `OrderStateUtils` `@0xproject/order-utils` to perform blockchain lookups for these values. - * Defaults to `makerAssetAmount` values from the signedOrders param. - * remainingFillableFeeAmounts: An array of BigNumbers corresponding to the signedFeeOrders parameter. + * Defaults to `makerAssetAmount` values from the orders param. + * remainingFillableFeeAmounts: An array of BigNumbers corresponding to the feeOrders parameter. * You can use OrderStateUtils @0xproject/order-utils to perform blockchain lookups for these values. - * Defaults to `makerAssetAmount` values from the signedFeeOrders param. + * Defaults to `makerAssetAmount` values from the feeOrders param. * slippageBufferAmount: An additional amount of fee to be covered by the result in case of trade collisions or partial fills. * Defaults to 0 */ -- cgit v1.2.3 From 1d9408a8e04aaea35bcf517fd3332b9bcc62bba0 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 14 Aug 2018 14:39:59 -0700 Subject: Fix additional merge conflicts --- packages/order-utils/src/signature_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index d466bb00d..40bbcef98 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -49,7 +49,7 @@ export const signatureUtils = { case SignatureType.EthSign: { const ecSignature = signatureUtils.parseECSignature(signature); - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.EthSign); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Default); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } -- cgit v1.2.3 From 622509c508272790e3e69c09cf1a1f9696815147 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 13 Aug 2018 12:23:29 +1000 Subject: [Order-utils] Order is valid when maker amount is very small Previously our min fillable calculation would throw a rounding error when encountering a valid order (with a small maker amount). This was inconsistent with the on-chain logic which allowed this order to be filled. --- packages/order-utils/src/order_state_utils.ts | 36 +++++++++++++-------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 189bf4180..7974d5d0b 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -11,6 +11,7 @@ import { BigNumber } from '@0xproject/utils'; import { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; import { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; import { orderHashUtils } from './order_hash'; +import { OrderValidationUtils } from './order_validation_utils'; import { RemainingFillableCalculator } from './remaining_fillable_calculator'; import { utils } from './utils'; @@ -22,10 +23,9 @@ interface SidedOrderRelevantState { traderFeeProxyAllowance: BigNumber; filledTakerAssetAmount: BigNumber; remainingFillableAssetAmount: BigNumber; + isOrderCancelled: boolean; } -const ACCEPTABLE_RELATIVE_ROUNDING_ERROR = 0.0001; - export class OrderStateUtils { private readonly _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher; private readonly _orderFilledCancelledFetcher: AbstractOrderFilledCancelledFetcher; @@ -34,6 +34,9 @@ export class OrderStateUtils { sidedOrderRelevantState: SidedOrderRelevantState, ): void { const isMakerSide = sidedOrderRelevantState.isMakerSide; + if (sidedOrderRelevantState.isOrderCancelled) { + throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); + } const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus( sidedOrderRelevantState.filledTakerAssetAmount, ); @@ -71,23 +74,15 @@ export class OrderStateUtils { ); } } - - let minFillableTakerAssetAmountWithinNoRoundingErrorRange; - if (isMakerSide) { - minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.takerAssetAmount - .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) - .dividedBy(signedOrder.makerAssetAmount); - } else { - minFillableTakerAssetAmountWithinNoRoundingErrorRange = signedOrder.makerAssetAmount - .dividedBy(ACCEPTABLE_RELATIVE_ROUNDING_ERROR) - .dividedBy(signedOrder.takerAssetAmount); - } - - if ( - sidedOrderRelevantState.remainingFillableAssetAmount.lessThan( - minFillableTakerAssetAmountWithinNoRoundingErrorRange, - ) - ) { + const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus( + sidedOrderRelevantState.filledTakerAssetAmount, + ); + const isRoundingError = OrderValidationUtils.isRoundingError( + remainingTakerAssetAmount, + signedOrder.takerAssetAmount, + signedOrder.makerAssetAmount, + ); + if (isRoundingError) { throw new Error(ExchangeContractErrs.OrderFillRoundingError); } } @@ -101,6 +96,7 @@ export class OrderStateUtils { public async getOpenOrderStateAsync(signedOrder: SignedOrder): Promise { const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder); const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash); const sidedOrderRelevantState = { isMakerSide: true, traderBalance: orderRelevantState.makerBalance, @@ -109,6 +105,7 @@ export class OrderStateUtils { traderFeeProxyAllowance: orderRelevantState.makerFeeProxyAllowance, filledTakerAssetAmount: orderRelevantState.filledTakerAssetAmount, remainingFillableAssetAmount: orderRelevantState.remainingFillableMakerAssetAmount, + isOrderCancelled, }; try { OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState); @@ -278,6 +275,7 @@ export class OrderStateUtils { traderFeeProxyAllowance, filledTakerAssetAmount, remainingFillableAssetAmount, + isOrderCancelled, }; return sidedOrderRelevantState; } -- cgit v1.2.3 From 88c99396a2d1b880bffb21ef19507d02b474ba9b Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 15 Aug 2018 13:03:31 +1000 Subject: Rename OrderAlreadyCancelledOrFilled -> OrderCancelled. Remove try catch of throwing errors in favour of returning the Errors in a OrderValidationResult --- packages/order-utils/src/order_state_utils.ts | 61 +++++++++++++++------------ 1 file changed, 33 insertions(+), 28 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 7974d5d0b..18fc18bf6 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -25,6 +25,14 @@ interface SidedOrderRelevantState { remainingFillableAssetAmount: BigNumber; isOrderCancelled: boolean; } +interface OrderValidResult { + isValid: true; +} +interface OrderInvalidResult { + isValid: false; + error: ExchangeContractErrs; +} +type OrderValidationResult = OrderValidResult | OrderInvalidResult; export class OrderStateUtils { private readonly _balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher; @@ -32,46 +40,42 @@ export class OrderStateUtils { private static _validateIfOrderIsValid( signedOrder: SignedOrder, sidedOrderRelevantState: SidedOrderRelevantState, - ): void { + ): OrderValidationResult { const isMakerSide = sidedOrderRelevantState.isMakerSide; if (sidedOrderRelevantState.isOrderCancelled) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); + return { isValid: false, error: ExchangeContractErrs.OrderCancelled }; } const availableTakerAssetAmount = signedOrder.takerAssetAmount.minus( sidedOrderRelevantState.filledTakerAssetAmount, ); if (availableTakerAssetAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); + return { isValid: false, error: ExchangeContractErrs.OrderRemainingFillAmountZero }; } if (sidedOrderRelevantState.traderBalance.eq(0)) { - throw new Error( - isMakerSide - ? ExchangeContractErrs.InsufficientMakerBalance - : ExchangeContractErrs.InsufficientTakerBalance, - ); + const error = isMakerSide + ? ExchangeContractErrs.InsufficientMakerBalance + : ExchangeContractErrs.InsufficientTakerBalance; + return { isValid: false, error }; } if (sidedOrderRelevantState.traderProxyAllowance.eq(0)) { - throw new Error( - isMakerSide - ? ExchangeContractErrs.InsufficientMakerAllowance - : ExchangeContractErrs.InsufficientTakerAllowance, - ); + const error = isMakerSide + ? ExchangeContractErrs.InsufficientMakerAllowance + : ExchangeContractErrs.InsufficientTakerAllowance; + return { isValid: false, error }; } if (!signedOrder.makerFee.eq(0)) { if (sidedOrderRelevantState.traderFeeBalance.eq(0)) { - throw new Error( - isMakerSide - ? ExchangeContractErrs.InsufficientMakerFeeBalance - : ExchangeContractErrs.InsufficientTakerFeeBalance, - ); + const error = isMakerSide + ? ExchangeContractErrs.InsufficientMakerFeeBalance + : ExchangeContractErrs.InsufficientTakerFeeBalance; + return { isValid: false, error }; } if (sidedOrderRelevantState.traderFeeProxyAllowance.eq(0)) { - throw new Error( - isMakerSide - ? ExchangeContractErrs.InsufficientMakerFeeAllowance - : ExchangeContractErrs.InsufficientTakerFeeAllowance, - ); + const error = isMakerSide + ? ExchangeContractErrs.InsufficientMakerFeeAllowance + : ExchangeContractErrs.InsufficientTakerFeeAllowance; + return { isValid: false, error }; } } const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus( @@ -83,8 +87,9 @@ export class OrderStateUtils { signedOrder.makerAssetAmount, ); if (isRoundingError) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); + return { isValid: false, error: ExchangeContractErrs.OrderFillRoundingError }; } + return { isValid: true }; } constructor( balanceAndProxyAllowanceFetcher: AbstractBalanceAndProxyAllowanceFetcher, @@ -107,19 +112,19 @@ export class OrderStateUtils { remainingFillableAssetAmount: orderRelevantState.remainingFillableMakerAssetAmount, isOrderCancelled, }; - try { - OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState); + const orderValidationResult = OrderStateUtils._validateIfOrderIsValid(signedOrder, sidedOrderRelevantState); + if (orderValidationResult.isValid) { const orderState: OrderStateValid = { isValid: true, orderHash, orderRelevantState, }; return orderState; - } catch (err) { + } else { const orderState: OrderStateInvalid = { isValid: false, orderHash, - error: err.message, + error: orderValidationResult.error, }; return orderState; } -- cgit v1.2.3 From 7553411fb2c0483e08046a85cc3b694e2063cbfd Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 21 Aug 2018 16:37:03 +0100 Subject: Fix up order-utils --- packages/order-utils/src/index.ts | 14 ++++++++++++-- packages/order-utils/src/market_utils.ts | 11 ++++++++--- packages/order-utils/src/types.ts | 10 ++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 1f393b0c4..354299304 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -9,14 +9,16 @@ export { sortingUtils } from './sorting_utils'; export { OrderStateUtils } from './order_state_utils'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; +export { AbstractBalanceAndProxyAllowanceLazyStore } from './abstract/abstract_balance_and_proxy_allowance_lazy_store'; export { AbstractOrderFilledCancelledFetcher } from './abstract/abstract_order_filled_cancelled_fetcher'; +export { AbstractOrderFilledCancelledLazyStore } from './abstract/abstract_order_filled_cancelled_lazy_store'; export { OrderValidationUtils } from './order_validation_utils'; export { ExchangeTransferSimulator } from './exchange_transfer_simulator'; export { BalanceAndProxyAllowanceLazyStore } from './store/balance_and_proxy_allowance_lazy_store'; export { OrderFilledCancelledLazyStore } from './store/order_filled_cancelled_lazy_store'; -export { Provider } from 'ethereum-types'; +export { Provider, JSONRPCRequestPayload, JSONRPCErrorCallback, JSONRPCResponsePayload } from 'ethereum-types'; export { SignedOrder, Order, @@ -26,13 +28,21 @@ export { ERC20AssetData, ERC721AssetData, AssetProxyId, + SignerType, + SignatureType, + OrderStateValid, + OrderStateInvalid, + ExchangeContractErrs, } from '@0xproject/types'; export { - CreateOrderOpts, OrderError, EIP712Parameter, EIP712Schema, EIP712Types, TradeSide, TransferType, + FindFeeOrdersThatCoverFeesForTargetOrdersOpts, + FindOrdersThatCoverMakerAssetFillAmountOpts, + FeeOrdersAndRemainingFeeAmount, + OrdersAndRemainingFillAmount, } from './types'; diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index a0a827546..b3f302dd2 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -5,7 +5,12 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; -import { FindFeeOrdersThatCoverFeesForTargetOrdersOpts, FindOrdersThatCoverMakerAssetFillAmountOpts } from './types'; +import { + FindFeeOrdersThatCoverFeesForTargetOrdersOpts, + FindOrdersThatCoverMakerAssetFillAmountOpts, + FeeOrdersAndRemainingFeeAmount, + OrdersAndRemainingFillAmount, +} from './types'; export const marketUtils = { /** @@ -22,7 +27,7 @@ export const marketUtils = { orders: T[], makerAssetFillAmount: BigNumber, opts?: FindOrdersThatCoverMakerAssetFillAmountOpts, - ): { resultOrders: T[]; remainingFillAmount: BigNumber } { + ): OrdersAndRemainingFillAmount { assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.isValidBaseUnitAmount('makerAssetFillAmount', makerAssetFillAmount); // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders @@ -84,7 +89,7 @@ export const marketUtils = { orders: T[], feeOrders: T[], opts?: FindFeeOrdersThatCoverFeesForTargetOrdersOpts, - ): { resultOrders: T[]; remainingFeeAmount: BigNumber } { + ): FeeOrdersAndRemainingFeeAmount { assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 2e9c79d80..4088805dc 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -69,3 +69,13 @@ export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts { remainingFillableFeeAmounts?: BigNumber[]; slippageBufferAmount?: BigNumber; } + +export interface FeeOrdersAndRemainingFeeAmount { + resultOrders: T[]; + remainingFeeAmount: BigNumber; +} + +export interface OrdersAndRemainingFillAmount { + resultOrders: T[]; + remainingFillAmount: BigNumber; +} -- cgit v1.2.3 From 301cb296ec77e8af5c1722679e04cb983c848153 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 21 Aug 2018 23:58:06 +0100 Subject: Move types from sol-compiler to types so they can be used in other places without requiring sol-compiler as a dep --- packages/order-utils/src/artifacts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/artifacts.ts b/packages/order-utils/src/artifacts.ts index 3d2d1e953..735cc2403 100644 --- a/packages/order-utils/src/artifacts.ts +++ b/packages/order-utils/src/artifacts.ts @@ -1,4 +1,4 @@ -import { ContractArtifact } from '@0xproject/sol-compiler'; +import { ContractArtifact } from 'ethereum-types'; import * as DummyERC20Token from './artifacts/DummyERC20Token.json'; import * as ERC20Proxy from './artifacts/ERC20Proxy.json'; -- cgit v1.2.3 From b7c119b2aaaa2f3579ca4aeef198eca7f38f1216 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Wed, 22 Aug 2018 18:52:17 +0100 Subject: Fix many linter errors that showed up upon upgrading tsutil --- .../src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts | 2 +- .../order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts | 2 +- packages/order-utils/src/market_utils.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts index 7cb859ca7..c7f06abad 100644 --- a/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts +++ b/packages/order-utils/src/abstract/abstract_balance_and_proxy_allowance_fetcher.ts @@ -1,6 +1,6 @@ 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 diff --git a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts index d2b01c359..fbc1c4718 100644 --- a/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts +++ b/packages/order-utils/src/abstract/abstract_order_filled_cancelled_fetcher.ts @@ -1,6 +1,6 @@ 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. diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index b3f302dd2..441c50e5c 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -6,9 +6,9 @@ import * as _ from 'lodash'; import { assert } from './assert'; import { constants } from './constants'; import { + FeeOrdersAndRemainingFeeAmount, FindFeeOrdersThatCoverFeesForTargetOrdersOpts, FindOrdersThatCoverMakerAssetFillAmountOpts, - FeeOrdersAndRemainingFeeAmount, OrdersAndRemainingFillAmount, } from './types'; -- cgit v1.2.3 From 3c973ba9f64197fcc1a66f319e3d1aa33d96b6d7 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 17 Aug 2018 00:15:52 -0700 Subject: Remove marketSell and add to marketBuy implementation --- packages/order-utils/src/market_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index a0a827546..7eae4c8fc 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -84,7 +84,7 @@ export const marketUtils = { orders: T[], feeOrders: T[], opts?: FindFeeOrdersThatCoverFeesForTargetOrdersOpts, - ): { resultOrders: T[]; remainingFeeAmount: BigNumber } { + ): { resultFeeOrders: T[]; remainingFeeAmount: BigNumber } { assert.doesConformToSchema('orders', orders, schemas.ordersSchema); assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); // try to get remainingFillableMakerAssetAmounts from opts, if it's not there, use makerAssetAmount values from orders @@ -137,7 +137,7 @@ export const marketUtils = { }, ); return { - resultOrders, + resultFeeOrders: resultOrders, remainingFeeAmount: remainingFillAmount, }; // TODO: add more orders here to cover rounding -- cgit v1.2.3 From e8a1950a7489e83f7f1a1127ee325f0c5c36483c Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Fri, 17 Aug 2018 13:00:54 -0700 Subject: Add ForwarderHelperImplConfig --- packages/order-utils/src/sorting_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/sorting_utils.ts b/packages/order-utils/src/sorting_utils.ts index 8811bcaf8..cd5163cf6 100644 --- a/packages/order-utils/src/sorting_utils.ts +++ b/packages/order-utils/src/sorting_utils.ts @@ -32,7 +32,7 @@ export const sortingUtils = { * the makerAsset and WETH as the takerAsset. * @return The input orders sorted by rate in ascending order */ - sortFeeOrdersByFeeAdjustedRate(feeOrders: Order[]): Order[] { + sortFeeOrdersByFeeAdjustedRate(feeOrders: T[]): T[] { assert.doesConformToSchema('feeOrders', feeOrders, schemas.ordersSchema); const rateCalculator = rateUtils.getFeeAdjustedRateOfFeeOrder.bind(rateUtils); const sortedOrders = sortOrders(feeOrders, rateCalculator); -- cgit v1.2.3 From 67d33ec10c3d2467d2d073d22bfe2957353a0cc8 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Tue, 21 Aug 2018 17:07:14 -0700 Subject: Fix rounding bug in marketUtils --- packages/order-utils/src/market_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 7eae4c8fc..b31a2b135 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -123,7 +123,7 @@ export const marketUtils = { const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index]; const feeToFillMakerAssetAmountAvailable = makerAssetAmountAvailable .mul(order.takerFee) - .div(order.makerAssetAmount); + .dividedToIntegerBy(order.makerAssetAmount); return accFees.plus(feeToFillMakerAssetAmountAvailable); }, constants.ZERO_AMOUNT, -- cgit v1.2.3 From 4f27991959ba6e4e15839e6195a40b0fd8a2122c Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 11:22:17 -0700 Subject: Remove SigntureType.Caller from signingUtils --- packages/order-utils/src/signature_utils.ts | 5 ----- 1 file changed, 5 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 40bbcef98..f8ed9cf9c 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -53,11 +53,6 @@ export const signatureUtils = { return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } - case SignatureType.Caller: - // HACK: We currently do not "validate" the caller signature type. - // It can only be validated during Exchange contract execution. - throw new Error('Caller signature type cannot be validated off-chain'); - case SignatureType.Wallet: { const isValid = await signatureUtils.isValidWalletSignatureAsync( provider, -- cgit v1.2.3 From 1932aff35c6b07093534cde66dec63c5f919fcf0 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 13:11:17 -0700 Subject: Remove Trezor SignatureType --- packages/order-utils/src/signature_utils.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index f8ed9cf9c..23577efdc 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -77,12 +77,6 @@ export const signatureUtils = { return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress); } - case SignatureType.Trezor: { - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Trezor); - const ecSignature = signatureUtils.parseECSignature(signature); - return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); - } - default: throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`); } @@ -288,10 +282,6 @@ export const signatureUtils = { signatureType = SignatureType.EthSign; break; } - case SignerType.Trezor: { - signatureType = SignatureType.Trezor; - break; - } default: throw new Error(`Unrecognized SignerType: ${signerType}`); } @@ -345,7 +335,7 @@ export const signatureUtils = { */ parseECSignature(signature: string): ECSignature { assert.isHexString('signature', signature); - const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor]; + const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712]; assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes); // tslint:disable-next-line:custom-no-magic-numbers -- cgit v1.2.3 From 241534a63dab54172326ec71d6b5e78733bb24c6 Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 02:42:20 -0700 Subject: Fixed trezor personal message in client+contracts; added a test using message signed by Trezor One (firmware v1.6.2) --- packages/order-utils/src/signature_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 23577efdc..c18e895f5 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -347,7 +347,7 @@ export const signatureUtils = { }; function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength)); + const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + message.byteLength); return ethUtil.sha3(Buffer.concat([prefix, message])); } -- cgit v1.2.3 From d039a1adda6fd8ce87a254310e0f56568b10136b Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Fri, 24 Aug 2018 11:57:12 -0700 Subject: Fixed linter in signatureUtils --- packages/order-utils/src/signature_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index c18e895f5..e5478d4d6 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -347,7 +347,7 @@ export const signatureUtils = { }; function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + message.byteLength); + const prefix = ethUtil.toBuffer(`\x19Ethereum Signed Message:\n${message.byteLength}`); return ethUtil.sha3(Buffer.concat([prefix, message])); } -- cgit v1.2.3 From 0629a7d1434e9a17ba98be4de66fd4a7fa7ff16f Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Fri, 24 Aug 2018 14:13:54 -0700 Subject: Remove remaining Trezor references --- packages/order-utils/src/signature_utils.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index e5478d4d6..c0c9e71a7 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -291,7 +291,7 @@ export const signatureUtils = { /** * Combines the signature proof and the Signature Type. * @param signature The hex encoded signature proof - * @param signatureType The signature type, i.e EthSign, Trezor, Wallet etc. + * @param signatureType The signature type, i.e EthSign, Wallet etc. * @return Hex encoded string of signature proof with Signature Type */ convertToSignatureWithType(signature: string, signatureType: SignatureType): string { @@ -318,12 +318,6 @@ export const signatureUtils = { const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); return prefixedMsgHex; } - case SignerType.Trezor: { - const msgBuff = ethUtil.toBuffer(message); - const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); - const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); - return prefixedMsgHex; - } default: throw new Error(`Unrecognized SignerType: ${signerType}`); } @@ -346,11 +340,6 @@ export const signatureUtils = { }, }; -function hashTrezorPersonalMessage(message: Buffer): Buffer { - const prefix = ethUtil.toBuffer(`\x19Ethereum Signed Message:\n${message.byteLength}`); - return ethUtil.sha3(Buffer.concat([prefix, message])); -} - function parseValidatorSignature(signature: string): ValidatorSignature { assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]); // tslint:disable:custom-no-magic-numbers -- cgit v1.2.3 From 80291caf7cef16f0b300484755960d92d6750a6a Mon Sep 17 00:00:00 2001 From: Remco Bloemen Date: Fri, 24 Aug 2018 16:16:44 -0700 Subject: Append -Floor to getPartialAmount and isRoundingError --- packages/order-utils/src/order_state_utils.ts | 4 ++-- packages/order-utils/src/order_validation_utils.ts | 10 +++++----- packages/order-utils/src/utils.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index a0e24acf0..8398776aa 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -81,7 +81,7 @@ export class OrderStateUtils { const remainingTakerAssetAmount = signedOrder.takerAssetAmount.minus( sidedOrderRelevantState.filledTakerAssetAmount, ); - const isRoundingError = OrderValidationUtils.isRoundingError( + const isRoundingError = OrderValidationUtils.isRoundingErrorFloor( remainingTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -191,7 +191,7 @@ export class OrderStateUtils { ); const remainingFillableTakerAssetAmountGivenMakersStatus = signedOrder.makerAssetAmount.eq(0) ? new BigNumber(0) - : utils.getPartialAmount( + : utils.getPartialAmountFloor( orderRelevantMakerState.remainingFillableAssetAmount, signedOrder.makerAssetAmount, signedOrder.takerAssetAmount, diff --git a/packages/order-utils/src/order_validation_utils.ts b/packages/order-utils/src/order_validation_utils.ts index 972e6f6d6..8227fb07c 100644 --- a/packages/order-utils/src/order_validation_utils.ts +++ b/packages/order-utils/src/order_validation_utils.ts @@ -24,7 +24,7 @@ export class OrderValidationUtils { * @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 { + public static isRoundingErrorFloor(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 if (denominator.eq(0)) { @@ -58,7 +58,7 @@ export class OrderValidationUtils { zrxAssetData: string, ): Promise { try { - const fillMakerTokenAmount = utils.getPartialAmount( + const fillMakerTokenAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, @@ -79,7 +79,7 @@ export class OrderValidationUtils { TradeSide.Taker, TransferType.Trade, ); - const makerFeeAmount = utils.getPartialAmount( + const makerFeeAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.makerFee, @@ -92,7 +92,7 @@ export class OrderValidationUtils { TradeSide.Maker, TransferType.Fee, ); - const takerFeeAmount = utils.getPartialAmount( + const takerFeeAmount = utils.getPartialAmountFloor( fillTakerAssetAmount, signedOrder.takerAssetAmount, signedOrder.takerFee, @@ -218,7 +218,7 @@ export class OrderValidationUtils { zrxAssetData, ); - const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingError( + const wouldRoundingErrorOccur = OrderValidationUtils.isRoundingErrorFloor( desiredFillTakerTokenAmount, signedOrder.takerAssetAmount, signedOrder.makerAssetAmount, diff --git a/packages/order-utils/src/utils.ts b/packages/order-utils/src/utils.ts index 7aaaf0609..0ff05e8ed 100644 --- a/packages/order-utils/src/utils.ts +++ b/packages/order-utils/src/utils.ts @@ -12,7 +12,7 @@ export const utils = { const milisecondsInSecond = 1000; return new BigNumber(Date.now() / milisecondsInSecond).round(); }, - getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { + getPartialAmountFloor(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { const fillMakerTokenAmount = numerator .mul(target) .div(denominator) -- cgit v1.2.3 From a14450f367d1a1f651f85ac6b0ad710fc2785e83 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Sep 2018 11:22:31 -0700 Subject: Add order parsers to order-util --- packages/order-utils/src/index.ts | 3 ++- packages/order-utils/src/parsing_utils.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 packages/order-utils/src/parsing_utils.ts (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index 354299304..e727b3ef7 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -6,6 +6,7 @@ export { eip712Utils } from './eip712_utils'; export { marketUtils } from './market_utils'; export { rateUtils } from './rate_utils'; export { sortingUtils } from './sorting_utils'; +export { orderParsingUtils } from './parsing_utils'; export { OrderStateUtils } from './order_state_utils'; export { AbstractBalanceAndProxyAllowanceFetcher } from './abstract/abstract_balance_and_proxy_allowance_fetcher'; @@ -45,4 +46,4 @@ export { FindOrdersThatCoverMakerAssetFillAmountOpts, FeeOrdersAndRemainingFeeAmount, OrdersAndRemainingFillAmount, -} from './types'; +} from './types'; \ No newline at end of file diff --git a/packages/order-utils/src/parsing_utils.ts b/packages/order-utils/src/parsing_utils.ts new file mode 100644 index 000000000..73841c1a2 --- /dev/null +++ b/packages/order-utils/src/parsing_utils.ts @@ -0,0 +1,27 @@ +import { BigNumber } from '@0xproject/utils'; +import * as _ from 'lodash'; + +export const orderParsingUtils = { + convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any { + const result = _.assign({}, obj); + _.each(fields, field => { + _.update(result, field, (value: string) => { + if (_.isUndefined(value)) { + throw new Error(`Could not find field '${field}' while converting string fields to BigNumber.`); + } + return new BigNumber(value); + }); + }); + return result; + }, + convertOrderStringFieldsToBigNumber(order: any): any { + return orderParsingUtils.convertStringsFieldsToBigNumbers(order, [ + 'makerAssetAmount', + 'takerAssetAmount', + 'makerFee', + 'takerFee', + 'expirationTimeSeconds', + 'salt', + ]); + } +} \ No newline at end of file -- cgit v1.2.3 From f5237f79716fb9ab5f16a64445fb21ea715d70b8 Mon Sep 17 00:00:00 2001 From: fragosti Date: Tue, 4 Sep 2018 11:55:08 -0700 Subject: Use order parser utils from order utils --- packages/order-utils/src/index.ts | 2 +- packages/order-utils/src/parsing_utils.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/index.ts b/packages/order-utils/src/index.ts index e727b3ef7..1553647c6 100644 --- a/packages/order-utils/src/index.ts +++ b/packages/order-utils/src/index.ts @@ -46,4 +46,4 @@ export { FindOrdersThatCoverMakerAssetFillAmountOpts, FeeOrdersAndRemainingFeeAmount, OrdersAndRemainingFillAmount, -} from './types'; \ No newline at end of file +} from './types'; diff --git a/packages/order-utils/src/parsing_utils.ts b/packages/order-utils/src/parsing_utils.ts index 73841c1a2..232c54b7b 100644 --- a/packages/order-utils/src/parsing_utils.ts +++ b/packages/order-utils/src/parsing_utils.ts @@ -23,5 +23,5 @@ export const orderParsingUtils = { 'expirationTimeSeconds', 'salt', ]); - } -} \ No newline at end of file + }, +}; -- cgit v1.2.3 From 60e2dfdbda1e089ee4d4419243167eaeb769ff6a Mon Sep 17 00:00:00 2001 From: fragosti Date: Wed, 19 Sep 2018 15:58:30 +0200 Subject: Calculate min and max rates in buy quote --- packages/order-utils/src/market_utils.ts | 39 ++++++++++++++++++++------------ packages/order-utils/src/types.ts | 2 ++ 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/market_utils.ts b/packages/order-utils/src/market_utils.ts index 4a664cb14..ed6af7d85 100644 --- a/packages/order-utils/src/market_utils.ts +++ b/packages/order-utils/src/market_utils.ts @@ -51,17 +51,23 @@ export const marketUtils = { // iterate through the orders input from left to right until we have enough makerAsset to fill totalFillAmount const result = _.reduce( orders, - ({ resultOrders, remainingFillAmount }, order, index) => { + ({ resultOrders, remainingFillAmount, ordersRemainingFillableMakerAssetAmounts }, order, index) => { if (remainingFillAmount.lessThanOrEqualTo(constants.ZERO_AMOUNT)) { - return { resultOrders, remainingFillAmount: constants.ZERO_AMOUNT }; + return { + resultOrders, + remainingFillAmount: constants.ZERO_AMOUNT, + ordersRemainingFillableMakerAssetAmounts, + }; } else { const makerAssetAmountAvailable = remainingFillableMakerAssetAmounts[index]; + const shouldIncludeOrder = makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT); // if there is no makerAssetAmountAvailable do not append order to resultOrders // if we have exceeded the total amount we want to fill set remainingFillAmount to 0 return { - resultOrders: makerAssetAmountAvailable.gt(constants.ZERO_AMOUNT) - ? _.concat(resultOrders, order) - : resultOrders, + resultOrders: shouldIncludeOrder ? _.concat(resultOrders, order) : resultOrders, + ordersRemainingFillableMakerAssetAmounts: shouldIncludeOrder + ? _.concat(ordersRemainingFillableMakerAssetAmounts, makerAssetAmountAvailable) + : ordersRemainingFillableMakerAssetAmounts, remainingFillAmount: BigNumber.max( constants.ZERO_AMOUNT, remainingFillAmount.minus(makerAssetAmountAvailable), @@ -69,7 +75,11 @@ export const marketUtils = { }; } }, - { resultOrders: [] as T[], remainingFillAmount: totalFillAmount }, + { + resultOrders: [] as T[], + remainingFillAmount: totalFillAmount, + ordersRemainingFillableMakerAssetAmounts: [] as BigNumber[], + }, ); return result; }, @@ -133,17 +143,18 @@ export const marketUtils = { }, constants.ZERO_AMOUNT, ); - const { resultOrders, remainingFillAmount } = marketUtils.findOrdersThatCoverMakerAssetFillAmount( - feeOrders, - totalFeeAmount, - { - remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, - slippageBufferAmount, - }, - ); + const { + resultOrders, + remainingFillAmount, + ordersRemainingFillableMakerAssetAmounts, + } = marketUtils.findOrdersThatCoverMakerAssetFillAmount(feeOrders, totalFeeAmount, { + remainingFillableMakerAssetAmounts: remainingFillableFeeAmounts, + slippageBufferAmount, + }); return { resultFeeOrders: resultOrders, remainingFeeAmount: remainingFillAmount, + feeOrdersRemainingFillableMakerAssetAmounts: ordersRemainingFillableMakerAssetAmounts, }; // TODO: add more orders here to cover rounding // https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarding-contract-specification.md#over-buying-zrx diff --git a/packages/order-utils/src/types.ts b/packages/order-utils/src/types.ts index 09292e557..a843efaa4 100644 --- a/packages/order-utils/src/types.ts +++ b/packages/order-utils/src/types.ts @@ -72,10 +72,12 @@ export interface FindFeeOrdersThatCoverFeesForTargetOrdersOpts { export interface FeeOrdersAndRemainingFeeAmount { resultFeeOrders: T[]; + feeOrdersRemainingFillableMakerAssetAmounts: BigNumber[]; remainingFeeAmount: BigNumber; } export interface OrdersAndRemainingFillAmount { resultOrders: T[]; + ordersRemainingFillableMakerAssetAmounts: BigNumber[]; remainingFillAmount: BigNumber; } -- cgit v1.2.3 From 9eecf3683b674f0753e508476c4c21eb32625f18 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 25 Sep 2018 16:10:13 +0100 Subject: Add transactionHash to OrderState and emit it from OrderWatcher subscription --- packages/order-utils/src/order_state_utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/order_state_utils.ts b/packages/order-utils/src/order_state_utils.ts index 8398776aa..9b21ef6e9 100644 --- a/packages/order-utils/src/order_state_utils.ts +++ b/packages/order-utils/src/order_state_utils.ts @@ -114,7 +114,7 @@ export class OrderStateUtils { * @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 { + public async getOpenOrderStateAsync(signedOrder: SignedOrder, transactionHash?: string): Promise { const orderRelevantState = await this.getOpenOrderRelevantStateAsync(signedOrder); const orderHash = orderHashUtils.getOrderHashHex(signedOrder); const isOrderCancelled = await this._orderFilledCancelledFetcher.isOrderCancelledAsync(orderHash); @@ -134,6 +134,7 @@ export class OrderStateUtils { isValid: true, orderHash, orderRelevantState, + transactionHash, }; return orderState; } else { @@ -141,6 +142,7 @@ export class OrderStateUtils { isValid: false, orderHash, error: orderValidationResult.error, + transactionHash, }; return orderState; } -- cgit v1.2.3 From 0591f1d32a4696924d9b063b09ec8a373e6757c7 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 27 Sep 2018 10:03:54 +0100 Subject: Add address normalization to isValidECSignature method --- packages/order-utils/src/signature_utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index c0c9e71a7..3b656d3fc 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -174,6 +174,7 @@ export const signatureUtils = { assert.isHexString('data', data); assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); assert.isETHAddressHex('signerAddress', signerAddress); + const normalizedSignerAddress = signerAddress.toLowerCase(); const msgHashBuff = ethUtil.toBuffer(data); try { @@ -184,7 +185,8 @@ export const signatureUtils = { ethUtil.toBuffer(signature.s), ); const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); - return retrievedAddress === signerAddress; + const normalizedRetrievedAddress = retrievedAddress.toLowerCase(); + return normalizedRetrievedAddress === normalizedSignerAddress; } catch (err) { return false; } -- cgit v1.2.3 From 059162a90a3d26e5fdfefd8553bb1f721a3116fc Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 3 Oct 2018 23:14:55 -0700 Subject: Add additional order factory methods and refactor test to use them --- packages/order-utils/src/order_factory.ts | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'packages/order-utils/src') diff --git a/packages/order-utils/src/order_factory.ts b/packages/order-utils/src/order_factory.ts index 46a69ae4d..b1292903a 100644 --- a/packages/order-utils/src/order_factory.ts +++ b/packages/order-utils/src/order_factory.ts @@ -8,8 +8,21 @@ import { orderHashUtils } from './order_hash'; import { generatePseudoRandomSalt } from './salt'; import { signatureUtils } from './signature_utils'; import { CreateOrderOpts } from './types'; - export const orderFactory = { + createOrderFromPartial(partialOrder: Partial): Order { + const defaultOrder = generateEmptyOrder(); + return { + ...defaultOrder, + ...partialOrder, + }; + }, + createSignedOrderFromPartial(partialSignedOrder: Partial): SignedOrder { + const defaultOrder = generateEmptySignedOrder(); + return { + ...defaultOrder, + ...partialSignedOrder, + }; + }, createOrder( makerAddress: string, makerAssetAmount: BigNumber, @@ -69,6 +82,30 @@ export const orderFactory = { }, }; +function generateEmptySignedOrder(): SignedOrder { + return { + ...generateEmptyOrder(), + signature: constants.NULL_BYTES, + }; +} +function generateEmptyOrder(): Order { + return { + senderAddress: constants.NULL_ADDRESS, + makerAddress: constants.NULL_ADDRESS, + takerAddress: constants.NULL_ADDRESS, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + makerAssetAmount: constants.ZERO_AMOUNT, + takerAssetAmount: constants.ZERO_AMOUNT, + makerAssetData: constants.NULL_BYTES, + takerAssetData: constants.NULL_BYTES, + salt: generatePseudoRandomSalt(), + exchangeAddress: constants.NULL_ADDRESS, + feeRecipientAddress: constants.NULL_ADDRESS, + expirationTimeSeconds: constants.INFINITE_TIMESTAMP_SEC, + }; +} + function generateDefaultCreateOrderOpts(): { takerAddress: string; senderAddress: string; -- cgit v1.2.3