aboutsummaryrefslogtreecommitdiffstats
path: root/packages/0x.js
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2018-05-09 02:04:31 +0800
committerBrandon Millman <brandon.millman@gmail.com>2018-05-09 02:04:31 +0800
commit607d738342a839788b59e0dee63849ea447331bc (patch)
treed90078ec0e95357a2fe9fb4af65f9135e9e44408 /packages/0x.js
parentb8c611de2b82657a274c55007ffc5d72807eae7f (diff)
parente9d70b7b1edb5089a6e5e2a9ee8c964ab4b4d4ab (diff)
downloaddexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar.gz
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar.bz2
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar.lz
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar.xz
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.tar.zst
dexon-sol-tools-607d738342a839788b59e0dee63849ea447331bc.zip
Merge branch 'development' into feature/website/top-bar-redesign
* development: (63 commits) Update The Ocean logo Fix artifacts paths Create an artifacts folder Introduce a var Add removeHexPrefix util method CHeck if ABI exists Improve the readability of the check for should compile Use named constants Add a comment Fix comments Rename args to constructor-args Fix a typo Define a separator const Move artifacts from src/artifacts to artifacts/v1 Fix sol-cov to work with the new artifacts format Implement new artifacts format Publish Updated CHANGELOGS Make node types a dependency Fix type errors in CSS properties ...
Diffstat (limited to 'packages/0x.js')
-rw-r--r--packages/0x.js/CHANGELOG.json35
-rw-r--r--packages/0x.js/CHANGELOG.md15
-rw-r--r--packages/0x.js/package.json33
-rw-r--r--packages/0x.js/src/0x.ts108
-rw-r--r--packages/0x.js/src/contract_wrappers/exchange_wrapper.ts7
-rw-r--r--packages/0x.js/src/types.ts1
-rw-r--r--packages/0x.js/src/utils/assert.ts10
-rw-r--r--packages/0x.js/src/utils/constants.ts1
-rw-r--r--packages/0x.js/src/utils/exchange_transfer_simulator.ts8
-rw-r--r--packages/0x.js/src/utils/order_validation_utils.ts32
-rw-r--r--packages/0x.js/src/utils/signature_utils.ts45
-rw-r--r--packages/0x.js/src/utils/utils.ts50
-rw-r--r--packages/0x.js/test/0x.js_test.ts133
-rw-r--r--packages/0x.js/test/assert_test.ts43
-rw-r--r--packages/0x.js/test/order_validation_test.ts39
-rw-r--r--packages/0x.js/test/utils/fill_scenarios.ts4
-rw-r--r--packages/0x.js/test/utils/order_factory.ts46
-rw-r--r--packages/0x.js/test/utils/web3_wrapper.ts3
18 files changed, 162 insertions, 451 deletions
diff --git a/packages/0x.js/CHANGELOG.json b/packages/0x.js/CHANGELOG.json
index ef4bb1e07..bdc575903 100644
--- a/packages/0x.js/CHANGELOG.json
+++ b/packages/0x.js/CHANGELOG.json
@@ -1,12 +1,43 @@
[
{
- "version": "0.36.4",
+ "timestamp": 1525477860,
+ "version": "0.37.2",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
+ "timestamp": 1525453812,
+ "version": "0.37.1",
+ "changes": [
+ {
+ "note": "Dependencies updated"
+ }
+ ]
+ },
+ {
+ "version": "0.37.0",
"changes": [
{
"note": "Fixed expiration watcher comparator to handle orders with equal expiration times",
"pr": 526
+ },
+ {
+ "note": "Update Web3 Provider Engine to 14.0.4",
+ "pr": 555
+ },
+ {
+ "note": "Add `zeroEx.getProvider()`",
+ "pr": 559
+ },
+ {
+ "note": "Move `ZeroExError.InvalidSignature` to `@0xproject/order-utils` `OrderError.InvalidSignature`",
+ "pr": 559
}
- ]
+ ],
+ "timestamp": 1525428773
},
{
"version": "0.36.3",
diff --git a/packages/0x.js/CHANGELOG.md b/packages/0x.js/CHANGELOG.md
index f800b86db..353e0de29 100644
--- a/packages/0x.js/CHANGELOG.md
+++ b/packages/0x.js/CHANGELOG.md
@@ -5,6 +5,21 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
+## v0.37.2 - _May 5, 2018_
+
+ * Dependencies updated
+
+## v0.37.1 - _May 4, 2018_
+
+ * Dependencies updated
+
+## v0.37.0 - _May 4, 2018_
+
+ * Fixed expiration watcher comparator to handle orders with equal expiration times (#526)
+ * Update Web3 Provider Engine to 14.0.4 (#555)
+ * Add `zeroEx.getProvider()` (#559)
+ * Move `ZeroExError.InvalidSignature` to `@0xproject/order-utils` `OrderError.InvalidSignature` (#559)
+
## v0.36.3 - _April 18, 2018_
* Move @0xproject/migrations to devDependencies
diff --git a/packages/0x.js/package.json b/packages/0x.js/package.json
index e7cdff09e..a23e01938 100644
--- a/packages/0x.js/package.json
+++ b/packages/0x.js/package.json
@@ -1,6 +1,6 @@
{
"name": "0x.js",
- "version": "0.36.3",
+ "version": "0.37.2",
"description": "A javascript library for interacting with the 0x protocol",
"keywords": [
"0x.js",
@@ -21,7 +21,7 @@
"test": "run-s clean test:commonjs",
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
- "update_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/src/artifacts/$i.json test/artifacts; done;",
+ "update_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/artifacts/1.0.0/$i.json test/artifacts; done;",
"clean": "shx rm -rf _bundles lib test_temp scripts",
"build:umd:prod": "NODE_ENV=production webpack",
"build:commonjs": "tsc && yarn update_artifacts && copyfiles -u 2 './src/compact_artifacts/**/*.json' ./lib/src/compact_artifacts && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts",
@@ -61,12 +61,12 @@
"node": ">=6.0.0"
},
"devDependencies": {
- "@0xproject/deployer": "^0.4.1",
- "@0xproject/dev-utils": "^0.3.6",
- "@0xproject/migrations": "^0.0.3",
- "@0xproject/monorepo-scripts": "^0.1.18",
- "@0xproject/subproviders": "^0.9.0",
- "@0xproject/tslint-config": "^0.4.16",
+ "@0xproject/deployer": "^0.4.3",
+ "@0xproject/dev-utils": "^0.4.1",
+ "@0xproject/migrations": "^0.0.5",
+ "@0xproject/monorepo-scripts": "^0.1.19",
+ "@0xproject/subproviders": "^0.10.1",
+ "@0xproject/tslint-config": "^0.4.17",
"@types/bintrees": "^1.0.2",
"@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42",
@@ -93,17 +93,18 @@
"tslint": "5.8.0",
"typedoc": "0xProject/typedoc",
"typescript": "2.7.1",
- "web3-provider-engine": "^13.0.1",
+ "web3-provider-engine": "^14.0.4",
"webpack": "^3.1.0"
},
"dependencies": {
- "@0xproject/assert": "^0.2.7",
- "@0xproject/base-contract": "^0.2.1",
- "@0xproject/json-schemas": "^0.7.21",
- "@0xproject/types": "^0.6.1",
- "@0xproject/typescript-typings": "^0.2.0",
- "@0xproject/utils": "^0.5.2",
- "@0xproject/web3-wrapper": "^0.6.1",
+ "@0xproject/assert": "^0.2.9",
+ "@0xproject/base-contract": "^0.3.1",
+ "@0xproject/json-schemas": "^0.7.23",
+ "@0xproject/order-utils": "^0.0.4",
+ "@0xproject/types": "^0.6.3",
+ "@0xproject/typescript-typings": "^0.3.1",
+ "@0xproject/utils": "^0.6.1",
+ "@0xproject/web3-wrapper": "^0.6.3",
"bintrees": "^1.0.2",
"bn.js": "^4.11.8",
"ethereumjs-abi": "^0.6.4",
diff --git a/packages/0x.js/src/0x.ts b/packages/0x.js/src/0x.ts
index 94d97c23e..78f9f6beb 100644
--- a/packages/0x.js/src/0x.ts
+++ b/packages/0x.js/src/0x.ts
@@ -1,4 +1,11 @@
import { schemas, SchemaValidator } from '@0xproject/json-schemas';
+import {
+ generatePseudoRandomSalt,
+ getOrderHashHex,
+ isValidOrderHash,
+ isValidSignature,
+ signOrderHashAsync,
+} from '@0xproject/order-utils';
import { ECSignature, Order, Provider, SignedOrder, TransactionReceiptWithDecodedLogs } from '@0xproject/types';
import { AbiDecoder, BigNumber, intervalUtils } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
@@ -19,7 +26,6 @@ import { OrderStateWatcherConfig, ZeroExConfig, ZeroExError } from './types';
import { assert } from './utils/assert';
import { constants } from './utils/constants';
import { decorators } from './utils/decorators';
-import { signatureUtils } from './utils/signature_utils';
import { utils } from './utils/utils';
/**
@@ -33,7 +39,6 @@ export class ZeroEx {
* this constant for your convenience.
*/
public static NULL_ADDRESS = constants.NULL_ADDRESS;
-
/**
* An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
*/
@@ -59,6 +64,15 @@ export class ZeroEx {
public proxy: TokenTransferProxyWrapper;
private _web3Wrapper: Web3Wrapper;
/**
+ * Generates a pseudo-random 256-bit salt.
+ * The salt can be included in a 0x order, ensuring that the order generates a unique orderHash
+ * and will not collide with other outstanding orders that are identical in all other parameters.
+ * @return A pseudo-random 256-bit number that can be used as a salt.
+ */
+ public static generatePseudoRandomSalt(): BigNumber {
+ return generatePseudoRandomSalt();
+ }
+ /**
* Verifies that the elliptic curve signature `signature` was generated
* by signing `data` with the private key corresponding to the `signerAddress` address.
* @param data The hex encoded data signed by the supplied signature.
@@ -67,27 +81,15 @@ export class ZeroEx {
* @return Whether the signature is valid for the supplied signerAddress and data.
*/
public static isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- assert.isHexString('data', data);
- assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const normalizedSignerAddress = signerAddress.toLowerCase();
-
- const isValidSignature = signatureUtils.isValidSignature(data, signature, normalizedSignerAddress);
- return isValidSignature;
+ return isValidSignature(data, signature, signerAddress);
}
/**
- * Generates a pseudo-random 256-bit salt.
- * The salt can be included in a 0x order, ensuring that the order generates a unique orderHash
- * and will not collide with other outstanding orders that are identical in all other parameters.
- * @return A pseudo-random 256-bit number that can be used as a salt.
+ * Computes the orderHash for a supplied order.
+ * @param order An object that conforms to the Order or SignedOrder interface definitions.
+ * @return The resulting orderHash from hashing the supplied order.
*/
- public static generatePseudoRandomSalt(): BigNumber {
- // BigNumber.random returns a pseudo-random number between 0 & 1 with a passed in number of decimal places.
- // Source: https://mikemcl.github.io/bignumber.js/#random
- const randomNumber = BigNumber.random(constants.MAX_DIGITS_IN_UNSIGNED_256_INT);
- const factor = new BigNumber(10).pow(constants.MAX_DIGITS_IN_UNSIGNED_256_INT - 1);
- const salt = randomNumber.times(factor).round();
- return salt;
+ public static getOrderHashHex(order: Order | SignedOrder): string {
+ return getOrderHashHex(order);
}
/**
* Checks if the supplied hex encoded order hash is valid.
@@ -97,12 +99,7 @@ export class ZeroEx {
* @return Whether the supplied orderHash has the expected format.
*/
public static isValidOrderHash(orderHash: string): boolean {
- // Since this method can be called to check if any arbitrary string conforms to an orderHash's
- // format, we only assert that we were indeed passed a string.
- assert.isString('orderHash', orderHash);
- const schemaValidator = new SchemaValidator();
- const isValidOrderHash = schemaValidator.validate(orderHash, schemas.orderHashSchema).valid;
- return isValidOrderHash;
+ return isValidOrderHash(orderHash);
}
/**
* A unit amount is defined as the amount of a token above the specified decimal places (integer part).
@@ -133,17 +130,6 @@ export class ZeroEx {
return baseUnitAmount;
}
/**
- * Computes the orderHash for a supplied order.
- * @param order An object that conforms to the Order or SignedOrder interface definitions.
- * @return The resulting orderHash from hashing the supplied order.
- */
- @decorators.syncZeroExErrorHandler
- public static getOrderHashHex(order: Order | SignedOrder): string {
- assert.doesConformToSchema('order', order, schemas.orderSchema);
- const orderHashHex = utils.getOrderHashHex(order);
- return orderHashHex;
- }
- /**
* Instantiates a new ZeroEx instance that provides the public interface to the 0x.js library.
* @param provider The Provider instance you would like the 0x.js library to use for interacting with
* the Ethereum network.
@@ -205,6 +191,13 @@ export class ZeroEx {
(this.etherToken as any)._setNetworkId(networkId);
}
/**
+ * Get the provider instance currently used by 0x.js
+ * @return Web3 provider instance
+ */
+ public getProvider(): Provider {
+ return this._web3Wrapper.getProvider();
+ }
+ /**
* Get user Ethereum addresses available through the supplied web3 provider available for sending transactions.
* @return An array of available user Ethereum addresses.
*/
@@ -229,41 +222,12 @@ export class ZeroEx {
signerAddress: string,
shouldAddPersonalMessagePrefix: boolean,
): Promise<ECSignature> {
- assert.isHexString('orderHash', orderHash);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
- const normalizedSignerAddress = signerAddress.toLowerCase();
-
- let msgHashHex = orderHash;
- if (shouldAddPersonalMessagePrefix) {
- const orderHashBuff = ethUtil.toBuffer(orderHash);
- const msgHashBuff = ethUtil.hashPersonalMessage(orderHashBuff);
- msgHashHex = ethUtil.bufferToHex(msgHashBuff);
- }
-
- const signature = await this._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.
- const validVParamValues = [27, 28];
- const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
- if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, normalizedSignerAddress);
- if (isValidVRSSignature) {
- return ecSignatureVRS;
- }
- }
-
- const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
- if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, normalizedSignerAddress);
- if (isValidRSVSignature) {
- return ecSignatureRSV;
- }
- }
-
- throw new Error(ZeroExError.InvalidSignature);
+ return signOrderHashAsync(
+ this._web3Wrapper.getProvider(),
+ orderHash,
+ signerAddress,
+ shouldAddPersonalMessagePrefix,
+ );
}
/**
* Waits for a transaction to be mined and returns the transaction receipt.
diff --git a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
index 7cda70f16..6ddaaaf4f 100644
--- a/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
+++ b/packages/0x.js/src/contract_wrappers/exchange_wrapper.ts
@@ -1,4 +1,5 @@
import { schemas } from '@0xproject/json-schemas';
+import { getOrderHashHex } from '@0xproject/order-utils';
import {
BlockParamLiteral,
DecodedLogArgs,
@@ -570,7 +571,7 @@ export class ExchangeWrapper extends ContractWrapper {
? SHOULD_VALIDATE_BY_DEFAULT
: orderTransactionOpts.shouldValidate;
if (shouldValidate) {
- const orderHash = utils.getOrderHashHex(order);
+ const orderHash = getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order,
@@ -629,7 +630,7 @@ export class ExchangeWrapper extends ContractWrapper {
: orderTransactionOpts.shouldValidate;
if (shouldValidate) {
for (const orderCancellationRequest of orderCancellationRequests) {
- const orderHash = utils.getOrderHashHex(orderCancellationRequest.order);
+ const orderHash = getOrderHashHex(orderCancellationRequest.order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid(
orderCancellationRequest.order,
@@ -801,7 +802,7 @@ export class ExchangeWrapper extends ContractWrapper {
): Promise<void> {
assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount);
- const orderHash = utils.getOrderHashHex(order);
+ const orderHash = getOrderHashHex(order);
const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils.validateCancelOrderThrowIfInvalid(
order,
diff --git a/packages/0x.js/src/types.ts b/packages/0x.js/src/types.ts
index 151204928..ae9f98c5f 100644
--- a/packages/0x.js/src/types.ts
+++ b/packages/0x.js/src/types.ts
@@ -27,7 +27,6 @@ export enum ZeroExError {
TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST',
UnhandledError = 'UNHANDLED_ERROR',
UserHasNoAssociatedAddress = 'USER_HAS_NO_ASSOCIATED_ADDRESSES',
- InvalidSignature = 'INVALID_SIGNATURE',
ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
diff --git a/packages/0x.js/src/utils/assert.ts b/packages/0x.js/src/utils/assert.ts
index 5e8004cd0..2588a4d09 100644
--- a/packages/0x.js/src/utils/assert.ts
+++ b/packages/0x.js/src/utils/assert.ts
@@ -8,13 +8,13 @@ import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash';
-import { signatureUtils } from '../utils/signature_utils';
+import { isValidSignature } from '@0xproject/order-utils';
export const assert = {
...sharedAssert,
isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string) {
- const isValidSignature = signatureUtils.isValidSignature(orderHash, ecSignature, signerAddress);
- this.assert(isValidSignature, `Expected order with hash '${orderHash}' to have a valid signature`);
+ const isValid = isValidSignature(orderHash, ecSignature, signerAddress);
+ this.assert(isValid, `Expected order with hash '${orderHash}' to have a valid signature`);
},
async isSenderAddressAsync(
variableName: string,
@@ -28,8 +28,4 @@ export const assert = {
`Specified ${variableName} ${senderAddressHex} isn't available through the supplied web3 provider`,
);
},
- async isUserAddressAvailableAsync(web3Wrapper: Web3Wrapper): Promise<void> {
- const availableAddresses = await web3Wrapper.getAvailableAddressesAsync();
- this.assert(!_.isEmpty(availableAddresses), 'No addresses were available on the provided web3 provider');
- },
};
diff --git a/packages/0x.js/src/utils/constants.ts b/packages/0x.js/src/utils/constants.ts
index 06beec8e2..07da6745d 100644
--- a/packages/0x.js/src/utils/constants.ts
+++ b/packages/0x.js/src/utils/constants.ts
@@ -3,7 +3,6 @@ import { BigNumber } from '@0xproject/utils';
export const constants = {
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
TESTRPC_NETWORK_ID: 50,
- MAX_DIGITS_IN_UNSIGNED_256_INT: 78,
INVALID_JUMP_PATTERN: 'invalid JUMP at',
OUT_OF_GAS_PATTERN: 'out of gas',
INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
diff --git a/packages/0x.js/src/utils/exchange_transfer_simulator.ts b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
index 9a920c643..f8301f5c2 100644
--- a/packages/0x.js/src/utils/exchange_transfer_simulator.ts
+++ b/packages/0x.js/src/utils/exchange_transfer_simulator.ts
@@ -5,6 +5,7 @@ import * as _ from 'lodash';
import { TokenWrapper } from '../contract_wrappers/token_wrapper';
import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store';
import { ExchangeContractErrs, TradeSide, TransferType } from '../types';
+import { constants } from '../utils/constants';
enum FailureReason {
Balance = 'balance',
@@ -66,6 +67,13 @@ export class ExchangeTransferSimulator {
tradeSide: TradeSide,
transferType: TransferType,
): Promise<void> {
+ // HACK: When simulating an open order (e.g taker is NULL_ADDRESS), we don't want to adjust balances/
+ // allowances for the taker. We do however, want to increase the balance of the maker since the maker
+ // might be relying on those funds to fill subsequent orders or pay the order's fees.
+ if (from === constants.NULL_ADDRESS && tradeSide === TradeSide.Taker) {
+ await this._increaseBalanceAsync(tokenAddress, to, amountInBaseUnits);
+ return;
+ }
const balance = await this._store.getBalanceAsync(tokenAddress, from);
const proxyAllowance = await this._store.getProxyAllowanceAsync(tokenAddress, from);
if (proxyAllowance.lessThan(amountInBaseUnits)) {
diff --git a/packages/0x.js/src/utils/order_validation_utils.ts b/packages/0x.js/src/utils/order_validation_utils.ts
index f32bf43d0..a13c3dc04 100644
--- a/packages/0x.js/src/utils/order_validation_utils.ts
+++ b/packages/0x.js/src/utils/order_validation_utils.ts
@@ -1,3 +1,4 @@
+import { getOrderHashHex, OrderError } from '@0xproject/order-utils';
import { Order, SignedOrder } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as _ from 'lodash';
@@ -113,7 +114,7 @@ export class OrderValidationUtils {
zrxTokenAddress: string,
expectedFillTakerTokenAmount?: BigNumber,
): Promise<void> {
- const orderHash = utils.getOrderHashHex(signedOrder);
+ const orderHash = getOrderHashHex(signedOrder);
const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
signedOrder.takerTokenAmount,
@@ -124,31 +125,12 @@ export class OrderValidationUtils {
if (!_.isUndefined(expectedFillTakerTokenAmount)) {
fillTakerTokenAmount = expectedFillTakerTokenAmount;
}
- const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount(
+ await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync(
+ exchangeTradeEmulator,
+ signedOrder,
fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerTokenAmount,
- );
- await exchangeTradeEmulator.transferFromAsync(
- signedOrder.makerTokenAddress,
- signedOrder.maker,
signedOrder.taker,
- fillMakerTokenAmount,
- TradeSide.Maker,
- TransferType.Trade,
- );
- const makerFeeAmount = OrderValidationUtils._getPartialAmount(
- fillTakerTokenAmount,
- signedOrder.takerTokenAmount,
- signedOrder.makerFee,
- );
- await exchangeTradeEmulator.transferFromAsync(
zrxTokenAddress,
- signedOrder.maker,
- signedOrder.feeRecipient,
- makerFeeAmount,
- TradeSide.Maker,
- TransferType.Fee,
);
}
public async validateFillOrderThrowIfInvalidAsync(
@@ -161,9 +143,9 @@ export class OrderValidationUtils {
if (fillTakerTokenAmount.eq(0)) {
throw new Error(ExchangeContractErrs.OrderFillAmountZero);
}
- const orderHash = utils.getOrderHashHex(signedOrder);
+ const orderHash = getOrderHashHex(signedOrder);
if (!ZeroEx.isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) {
- throw new Error(ZeroExError.InvalidSignature);
+ throw new Error(OrderError.InvalidSignature);
}
const unavailableTakerTokenAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash);
OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow(
diff --git a/packages/0x.js/src/utils/signature_utils.ts b/packages/0x.js/src/utils/signature_utils.ts
deleted file mode 100644
index 46f167339..000000000
--- a/packages/0x.js/src/utils/signature_utils.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { ECSignature } from '@0xproject/types';
-import * as ethUtil from 'ethereumjs-util';
-
-export const signatureUtils = {
- isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
- const dataBuff = ethUtil.toBuffer(data);
- const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
- 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;
- }
- },
- parseSignatureHexAsVRS(signatureHex: string): ECSignature {
- const signatureBuffer = ethUtil.toBuffer(signatureHex);
- let v = signatureBuffer[0];
- if (v < 27) {
- v += 27;
- }
- const r = signatureBuffer.slice(1, 33);
- const s = signatureBuffer.slice(33, 65);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
- parseSignatureHexAsRSV(signatureHex: string): ECSignature {
- const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
- },
-};
diff --git a/packages/0x.js/src/utils/utils.ts b/packages/0x.js/src/utils/utils.ts
index c8bcd907e..af1125632 100644
--- a/packages/0x.js/src/utils/utils.ts
+++ b/packages/0x.js/src/utils/utils.ts
@@ -1,59 +1,9 @@
-import { Order, SignedOrder, SolidityTypes } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
-import BN = require('bn.js');
-import * as ethABI from 'ethereumjs-abi';
-import * as ethUtil from 'ethereumjs-util';
-import * as _ from 'lodash';
export const utils = {
- /**
- * Converts BigNumber instance to BN
- * The only reason we convert to BN is to remain compatible with `ethABI. soliditySHA3` that
- * expects values of Solidity type `uint` to be passed as type `BN`.
- * We do not use BN anywhere else in the codebase.
- */
- bigNumberToBN(value: BigNumber) {
- return new BN(value.toString(), 10);
- },
spawnSwitchErr(name: string, value: any): Error {
return new Error(`Unexpected switch value: ${value} encountered for ${name}`);
},
- getOrderHashHex(order: Order | SignedOrder): string {
- const orderParts = [
- { value: order.exchangeContractAddress, type: SolidityTypes.Address },
- { value: order.maker, type: SolidityTypes.Address },
- { value: order.taker, type: SolidityTypes.Address },
- { value: order.makerTokenAddress, type: SolidityTypes.Address },
- { value: order.takerTokenAddress, type: SolidityTypes.Address },
- { value: order.feeRecipient, type: SolidityTypes.Address },
- {
- value: utils.bigNumberToBN(order.makerTokenAmount),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.takerTokenAmount),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.makerFee),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.takerFee),
- type: SolidityTypes.Uint256,
- },
- {
- value: utils.bigNumberToBN(order.expirationUnixTimestampSec),
- type: SolidityTypes.Uint256,
- },
- { value: utils.bigNumberToBN(order.salt), type: SolidityTypes.Uint256 },
- ];
- const types = _.map(orderParts, o => o.type);
- const values = _.map(orderParts, o => o.value);
- const hashBuff = ethABI.soliditySHA3(types, values);
- const hashHex = ethUtil.bufferToHex(hashBuff);
- return hashHex;
- },
getCurrentUnixTimestampSec(): BigNumber {
return new BigNumber(Date.now() / 1000).round();
},
diff --git a/packages/0x.js/test/0x.js_test.ts b/packages/0x.js/test/0x.js_test.ts
index 838ee7080..6dccdaea7 100644
--- a/packages/0x.js/test/0x.js_test.ts
+++ b/packages/0x.js/test/0x.js_test.ts
@@ -17,8 +17,6 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
chaiSetup.configure();
const expect = chai.expect;
-const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
-
describe('ZeroEx library', () => {
let zeroEx: ZeroEx;
before(async () => {
@@ -63,14 +61,12 @@ describe('ZeroEx library', () => {
};
const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
it("should return false if the data doesn't pertain to the signature & address", async () => {
- expect(ZeroEx.isValidSignature('0x0', signature, address)).to.be.false();
return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync('0x0', signature, address),
).to.become(false);
});
it("should return false if the address doesn't pertain to the signature & data", async () => {
const validUnrelatedAddress = '0x8b0292b11a196601ed2ce54b665cafeca0347d42';
- expect(ZeroEx.isValidSignature(dataHex, signature, validUnrelatedAddress)).to.be.false();
return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(
dataHex,
@@ -81,45 +77,16 @@ describe('ZeroEx library', () => {
});
it("should return false if the signature doesn't pertain to the dataHex & address", async () => {
const wrongSignature = _.assign({}, signature, { v: 28 });
- expect(ZeroEx.isValidSignature(dataHex, wrongSignature, address)).to.be.false();
return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, wrongSignature, address),
).to.become(false);
});
it('should return true if the signature does pertain to the dataHex & address', async () => {
- const isValidSignatureLocal = ZeroEx.isValidSignature(dataHex, signature, address);
- expect(isValidSignatureLocal).to.be.true();
return expect(
(zeroEx.exchange as any)._isValidSignatureUsingContractCallAsync(dataHex, signature, address),
).to.become(true);
});
});
- describe('#generateSalt', () => {
- it('generates different salts', () => {
- const equal = ZeroEx.generatePseudoRandomSalt().eq(ZeroEx.generatePseudoRandomSalt());
- expect(equal).to.be.false();
- });
- it('generates salt in range [0..2^256)', () => {
- const salt = ZeroEx.generatePseudoRandomSalt();
- expect(salt.greaterThanOrEqualTo(0)).to.be.true();
- const twoPow256 = new BigNumber(2).pow(256);
- expect(salt.lessThan(twoPow256)).to.be.true();
- });
- });
- describe('#isValidOrderHash', () => {
- it('returns false if the value is not a hex string', () => {
- const isValid = ZeroEx.isValidOrderHash('not a hex');
- expect(isValid).to.be.false();
- });
- it('returns false if the length is wrong', () => {
- const isValid = ZeroEx.isValidOrderHash('0xdeadbeef');
- expect(isValid).to.be.false();
- });
- it('returns true if order hash is correct', () => {
- const isValid = ZeroEx.isValidOrderHash('0x' + Array(65).join('0'));
- expect(isValid).to.be.true();
- });
- });
describe('#toUnitAmount', () => {
it('should throw if invalid baseUnit amount supplied as argument', () => {
const invalidBaseUnitAmount = new BigNumber(1000000000.4);
@@ -152,106 +119,6 @@ describe('ZeroEx library', () => {
);
});
});
- describe('#getOrderHashHex', () => {
- const expectedOrderHash = '0x39da987067a3c9e5f1617694f1301326ba8c8b0498ebef5df4863bed394e3c83';
- const fakeExchangeContractAddress = '0xb69e673309512a9d726f87304c6984054f87a93b';
- const order: Order = {
- maker: constants.NULL_ADDRESS,
- taker: constants.NULL_ADDRESS,
- feeRecipient: constants.NULL_ADDRESS,
- makerTokenAddress: constants.NULL_ADDRESS,
- takerTokenAddress: constants.NULL_ADDRESS,
- exchangeContractAddress: fakeExchangeContractAddress,
- salt: new BigNumber(0),
- makerFee: new BigNumber(0),
- takerFee: new BigNumber(0),
- makerTokenAmount: new BigNumber(0),
- takerTokenAmount: new BigNumber(0),
- expirationUnixTimestampSec: new BigNumber(0),
- };
- it('calculates the order hash', async () => {
- const orderHash = ZeroEx.getOrderHashHex(order);
- expect(orderHash).to.be.equal(expectedOrderHash);
- });
- it('throws a readable error message if taker format is invalid', async () => {
- const orderWithInvalidtakerFormat = {
- ...order,
- taker: (null as any) as string,
- };
- const expectedErrorMessage =
- 'Order taker must be of type string. If you want anyone to be able to fill an order - pass ZeroEx.NULL_ADDRESS';
- expect(() => ZeroEx.getOrderHashHex(orderWithInvalidtakerFormat)).to.throw(expectedErrorMessage);
- });
- });
- describe('#signOrderHashAsync', () => {
- let stubs: Sinon.SinonStub[] = [];
- let makerAddress: string;
- before(async () => {
- const availableAddreses = await zeroEx.getAvailableAddressesAsync();
- makerAddress = availableAddreses[0];
- });
- afterEach(() => {
- // clean up any stubs after the test has completed
- _.each(stubs, s => s.restore());
- stubs = [];
- });
- it('Should return the correct ECSignature', async () => {
- const orderHash = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
- const expectedECSignature = {
- v: 27,
- r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
- s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
- };
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as R + S + V', async () => {
- const orderHash = '0x34decbedc118904df65f379a175bb39ca18209d6ce41d5ed549d54e6e0a95004';
- const signature =
- '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb021b';
- const expectedECSignature = {
- v: 27,
- r: '0x22109d11d79cb8bf96ed88625e1cd9558800c4073332a9a02857499883ee5ce3',
- s: '0x050aa3cc1f2c435e67e114cdce54b9527b4f50548342401bc5d2b77adbdacb02',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signMessageAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
-
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- it('should return the correct ECSignature for signatureHex concatenated as V + R + S', async () => {
- const orderHash = '0xc793e33ffded933b76f2f48d9aa3339fc090399d5e7f5dec8d3660f5480793f7';
- const signature =
- '0x1bc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee02dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960';
- const expectedECSignature = {
- v: 27,
- r: '0xc80bedc6756722672753413efdd749b5adbd4fd552595f59c13427407ee9aee0',
- s: '0x2dea66f25a608bbae457e020fb6decb763deb8b7192abab624997242da248960',
- };
- stubs = [
- Sinon.stub((zeroEx as any)._web3Wrapper, 'signMessageAsync').returns(Promise.resolve(signature)),
- Sinon.stub(ZeroEx, 'isValidSignature').returns(true),
- ];
-
- const ecSignature = await zeroEx.signOrderHashAsync(
- orderHash,
- makerAddress,
- SHOULD_ADD_PERSONAL_MESSAGE_PREFIX,
- );
- expect(ecSignature).to.deep.equal(expectedECSignature);
- });
- });
describe('#awaitTransactionMinedAsync', () => {
beforeEach(async () => {
await blockchainLifecycle.startAsync();
diff --git a/packages/0x.js/test/assert_test.ts b/packages/0x.js/test/assert_test.ts
index b08f3e23b..e69de29bb 100644
--- a/packages/0x.js/test/assert_test.ts
+++ b/packages/0x.js/test/assert_test.ts
@@ -1,43 +0,0 @@
-import { web3Factory } from '@0xproject/dev-utils';
-import * as chai from 'chai';
-import 'mocha';
-
-import { ZeroEx } from '../src';
-import { assert } from '../src/utils/assert';
-
-import { constants } from './utils/constants';
-import { provider } from './utils/web3_wrapper';
-
-const expect = chai.expect;
-
-describe('Assertion library', () => {
- const config = {
- networkId: constants.TESTRPC_NETWORK_ID,
- };
- const zeroEx = new ZeroEx(provider, config);
- describe('#isSenderAddressHexAsync', () => {
- it('throws when address is invalid', async () => {
- const address = '0xdeadbeef';
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, address, (zeroEx as any)._web3Wrapper),
- ).to.be.rejectedWith(`Expected ${varName} to be of type ETHAddressHex, encountered: ${address}`);
- });
- it('throws when address is unavailable', async () => {
- const validUnrelatedAddress = '0x8b0292b11a196601eddce54b665cafeca0347d42';
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, validUnrelatedAddress, (zeroEx as any)._web3Wrapper),
- ).to.be.rejectedWith(
- `Specified ${varName} ${validUnrelatedAddress} isn't available through the supplied web3 provider`,
- );
- });
- it("doesn't throw if address is available", async () => {
- const availableAddress = (await zeroEx.getAvailableAddressesAsync())[0];
- const varName = 'address';
- return expect(
- assert.isSenderAddressAsync(varName, availableAddress, (zeroEx as any)._web3Wrapper),
- ).to.become(undefined);
- });
- });
-});
diff --git a/packages/0x.js/test/order_validation_test.ts b/packages/0x.js/test/order_validation_test.ts
index c894774b8..0cb95c1b6 100644
--- a/packages/0x.js/test/order_validation_test.ts
+++ b/packages/0x.js/test/order_validation_test.ts
@@ -1,4 +1,5 @@
import { BlockchainLifecycle, devConstants } from '@0xproject/dev-utils';
+import { OrderError } from '@0xproject/order-utils';
import { BlockParamLiteral } from '@0xproject/types';
import { BigNumber } from '@0xproject/utils';
import * as chai from 'chai';
@@ -68,6 +69,40 @@ describe('OrderValidation', () => {
);
await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
});
+ it('should succeed if the maker is buying ZRX and has no ZRX balance', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ zrxTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ takerAddress,
+ fillableAmount,
+ feeRecipient,
+ );
+ const zrxMakerBalance = await zeroEx.token.getBalanceAsync(zrxTokenAddress, makerAddress);
+ await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
+ it('should succeed if the maker is buying ZRX and has no ZRX balance and there is no specified taker', async () => {
+ const makerFee = new BigNumber(2);
+ const takerFee = new BigNumber(2);
+ const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync(
+ makerTokenAddress,
+ zrxTokenAddress,
+ makerFee,
+ takerFee,
+ makerAddress,
+ constants.NULL_ADDRESS,
+ fillableAmount,
+ feeRecipient,
+ );
+ const zrxMakerBalance = await zeroEx.token.getBalanceAsync(zrxTokenAddress, makerAddress);
+ await zeroEx.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance);
+ await zeroEx.exchange.validateOrderFillableOrThrowAsync(signedOrder);
+ });
it('should succeed if the order is asymmetric and fillable', async () => {
const makerFillableAmount = fillableAmount;
const takerFillableAmount = fillableAmount.minus(4);
@@ -135,7 +170,7 @@ describe('OrderValidation', () => {
signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27;
return expect(
zeroEx.exchange.validateFillOrderThrowIfInvalidAsync(signedOrder, fillableAmount, takerAddress),
- ).to.be.rejectedWith(ZeroExError.InvalidSignature);
+ ).to.be.rejectedWith(OrderError.InvalidSignature);
});
it('should throw when the order is fully filled or cancelled', async () => {
const signedOrder = await fillScenarios.createFillableSignedOrderAsync(
@@ -469,4 +504,4 @@ describe('OrderValidation', () => {
expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee);
});
});
-});
+}); // tslint:disable-line:max-file-line-count
diff --git a/packages/0x.js/test/utils/fill_scenarios.ts b/packages/0x.js/test/utils/fill_scenarios.ts
index 7d0e8c501..5a82a56d2 100644
--- a/packages/0x.js/test/utils/fill_scenarios.ts
+++ b/packages/0x.js/test/utils/fill_scenarios.ts
@@ -1,10 +1,10 @@
+import { orderFactory } from '@0xproject/order-utils';
import { BigNumber } from '@0xproject/utils';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import { SignedOrder, Token, ZeroEx } from '../../src';
import { artifacts } from '../../src/artifacts';
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
-import { orderFactory } from '../utils/order_factory';
import { constants } from './constants';
@@ -164,7 +164,7 @@ export class FillScenarios {
]);
const signedOrder = await orderFactory.createSignedOrderAsync(
- this._zeroEx,
+ this._zeroEx.getProvider(),
makerAddress,
takerAddress,
makerFee,
diff --git a/packages/0x.js/test/utils/order_factory.ts b/packages/0x.js/test/utils/order_factory.ts
deleted file mode 100644
index 08f2081a4..000000000
--- a/packages/0x.js/test/utils/order_factory.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { BigNumber } from '@0xproject/utils';
-import * as _ from 'lodash';
-
-import { SignedOrder, ZeroEx } from '../../src';
-
-const SHOULD_ADD_PERSONAL_MESSAGE_PREFIX = false;
-
-export const orderFactory = {
- async createSignedOrderAsync(
- zeroEx: ZeroEx,
- maker: string,
- taker: string,
- makerFee: BigNumber,
- takerFee: BigNumber,
- makerTokenAmount: BigNumber,
- makerTokenAddress: string,
- takerTokenAmount: BigNumber,
- takerTokenAddress: string,
- exchangeContractAddress: string,
- feeRecipient: string,
- expirationUnixTimestampSecIfExists?: BigNumber,
- ): Promise<SignedOrder> {
- const defaultExpirationUnixTimestampSec = new BigNumber(2524604400); // Close to infinite
- const expirationUnixTimestampSec = _.isUndefined(expirationUnixTimestampSecIfExists)
- ? defaultExpirationUnixTimestampSec
- : expirationUnixTimestampSecIfExists;
- const order = {
- maker,
- taker,
- makerFee,
- takerFee,
- makerTokenAmount,
- takerTokenAmount,
- makerTokenAddress,
- takerTokenAddress,
- salt: ZeroEx.generatePseudoRandomSalt(),
- exchangeContractAddress,
- feeRecipient,
- expirationUnixTimestampSec,
- };
- const orderHash = ZeroEx.getOrderHashHex(order);
- const ecSignature = await zeroEx.signOrderHashAsync(orderHash, maker, SHOULD_ADD_PERSONAL_MESSAGE_PREFIX);
- const signedOrder: SignedOrder = _.assign(order, { ecSignature });
- return signedOrder;
- },
-};
diff --git a/packages/0x.js/test/utils/web3_wrapper.ts b/packages/0x.js/test/utils/web3_wrapper.ts
index b7b3f0b7f..b0ccfa546 100644
--- a/packages/0x.js/test/utils/web3_wrapper.ts
+++ b/packages/0x.js/test/utils/web3_wrapper.ts
@@ -1,9 +1,6 @@
import { devConstants, web3Factory } from '@0xproject/dev-utils';
import { Provider } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
-import * as Web3 from 'web3';
-
-import { constants } from './constants';
const web3 = web3Factory.create({ shouldUseInProcessGanache: true });
const provider: Provider = web3.currentProvider;