Merge branch 'development' into fix/unsubscribeAll

This commit is contained in:
Leonid Logvinov
2018-02-27 10:24:36 -08:00
committed by GitHub
11 changed files with 152 additions and 71 deletions

View File

@@ -2,8 +2,9 @@
## v0.33.0 - _TBD, 2018_ ## v0.33.0 - _TBD, 2018_
* Rename all public `_unsubscribeAll` methods to `unsubscribeAll` (#415) * Validate and lowercase all addresses in public methods (#373)
* Improve validation to force passing contract addresses on private networks (#385) * Improve validation to force passing contract addresses on private networks (#385)
* Rename all public `_unsubscribeAll` methods to `unsubscribeAll` (#415)
## v0.32.2 - _February 9, 2018_ ## v0.32.2 - _February 9, 2018_

View File

@@ -76,8 +76,9 @@ export class ZeroEx {
assert.isHexString('data', data); assert.isHexString('data', data);
assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema); assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddress', signerAddress); assert.isETHAddressHex('signerAddress', signerAddress);
const normalizedSignerAddress = signerAddress.toLowerCase();
const isValidSignature = signatureUtils.isValidSignature(data, signature, signerAddress); const isValidSignature = signatureUtils.isValidSignature(data, signature, normalizedSignerAddress);
return isValidSignature; return isValidSignature;
} }
/** /**
@@ -250,6 +251,7 @@ export class ZeroEx {
): Promise<ECSignature> { ): Promise<ECSignature> {
assert.isHexString('orderHash', orderHash); assert.isHexString('orderHash', orderHash);
await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('signerAddress', signerAddress, this._web3Wrapper);
const normalizedSignerAddress = signerAddress.toLowerCase();
let msgHashHex = orderHash; let msgHashHex = orderHash;
if (shouldAddPersonalMessagePrefix) { if (shouldAddPersonalMessagePrefix) {
@@ -258,7 +260,7 @@ export class ZeroEx {
msgHashHex = ethUtil.bufferToHex(msgHashBuff); msgHashHex = ethUtil.bufferToHex(msgHashBuff);
} }
const signature = await this._web3Wrapper.signTransactionAsync(signerAddress, msgHashHex); const signature = await this._web3Wrapper.signTransactionAsync(normalizedSignerAddress, msgHashHex);
// HACK: There is no consensus on whether the signatureHex string should be formatted as // 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) // v + r + s OR r + s + v, and different clients (even different versions of the same client)
@@ -267,7 +269,7 @@ export class ZeroEx {
const validVParamValues = [27, 28]; const validVParamValues = [27, 28];
const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature); const ecSignatureVRS = signatureUtils.parseSignatureHexAsVRS(signature);
if (_.includes(validVParamValues, ecSignatureVRS.v)) { if (_.includes(validVParamValues, ecSignatureVRS.v)) {
const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, signerAddress); const isValidVRSSignature = ZeroEx.isValidSignature(orderHash, ecSignatureVRS, normalizedSignerAddress);
if (isValidVRSSignature) { if (isValidVRSSignature) {
return ecSignatureVRS; return ecSignatureVRS;
} }
@@ -275,7 +277,7 @@ export class ZeroEx {
const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature); const ecSignatureRSV = signatureUtils.parseSignatureHexAsRSV(signature);
if (_.includes(validVParamValues, ecSignatureRSV.v)) { if (_.includes(validVParamValues, ecSignatureRSV.v)) {
const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, signerAddress); const isValidRSVSignature = ZeroEx.isValidSignature(orderHash, ecSignatureRSV, normalizedSignerAddress);
if (isValidRSVSignature) { if (isValidRSVSignature) {
return ecSignatureRSV; return ecSignatureRSV;
} }

View File

@@ -41,15 +41,18 @@ export class EtherTokenWrapper extends ContractWrapper {
depositor: string, depositor: string,
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
assert.isValidBaseUnitAmount('amountInWei', amountInWei); assert.isValidBaseUnitAmount('amountInWei', amountInWei);
await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper); await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
const normalizedDepositorAddress = depositor.toLowerCase();
const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(depositor); const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(normalizedDepositorAddress);
assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit); assert.assert(ethBalanceInWei.gte(amountInWei), ZeroExError.InsufficientEthBalanceForDeposit);
const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress); const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.deposit.sendTransactionAsync({ const txHash = await wethContract.deposit.sendTransactionAsync({
from: depositor, from: normalizedDepositorAddress,
value: amountInWei, value: amountInWei,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice, gasPrice: txOpts.gasPrice,
@@ -72,14 +75,20 @@ export class EtherTokenWrapper extends ContractWrapper {
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isValidBaseUnitAmount('amountInWei', amountInWei); assert.isValidBaseUnitAmount('amountInWei', amountInWei);
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper); await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
const normalizedWithdrawerAddress = withdrawer.toLowerCase();
const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(etherTokenAddress, withdrawer); const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync(
normalizedEtherTokenAddress,
normalizedWithdrawerAddress,
);
assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal); assert.assert(WETHBalanceInBaseUnits.gte(amountInWei), ZeroExError.InsufficientWEthBalanceForWithdrawal);
const wethContract = await this._getEtherTokenContractAsync(etherTokenAddress); const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, { const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, {
from: withdrawer, from: normalizedWithdrawerAddress,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice, gasPrice: txOpts.gasPrice,
}); });
@@ -101,11 +110,12 @@ export class EtherTokenWrapper extends ContractWrapper {
indexFilterValues: IndexedFilterValues, indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> { ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>( const logs = await this._getLogsAsync<ArgsType>(
etherTokenAddress, normalizedEtherTokenAddress,
eventName, eventName,
blockRange, blockRange,
indexFilterValues, indexFilterValues,
@@ -129,11 +139,12 @@ export class EtherTokenWrapper extends ContractWrapper {
callback: EventCallback<ArgsType>, callback: EventCallback<ArgsType>,
): string { ): string {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>( const subscriptionToken = this._subscribe<ArgsType>(
etherTokenAddress, normalizedEtherTokenAddress,
eventName, eventName,
indexFilterValues, indexFilterValues,
artifacts.EtherTokenArtifact.abi, artifacts.EtherTokenArtifact.abi,

View File

@@ -180,6 +180,7 @@ export class ExchangeWrapper extends ContractWrapper {
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
@@ -192,7 +193,7 @@ export class ExchangeWrapper extends ContractWrapper {
exchangeTradeEmulator, exchangeTradeEmulator,
signedOrder, signedOrder,
fillTakerTokenAmount, fillTakerTokenAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -208,7 +209,7 @@ export class ExchangeWrapper extends ContractWrapper {
signedOrder.ecSignature.r, signedOrder.ecSignature.r,
signedOrder.ecSignature.s, signedOrder.ecSignature.s,
{ {
from: takerAddress, from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -254,6 +255,7 @@ export class ExchangeWrapper extends ContractWrapper {
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
? SHOULD_VALIDATE_BY_DEFAULT ? SHOULD_VALIDATE_BY_DEFAULT
@@ -267,7 +269,7 @@ export class ExchangeWrapper extends ContractWrapper {
exchangeTradeEmulator, exchangeTradeEmulator,
signedOrder, signedOrder,
fillTakerTokenAmount.minus(filledTakerTokenAmount), fillTakerTokenAmount.minus(filledTakerTokenAmount),
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount); filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount);
@@ -301,7 +303,7 @@ export class ExchangeWrapper extends ContractWrapper {
rArray, rArray,
sArray, sArray,
{ {
from: takerAddress, from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -345,6 +347,7 @@ export class ExchangeWrapper extends ContractWrapper {
); );
assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
? SHOULD_VALIDATE_BY_DEFAULT ? SHOULD_VALIDATE_BY_DEFAULT
: orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
@@ -356,7 +359,7 @@ export class ExchangeWrapper extends ContractWrapper {
exchangeTradeEmulator, exchangeTradeEmulator,
orderFillRequest.signedOrder, orderFillRequest.signedOrder,
orderFillRequest.takerTokenFillAmount, orderFillRequest.takerTokenFillAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -389,7 +392,7 @@ export class ExchangeWrapper extends ContractWrapper {
rArray, rArray,
sArray, sArray,
{ {
from: takerAddress, from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -417,6 +420,7 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
@@ -430,7 +434,7 @@ export class ExchangeWrapper extends ContractWrapper {
exchangeTradeEmulator, exchangeTradeEmulator,
signedOrder, signedOrder,
fillTakerTokenAmount, fillTakerTokenAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -444,7 +448,7 @@ export class ExchangeWrapper extends ContractWrapper {
signedOrder.ecSignature.r, signedOrder.ecSignature.r,
signedOrder.ecSignature.s, signedOrder.ecSignature.s,
{ {
from: takerAddress, from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -476,6 +480,7 @@ export class ExchangeWrapper extends ContractWrapper {
ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress,
); );
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
if (_.isEmpty(orderFillRequests)) { if (_.isEmpty(orderFillRequests)) {
throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem);
} }
@@ -492,7 +497,7 @@ export class ExchangeWrapper extends ContractWrapper {
exchangeTradeEmulator, exchangeTradeEmulator,
orderFillRequest.signedOrder, orderFillRequest.signedOrder,
orderFillRequest.takerTokenFillAmount, orderFillRequest.takerTokenFillAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -520,7 +525,7 @@ export class ExchangeWrapper extends ContractWrapper {
rParams, rParams,
sParams, sParams,
{ {
from: takerAddress, from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -544,6 +549,7 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesConformToSchema('order', order, schemas.orderSchema); assert.doesConformToSchema('order', order, schemas.orderSchema);
assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount); assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount);
await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper);
const normalizedMakerAddress = order.maker.toLowerCase();
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
@@ -566,7 +572,7 @@ export class ExchangeWrapper extends ContractWrapper {
orderValues, orderValues,
cancelTakerTokenAmount, cancelTakerTokenAmount,
{ {
from: order.maker, from: normalizedMakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -603,6 +609,8 @@ export class ExchangeWrapper extends ContractWrapper {
assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed);
const maker = makers[0]; const maker = makers[0];
await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper);
const normalizedMakerAddress = maker.toLowerCase();
const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate)
? SHOULD_VALIDATE_BY_DEFAULT ? SHOULD_VALIDATE_BY_DEFAULT
: orderTransactionOpts.shouldValidate; : orderTransactionOpts.shouldValidate;
@@ -636,7 +644,7 @@ export class ExchangeWrapper extends ContractWrapper {
orderValues, orderValues,
cancelTakerTokenAmounts, cancelTakerTokenAmounts,
{ {
from: maker, from: normalizedMakerAddress,
gas: orderTransactionOpts.gasLimit, gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice, gasPrice: orderTransactionOpts.gasPrice,
}, },
@@ -757,13 +765,14 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, exchangeTradeEmulator,
signedOrder, signedOrder,
fillTakerTokenAmount, fillTakerTokenAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -803,13 +812,14 @@ export class ExchangeWrapper extends ContractWrapper {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const normalizedTakerAddress = takerAddress.toLowerCase();
const zrxTokenAddress = this.getZRXTokenAddress(); const zrxTokenAddress = this.getZRXTokenAddress();
const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest); const exchangeTradeEmulator = new ExchangeTransferSimulator(this._tokenWrapper, BlockParamLiteral.Latest);
await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( await this._orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync(
exchangeTradeEmulator, exchangeTradeEmulator,
signedOrder, signedOrder,
fillTakerTokenAmount, fillTakerTokenAmount,
takerAddress, normalizedTakerAddress,
zrxTokenAddress, zrxTokenAddress,
); );
} }
@@ -873,11 +883,12 @@ export class ExchangeWrapper extends ContractWrapper {
assert.isHexString('dataHex', dataHex); assert.isHexString('dataHex', dataHex);
assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema); assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddressHex', signerAddressHex); assert.isETHAddressHex('signerAddressHex', signerAddressHex);
const normalizedSignerAddress = signerAddressHex.toLowerCase();
const exchangeInstance = await this._getExchangeContractAsync(); const exchangeInstance = await this._getExchangeContractAsync();
const isValidSignature = await exchangeInstance.isValidSignature.callAsync( const isValidSignature = await exchangeInstance.isValidSignature.callAsync(
signerAddressHex, normalizedSignerAddress,
dataHex, dataHex,
ecSignature.v, ecSignature.v,
ecSignature.r, ecSignature.r,

View File

@@ -58,9 +58,10 @@ export class TokenRegistryWrapper extends ContractWrapper {
*/ */
public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> { public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> {
assert.isETHAddressHex('address', address); assert.isETHAddressHex('address', address);
const normalizedAddress = address.toLowerCase();
const tokenRegistryContract = await this._getTokenRegistryContractAsync(); const tokenRegistryContract = await this._getTokenRegistryContractAsync();
const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(address); const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(normalizedAddress);
const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); const token = TokenRegistryWrapper._createTokenFromMetadata(metadata);
return token; return token;
} }

View File

@@ -2,6 +2,7 @@ import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { artifacts } from '../artifacts'; import { artifacts } from '../artifacts';
import { assert } from '../utils/assert';
import { ContractWrapper } from './contract_wrapper'; import { ContractWrapper } from './contract_wrapper';
import { TokenTransferProxyContract } from './generated/token_transfer_proxy'; import { TokenTransferProxyContract } from './generated/token_transfer_proxy';
@@ -22,8 +23,12 @@ export class TokenTransferProxyWrapper extends ContractWrapper {
* @return Whether the exchangeContractAddress is authorized. * @return Whether the exchangeContractAddress is authorized.
*/ */
public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> {
assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress);
const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase();
const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync(); const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync();
const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(exchangeContractAddress); const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync(
normalizedExchangeContractAddress,
);
return isAuthorized; return isAuthorized;
} }
/** /**

View File

@@ -46,10 +46,12 @@ export class TokenWrapper extends ContractWrapper {
): Promise<BigNumber> { ): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const tokenContract = await this._getTokenContractAsync(tokenAddress); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
let balance = await tokenContract.balanceOf.callAsync(ownerAddress, defaultBlock); let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, defaultBlock);
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
balance = new BigNumber(balance); balance = new BigNumber(balance);
return balance; return balance;
@@ -72,14 +74,17 @@ export class TokenWrapper extends ContractWrapper {
amountInBaseUnits: BigNumber, amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
assert.isETHAddressHex('spenderAddress', spenderAddress); assert.isETHAddressHex('spenderAddress', spenderAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const tokenContract = await this._getTokenContractAsync(tokenAddress); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const txHash = await tokenContract.approve.sendTransactionAsync(spenderAddress, amountInBaseUnits, { const txHash = await tokenContract.approve.sendTransactionAsync(normalizedSpenderAddress, amountInBaseUnits, {
from: ownerAddress, from: normalizedOwnerAddress,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice, gasPrice: txOpts.gasPrice,
}); });
@@ -103,10 +108,16 @@ export class TokenWrapper extends ContractWrapper {
spenderAddress: string, spenderAddress: string,
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('spenderAddress', spenderAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
const txHash = await this.setAllowanceAsync( const txHash = await this.setAllowanceAsync(
tokenAddress, normalizedTokenAddress,
ownerAddress, normalizedOwnerAddress,
spenderAddress, normalizedSpenderAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts, txOpts,
); );
@@ -128,10 +139,18 @@ export class TokenWrapper extends ContractWrapper {
): Promise<BigNumber> { ): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('spenderAddress', spenderAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
const tokenContract = await this._getTokenContractAsync(tokenAddress); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock;
let allowanceInBaseUnits = await tokenContract.allowance.callAsync(ownerAddress, spenderAddress, defaultBlock); let allowanceInBaseUnits = await tokenContract.allowance.callAsync(
normalizedOwnerAddress,
normalizedSpenderAddress,
defaultBlock,
);
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits); allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
return allowanceInBaseUnits; return allowanceInBaseUnits;
@@ -149,9 +168,16 @@ export class TokenWrapper extends ContractWrapper {
): Promise<BigNumber> { ): Promise<BigNumber> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts); const allowanceInBaseUnits = await this.getAllowanceAsync(
normalizedTokenAddress,
normalizedOwnerAddress,
proxyAddress,
methodOpts,
);
return allowanceInBaseUnits; return allowanceInBaseUnits;
} }
/** /**
@@ -172,12 +198,14 @@ export class TokenWrapper extends ContractWrapper {
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress();
const txHash = await this.setAllowanceAsync( const txHash = await this.setAllowanceAsync(
tokenAddress, normalizedTokenAddress,
ownerAddress, normalizedOwnerAddress,
proxyAddress, proxyAddress,
amountInBaseUnits, amountInBaseUnits,
txOpts, txOpts,
@@ -200,9 +228,13 @@ export class TokenWrapper extends ContractWrapper {
ownerAddress: string, ownerAddress: string,
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('ownerAddress', ownerAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedOwnerAddress = ownerAddress.toLowerCase();
const txHash = await this.setProxyAllowanceAsync( const txHash = await this.setProxyAllowanceAsync(
tokenAddress, normalizedTokenAddress,
ownerAddress, normalizedOwnerAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts, txOpts,
); );
@@ -225,19 +257,22 @@ export class TokenWrapper extends ContractWrapper {
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
assert.isETHAddressHex('toAddress', toAddress); assert.isETHAddressHex('toAddress', toAddress);
await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedFromAddress = fromAddress.toLowerCase();
const normalizedToAddress = toAddress.toLowerCase();
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const tokenContract = await this._getTokenContractAsync(tokenAddress); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress); const fromAddressBalance = await this.getBalanceAsync(normalizedTokenAddress, normalizedFromAddress);
if (fromAddressBalance.lessThan(amountInBaseUnits)) { if (fromAddressBalance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.InsufficientBalanceForTransfer); throw new Error(ZeroExError.InsufficientBalanceForTransfer);
} }
const txHash = await tokenContract.transfer.sendTransactionAsync(toAddress, amountInBaseUnits, { const txHash = await tokenContract.transfer.sendTransactionAsync(normalizedToAddress, amountInBaseUnits, {
from: fromAddress, from: normalizedFromAddress,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice, gasPrice: txOpts.gasPrice,
}); });
@@ -265,30 +300,38 @@ export class TokenWrapper extends ContractWrapper {
amountInBaseUnits: BigNumber, amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {}, txOpts: TransactionOpts = {},
): Promise<string> { ): Promise<string> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isETHAddressHex('fromAddress', fromAddress);
assert.isETHAddressHex('toAddress', toAddress); assert.isETHAddressHex('toAddress', toAddress);
assert.isETHAddressHex('fromAddress', fromAddress);
assert.isETHAddressHex('tokenAddress', tokenAddress);
await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
const normalizedToAddress = toAddress.toLowerCase();
const normalizedFromAddress = fromAddress.toLowerCase();
const normalizedTokenAddress = tokenAddress.toLowerCase();
const normalizedSenderAddress = senderAddress.toLowerCase();
assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits);
const tokenContract = await this._getTokenContractAsync(tokenAddress); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const fromAddressAllowance = await this.getAllowanceAsync(tokenAddress, fromAddress, senderAddress); const fromAddressAllowance = await this.getAllowanceAsync(
normalizedTokenAddress,
normalizedFromAddress,
normalizedSenderAddress,
);
if (fromAddressAllowance.lessThan(amountInBaseUnits)) { if (fromAddressAllowance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.InsufficientAllowanceForTransfer); throw new Error(ZeroExError.InsufficientAllowanceForTransfer);
} }
const fromAddressBalance = await this.getBalanceAsync(tokenAddress, fromAddress); const fromAddressBalance = await this.getBalanceAsync(normalizedTokenAddress, normalizedFromAddress);
if (fromAddressBalance.lessThan(amountInBaseUnits)) { if (fromAddressBalance.lessThan(amountInBaseUnits)) {
throw new Error(ZeroExError.InsufficientBalanceForTransfer); throw new Error(ZeroExError.InsufficientBalanceForTransfer);
} }
const txHash = await tokenContract.transferFrom.sendTransactionAsync( const txHash = await tokenContract.transferFrom.sendTransactionAsync(
fromAddress, normalizedFromAddress,
toAddress, normalizedToAddress,
amountInBaseUnits, amountInBaseUnits,
{ {
from: senderAddress, from: normalizedSenderAddress,
gas: txOpts.gasLimit, gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice, gasPrice: txOpts.gasPrice,
}, },
@@ -311,11 +354,12 @@ export class TokenWrapper extends ContractWrapper {
callback: EventCallback<ArgsType>, callback: EventCallback<ArgsType>,
): string { ): string {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback); assert.isFunction('callback', callback);
const subscriptionToken = this._subscribe<ArgsType>( const subscriptionToken = this._subscribe<ArgsType>(
tokenAddress, normalizedTokenAddress,
eventName, eventName,
indexFilterValues, indexFilterValues,
artifacts.TokenArtifact.abi, artifacts.TokenArtifact.abi,
@@ -352,11 +396,12 @@ export class TokenWrapper extends ContractWrapper {
indexFilterValues: IndexedFilterValues, indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> { ): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('tokenAddress', tokenAddress); assert.isETHAddressHex('tokenAddress', tokenAddress);
const normalizedTokenAddress = tokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); assert.doesBelongToStringEnum('eventName', eventName, TokenEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._getLogsAsync<ArgsType>( const logs = await this._getLogsAsync<ArgsType>(
tokenAddress, normalizedTokenAddress,
eventName, eventName,
blockRange, blockRange,
indexFilterValues, indexFilterValues,
@@ -369,17 +414,18 @@ export class TokenWrapper extends ContractWrapper {
this._tokenContractsByAddress = {}; this._tokenContractsByAddress = {};
} }
private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> { private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> {
let tokenContract = this._tokenContractsByAddress[tokenAddress]; const normalizedTokenAddress = tokenAddress.toLowerCase();
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
if (!_.isUndefined(tokenContract)) { if (!_.isUndefined(tokenContract)) {
return tokenContract; return tokenContract;
} }
const web3ContractInstance = await this._instantiateContractIfExistsAsync( const web3ContractInstance = await this._instantiateContractIfExistsAsync(
artifacts.TokenArtifact, artifacts.TokenArtifact,
tokenAddress, normalizedTokenAddress,
); );
const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults()); const contractInstance = new TokenContract(web3ContractInstance, this._web3Wrapper.getContractDefaults());
tokenContract = contractInstance; tokenContract = contractInstance;
this._tokenContractsByAddress[tokenAddress] = tokenContract; this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract;
return tokenContract; return tokenContract;
} }
} }

View File

@@ -2,6 +2,7 @@
## v0.1.0 - _TBD, 2018_ ## v0.1.0 - _TBD, 2018_
* Remove isETHAddressHex checksum address check and assume address will be lowercased (#373)
* Add an optional parameter `subSchemas` to `doesConformToSchema` method (#385) * Add an optional parameter `subSchemas` to `doesConformToSchema` method (#385)
## v0.0.18 - _February 9, 2017_ ## v0.0.18 - _February 9, 2017_

View File

@@ -33,11 +33,8 @@ export const assert = {
); );
}, },
isETHAddressHex(variableName: string, value: string): void { isETHAddressHex(variableName: string, value: string): void {
this.assert(_.isString(value), this.typeAssertionMessage(variableName, 'string', value));
this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value)); this.assert(addressUtils.isAddress(value), this.typeAssertionMessage(variableName, 'ETHAddressHex', value));
this.assert(
addressUtils.isAddress(value) && value.toLowerCase() === value,
`Checksummed addresses are not supported. Convert ${variableName} to lower case before passing`,
);
}, },
doesBelongToStringEnum( doesBelongToStringEnum(
variableName: string, variableName: string,

View File

@@ -1,5 +1,9 @@
# CHANGELOG # CHANGELOG
## v0.1.12 _February 9, 2018_ ## v0.2.XX - _TBD_ 2018
* Ensure all returned user addresses are lowercase (#373)
## v0.1.12 - _February 9, 2018_
* Fix publishing issue where .npmignore was not properly excluding undesired content (#389) * Fix publishing issue where .npmignore was not properly excluding undesired content (#389)

View File

@@ -41,7 +41,8 @@ export class Web3Wrapper {
} }
public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> { public async isSenderAddressAvailableAsync(senderAddress: string): Promise<boolean> {
const addresses = await this.getAvailableAddressesAsync(); const addresses = await this.getAvailableAddressesAsync();
return _.includes(addresses, senderAddress); const normalizedAddress = senderAddress.toLowerCase();
return _.includes(addresses, normalizedAddress);
} }
public async getNodeVersionAsync(): Promise<string> { public async getNodeVersionAsync(): Promise<string> {
const nodeVersion = await promisify<string>(this._web3.version.getNode)(); const nodeVersion = await promisify<string>(this._web3.version.getNode)();
@@ -96,7 +97,8 @@ export class Web3Wrapper {
} }
public async getAvailableAddressesAsync(): Promise<string[]> { public async getAvailableAddressesAsync(): Promise<string[]> {
const addresses = await promisify<string[]>(this._web3.eth.getAccounts)(); const addresses = await promisify<string[]>(this._web3.eth.getAccounts)();
return addresses; const normalizedAddresses = _.map(addresses, address => address.toLowerCase());
return normalizedAddresses;
} }
public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> { public async getLogsAsync(filter: Web3.FilterObject): Promise<Web3.LogEntry[]> {
let fromBlock = filter.fromBlock; let fromBlock = filter.fromBlock;