diff options
author | Leonid <logvinov.leon@gmail.com> | 2017-06-09 22:58:52 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-09 22:58:52 +0800 |
commit | 3a9d07c40ce2e26203962487036132d6357ec530 (patch) | |
tree | 87a2416fa3c0506ce0fffe2021de05069db4fbc2 /src | |
parent | 389894088d2372e1eb9bf4d39bde2efe8ab38713 (diff) | |
parent | ba9a0b0bb9e87e69c818a5fe73183d8d22476948 (diff) | |
download | dexon-0x-contracts-3a9d07c40ce2e26203962487036132d6357ec530.tar.gz dexon-0x-contracts-3a9d07c40ce2e26203962487036132d6357ec530.tar.zst dexon-0x-contracts-3a9d07c40ce2e26203962487036132d6357ec530.zip |
Merge pull request #49 from 0xProject/fillOrderUpToAsync
Implement fillOrderUpToAsync
Diffstat (limited to 'src')
-rw-r--r-- | src/contract_wrappers/exchange_wrapper.ts | 75 | ||||
-rw-r--r-- | src/schemas/signed_orders_schema.ts | 5 | ||||
-rw-r--r-- | src/types.ts | 9 | ||||
-rw-r--r-- | src/utils/assert.ts | 3 |
4 files changed, 87 insertions, 5 deletions
diff --git a/src/contract_wrappers/exchange_wrapper.ts b/src/contract_wrappers/exchange_wrapper.ts index ed0438372..5473e2565 100644 --- a/src/contract_wrappers/exchange_wrapper.ts +++ b/src/contract_wrappers/exchange_wrapper.ts @@ -28,6 +28,7 @@ import {utils} from '../utils/utils'; import {ContractWrapper} from './contract_wrapper'; import * as ExchangeArtifacts from '../artifacts/Exchange.json'; import {ecSignatureSchema} from '../schemas/ec_signature_schema'; +import {signedOrdersSchema} from '../schemas/signed_orders_schema'; import {orderFillRequestsSchema} from '../schemas/order_fill_requests_schema'; import {orderCancellationRequestsSchema} from '../schemas/order_cancel_schema'; import {orderFillOrKillRequestsSchema} from '../schemas/order_fill_or_kill_requests_schema'; @@ -163,6 +164,72 @@ export class ExchangeWrapper extends ContractWrapper { this.throwErrorLogsAsErrors(response.logs); } /** + * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount. + * If the fill amount is reached - it succeeds and does not fill the rest of the orders. + * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds. + */ + public async fillOrdersUpToAsync(signedOrders: SignedOrder[], takerTokenFillAmount: BigNumber.BigNumber, + shouldCheckTransfer: boolean, takerAddress: string): Promise<void> { + const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); + assert.hasAtMostOneUniqueValue(takerTokenAddresses, + ExchangeContractErrs.MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED); + assert.isBigNumber('takerTokenFillAmount', takerTokenFillAmount); + assert.isBoolean('shouldCheckTransfer', shouldCheckTransfer); + assert.doesConformToSchema( + 'signedOrders', SchemaValidator.convertToJSONSchemaCompatibleObject(signedOrders), signedOrdersSchema + ); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this.web3Wrapper); + _.forEach(signedOrders, + async (signedOrder: SignedOrder, i: number) => { + await this.validateFillOrderAndThrowIfInvalidAsync( + signedOrder, takerTokenFillAmount, takerAddress); + }); + if (_.isEmpty(signedOrders)) { + return; // no-op + } + + const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => { + return [ + ...ExchangeWrapper.getOrderAddressesAndValues(signedOrder), + signedOrder.ecSignature.v, + signedOrder.ecSignature.r, + signedOrder.ecSignature.s, + ]; + }); + // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( + const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>( + orderAddressesValuesAndSignatureArray, + ); + + const exchangeInstance = await this.getExchangeContractAsync(); + const gas = await exchangeInstance.fillUpTo.estimateGas( + orderAddressesArray, + orderValuesArray, + takerTokenFillAmount, + shouldCheckTransfer, + vArray, + rArray, + sArray, + { + from: takerAddress, + }, + ); + const response: ContractResponse = await exchangeInstance.fillUpTo( + orderAddressesArray, + orderValuesArray, + takerTokenFillAmount, + shouldCheckTransfer, + vArray, + rArray, + sArray, + { + from: takerAddress, + gas, + }, + ); + this.throwErrorLogsAsErrors(response.logs); + } + /** * Batch version of fillOrderAsync. * Executes multiple fills atomically in a single transaction. * If shouldCheckTransfer is set to true, it will continue filling subsequent orders even when earlier ones fail. @@ -366,11 +433,8 @@ export class ExchangeWrapper extends ContractWrapper { * All orders must be from the same maker. */ public async batchCancelOrderAsync(orderCancellationRequests: OrderCancellationRequest[]): Promise<void> { - if (_.isEmpty(orderCancellationRequests)) { - return; // no-op - } const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker); - assert.assert(_.uniq(makers).length === 1, ExchangeContractErrs.MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH); + assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED); const maker = makers[0]; await assert.isSenderAddressAsync('maker', maker, this.web3Wrapper); assert.doesConformToSchema('orderCancellationRequests', @@ -381,6 +445,9 @@ export class ExchangeWrapper extends ContractWrapper { cancellationRequest.order, cancellationRequest.takerTokenCancelAmount, ); }); + if (_.isEmpty(orderCancellationRequests)) { + return; // no-op + } const exchangeInstance = await this.getExchangeContractAsync(); const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => { return [ diff --git a/src/schemas/signed_orders_schema.ts b/src/schemas/signed_orders_schema.ts new file mode 100644 index 000000000..3b3b23649 --- /dev/null +++ b/src/schemas/signed_orders_schema.ts @@ -0,0 +1,5 @@ +export const signedOrdersSchema = { + id: '/signedOrdersSchema', + type: 'array', + items: {$ref: '/signedOrderSchema'}, +}; diff --git a/src/types.ts b/src/types.ts index dc5a5e6b3..5237fdb1b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -74,6 +74,12 @@ export interface ExchangeContract extends ContractInstance { estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmounts: BigNumber.BigNumber[], shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts) => number; }; + fillUpTo: { + (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber, + shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts): ContractResponse; + estimateGas: (orderAddresses: OrderAddresses[], orderValues: OrderValues[], fillAmount: BigNumber.BigNumber, + shouldCheckTransfer: boolean, v: number[], r: string[], s: string[], txOpts?: TxOpts) => number; + }; cancel: { (orderAddresses: OrderAddresses, orderValues: OrderValues, cancelAmount: BigNumber.BigNumber, txOpts?: TxOpts): ContractResponse; @@ -163,8 +169,9 @@ export const ExchangeContractErrs = strEnum([ 'INSUFFICIENT_MAKER_FEE_BALANCE', 'INSUFFICIENT_MAKER_FEE_ALLOWANCE', 'TRANSACTION_SENDER_IS_NOT_FILL_ORDER_TAKER', - 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH', + 'MULTIPLE_MAKERS_IN_SINGLE_CANCEL_BATCH_DISALLOWED', 'INSUFFICIENT_REMAINING_FILL_AMOUNT', + 'MULTIPLE_TAKER_TOKENS_IN_FILL_UP_TO_DISALLOWED', ]); export type ExchangeContractErrs = keyof typeof ExchangeContractErrs; diff --git a/src/utils/assert.ts b/src/utils/assert.ts index d93ac71f7..94b119d5a 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -38,6 +38,9 @@ export const assert = { const availableAddresses = await web3Wrapper.getAvailableAddressesAsync(); this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 instance'); }, + hasAtMostOneUniqueValue(value: any[], errMsg: string): void { + this.assert(_.uniq(value).length <= 1, errMsg); + }, isNumber(variableName: string, value: number): void { this.assert(_.isFinite(value), this.typeAssertionMessage(variableName, 'number', value)); }, |