aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabio B <kandinsky454@protonmail.ch>2019-02-06 18:23:21 +0800
committerGitHub <noreply@github.com>2019-02-06 18:23:21 +0800
commit8b5bb97537b5c13815928ec0939937dbd2566488 (patch)
tree790b0cb50379d748ade66adcb3e942d6c5af1578
parentb5eb47f60930d040b94177023cd147834f98db1d (diff)
parent3d7a635543b74eddf303ab4b0181ecf47dc29591 (diff)
downloaddexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar.gz
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar.bz2
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar.lz
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar.xz
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.tar.zst
dexon-0x-contracts-8b5bb97537b5c13815928ec0939937dbd2566488.zip
Merge pull request #1587 from 0xProject/fix/abiEncoder/DefaultTypesOnNullInput
Handle ABI decoding NULL for all data types
-rw-r--r--packages/utils/CHANGELOG.json9
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/data_type.ts4
-rw-r--r--packages/utils/src/abi_encoder/abstract_data_types/types/set.ts21
-rw-r--r--packages/utils/src/abi_encoder/calldata/raw_calldata.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/address.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/bool.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/int.ts12
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/pointer.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts7
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/string.ts5
-rw-r--r--packages/utils/src/abi_encoder/evm_data_types/uint.ts12
-rw-r--r--packages/utils/src/abi_encoder/utils/constants.ts6
-rw-r--r--packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts25
-rw-r--r--packages/utils/test/abi_encoder/evm_data_types_test.ts171
-rw-r--r--packages/utils/test/abi_encoder/return_values_test.ts251
16 files changed, 470 insertions, 78 deletions
diff --git a/packages/utils/CHANGELOG.json b/packages/utils/CHANGELOG.json
index 8671729d3..8e8a125ef 100644
--- a/packages/utils/CHANGELOG.json
+++ b/packages/utils/CHANGELOG.json
@@ -1,5 +1,14 @@
[
{
+ "version": "4.0.2",
+ "changes": [
+ {
+ "note": "ABI Decode NULL for all data types",
+ "pr": 1587
+ }
+ ]
+ },
+ {
"version": "4.0.1",
"changes": [
{
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
index f23324721..db4f02b89 100644
--- a/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
+++ b/packages/utils/src/abi_encoder/abstract_data_types/data_type.ts
@@ -47,7 +47,8 @@ export abstract class DataType {
const hasSelector = !_.isUndefined(selector);
const rawCalldata = new RawCalldata(calldata, hasSelector);
const rules_ = _.isUndefined(rules) ? constants.DEFAULT_DECODING_RULES : rules;
- const value = this.generateValue(rawCalldata, rules_);
+ const value =
+ rawCalldata.getSizeInBytes() > 0 ? this.generateValue(rawCalldata, rules_) : this.getDefaultValue(rules_);
return value;
}
@@ -71,6 +72,7 @@ export abstract class DataType {
public abstract generateCalldataBlock(value: any, parentBlock?: CalldataBlock): CalldataBlock;
public abstract generateValue(calldata: RawCalldata, rules: DecodingRules): any;
+ public abstract getDefaultValue(rules?: DecodingRules): any;
public abstract getSignatureType(): string;
public abstract isStatic(): boolean;
}
diff --git a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
index 2c6c4b0f6..d45088482 100644
--- a/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
+++ b/packages/utils/src/abi_encoder/abstract_data_types/types/set.ts
@@ -97,6 +97,27 @@ export abstract class AbstractSetDataType extends DataType {
return isStatic;
}
+ public getDefaultValue(rules?: DecodingRules): any[] | object {
+ let defaultValue: any[] | object;
+ if (this._isArray && _.isUndefined(this._arrayLength)) {
+ defaultValue = [];
+ } else if (!_.isUndefined(rules) && rules.shouldConvertStructsToObjects && !this._isArray) {
+ defaultValue = {};
+ _.each(this._memberIndexByName, (idx: number, key: string) => {
+ const member = this._members[idx];
+ const memberValue = member.getDefaultValue();
+ (defaultValue as { [key: string]: any })[key] = memberValue;
+ });
+ } else {
+ defaultValue = [];
+ _.each(this._members, (member: DataType, idx: number) => {
+ const memberValue = member.getDefaultValue();
+ (defaultValue as any[]).push(memberValue);
+ });
+ }
+ return defaultValue;
+ }
+
protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): SetCalldataBlock {
// Sanity check: if the set has a defined length then `value` must have the same length.
if (!_.isUndefined(this._arrayLength) && value.length !== this._arrayLength) {
diff --git a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts
index 189841989..dbc9d4942 100644
--- a/packages/utils/src/abi_encoder/calldata/raw_calldata.ts
+++ b/packages/utils/src/abi_encoder/calldata/raw_calldata.ts
@@ -79,4 +79,9 @@ export class RawCalldata {
public getSelector(): string {
return this._selector;
}
+
+ public getSizeInBytes(): number {
+ const sizeInBytes = this._value.byteLength;
+ return sizeInBytes;
+ }
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/address.ts b/packages/utils/src/abi_encoder/evm_data_types/address.ts
index 2278830eb..7e92d3888 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/address.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/address.ts
@@ -12,6 +12,7 @@ export class AddressDataType extends AbstractBlobDataType {
private static readonly _ADDRESS_SIZE_IN_BYTES = 20;
private static readonly _DECODED_ADDRESS_OFFSET_IN_BYTES =
constants.EVM_WORD_WIDTH_IN_BYTES - AddressDataType._ADDRESS_SIZE_IN_BYTES;
+ private static readonly _DEFAULT_VALUE = '0x0000000000000000000000000000000000000000';
public static matchType(type: string): boolean {
return type === SolidityTypes.Address;
@@ -43,6 +44,10 @@ export class AddressDataType extends AbstractBlobDataType {
return valueLowercase;
}
+ public getDefaultValue(): string {
+ return AddressDataType._DEFAULT_VALUE;
+ }
+
public getSignatureType(): string {
return SolidityTypes.Address;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/bool.ts b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
index ffccd6e53..24887c509 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/bool.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/bool.ts
@@ -10,6 +10,7 @@ import { constants } from '../utils/constants';
export class BoolDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
+ private static readonly _DEFAULT_VALUE: boolean = false;
public static matchType(type: string): boolean {
return type === SolidityTypes.Bool;
@@ -47,6 +48,10 @@ export class BoolDataType extends AbstractBlobDataType {
return value;
}
+ public getDefaultValue(): boolean {
+ return BoolDataType._DEFAULT_VALUE;
+ }
+
public getSignatureType(): string {
return SolidityTypes.Bool;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
index fa38b63c0..7a49a84b4 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/dynamic_bytes.ts
@@ -9,6 +9,7 @@ import { constants } from '../utils/constants';
export class DynamicBytesDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
+ private static readonly _DEFAULT_VALUE = '0x';
public static matchType(type: string): boolean {
return type === SolidityTypes.Bytes;
@@ -65,6 +66,10 @@ export class DynamicBytesDataType extends AbstractBlobDataType {
return value;
}
+ public getDefaultValue(): string {
+ return DynamicBytesDataType._DEFAULT_VALUE;
+ }
+
public getSignatureType(): string {
return SolidityTypes.Bytes;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/int.ts b/packages/utils/src/abi_encoder/evm_data_types/int.ts
index f8be1f778..02278f666 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/int.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/int.ts
@@ -15,6 +15,7 @@ export class IntDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = true;
private static readonly _MAX_WIDTH: number = 256;
private static readonly _DEFAULT_WIDTH: number = IntDataType._MAX_WIDTH;
+ private static readonly _DEFAULT_VALUE = new BigNumber(0);
private readonly _width: number;
private readonly _minValue: BigNumber;
private readonly _maxValue: BigNumber;
@@ -50,13 +51,20 @@ export class IntDataType extends AbstractBlobDataType {
public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, this._minValue, this._maxValue);
- const numberOfBytesInUint8 = 8;
- if (this._width === numberOfBytesInUint8) {
+ if (this._width === constants.NUMBER_OF_BYTES_IN_INT8) {
return value.toNumber();
}
return value;
}
+ public getDefaultValue(): BigNumber | number {
+ const defaultValue = IntDataType._DEFAULT_VALUE;
+ if (this._width === constants.NUMBER_OF_BYTES_IN_INT8) {
+ return defaultValue.toNumber();
+ }
+ return defaultValue;
+ }
+
public getSignatureType(): string {
return `${SolidityTypes.Int}${this._width}`;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
index 250db7c64..50f68f5ea 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/pointer.ts
@@ -18,4 +18,9 @@ export class PointerDataType extends AbstractPointerDataType {
public getSignature(isDetailed?: boolean): string {
return this._destination.getSignature(isDetailed);
}
+
+ public getDefaultValue(): any {
+ const defaultValue = this._destination.getDefaultValue();
+ return defaultValue;
+ }
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
index cbf1957d7..a965f6796 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/static_bytes.ts
@@ -58,6 +58,13 @@ export class StaticBytesDataType extends AbstractBlobDataType {
return value;
}
+ public getDefaultValue(): string {
+ const valueBufPadded = constants.EMPTY_EVM_WORD_BUFFER;
+ const valueBuf = valueBufPadded.slice(0, this._width);
+ const value = ethUtil.bufferToHex(valueBuf);
+ return value;
+ }
+
private _sanityCheckValue(value: string | Buffer): void {
if (typeof value === 'string') {
if (!_.startsWith(value, '0x')) {
diff --git a/packages/utils/src/abi_encoder/evm_data_types/string.ts b/packages/utils/src/abi_encoder/evm_data_types/string.ts
index 97ac46442..f54d739b4 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/string.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/string.ts
@@ -9,6 +9,7 @@ import { constants } from '../utils/constants';
export class StringDataType extends AbstractBlobDataType {
private static readonly _SIZE_KNOWN_AT_COMPILE_TIME: boolean = false;
+ private static readonly _DEFAULT_VALUE = '';
public static matchType(type: string): boolean {
return type === SolidityTypes.String;
@@ -52,6 +53,10 @@ export class StringDataType extends AbstractBlobDataType {
return value;
}
+ public getDefaultValue(): string {
+ return StringDataType._DEFAULT_VALUE;
+ }
+
public getSignatureType(): string {
return SolidityTypes.String;
}
diff --git a/packages/utils/src/abi_encoder/evm_data_types/uint.ts b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
index a82aa789e..d530606b9 100644
--- a/packages/utils/src/abi_encoder/evm_data_types/uint.ts
+++ b/packages/utils/src/abi_encoder/evm_data_types/uint.ts
@@ -16,6 +16,7 @@ export class UIntDataType extends AbstractBlobDataType {
private static readonly _MAX_WIDTH: number = 256;
private static readonly _DEFAULT_WIDTH: number = UIntDataType._MAX_WIDTH;
private static readonly _MIN_VALUE = new BigNumber(0);
+ private static readonly _DEFAULT_VALUE = new BigNumber(0);
private readonly _width: number;
private readonly _maxValue: BigNumber;
@@ -49,13 +50,20 @@ export class UIntDataType extends AbstractBlobDataType {
public decodeValue(calldata: RawCalldata): BigNumber | number {
const valueBuf = calldata.popWord();
const value = EncoderMath.safeDecodeNumericValue(valueBuf, UIntDataType._MIN_VALUE, this._maxValue);
- const numberOfBytesInUint8 = 8;
- if (this._width === numberOfBytesInUint8) {
+ if (this._width === constants.NUMBER_OF_BYTES_IN_UINT8) {
return value.toNumber();
}
return value;
}
+ public getDefaultValue(): BigNumber | number {
+ const defaultValue = UIntDataType._DEFAULT_VALUE;
+ if (this._width === constants.NUMBER_OF_BYTES_IN_UINT8) {
+ return defaultValue.toNumber();
+ }
+ return defaultValue;
+ }
+
public getSignatureType(): string {
return `${SolidityTypes.Uint}${this._width}`;
}
diff --git a/packages/utils/src/abi_encoder/utils/constants.ts b/packages/utils/src/abi_encoder/utils/constants.ts
index fc586f295..b45b5b8e8 100644
--- a/packages/utils/src/abi_encoder/utils/constants.ts
+++ b/packages/utils/src/abi_encoder/utils/constants.ts
@@ -1,3 +1,5 @@
+import * as ethUtil from 'ethereumjs-util';
+
import { DecodingRules, EncodingRules } from './rules';
export const constants = {
@@ -14,4 +16,8 @@ export const constants = {
DEFAULT_DECODING_RULES: { shouldConvertStructsToObjects: true } as DecodingRules,
DEFAULT_ENCODING_RULES: { shouldOptimize: true, shouldAnnotate: false } as EncodingRules,
/* tslint:enable no-object-literal-type-assertion */
+ EMPTY_EVM_WORD_STRING: '0x0000000000000000000000000000000000000000000000000000000000000000',
+ EMPTY_EVM_WORD_BUFFER: ethUtil.toBuffer('0x0000000000000000000000000000000000000000000000000000000000000000'),
+ NUMBER_OF_BYTES_IN_UINT8: 8,
+ NUMBER_OF_BYTES_IN_INT8: 8,
};
diff --git a/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts
index ac2124011..ad07b3d24 100644
--- a/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts
+++ b/packages/utils/test/abi_encoder/abi_samples/return_value_abis.ts
@@ -97,3 +97,28 @@ export const mixedStaticAndDynamicReturnValues: MethodAbi = {
stateMutability: 'nonpayable',
type: 'function',
};
+
+export const structuredReturnValue: MethodAbi = {
+ constant: false,
+ inputs: [],
+ name: 'fillOrder',
+ outputs: [
+ {
+ components: [
+ {
+ name: 'makerAssetFilledAmount',
+ type: 'uint256',
+ },
+ {
+ name: 'takerAssetFilledAmount',
+ type: 'uint256',
+ },
+ ],
+ name: 'fillResults',
+ type: 'tuple',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+};
diff --git a/packages/utils/test/abi_encoder/evm_data_types_test.ts b/packages/utils/test/abi_encoder/evm_data_types_test.ts
index c146890e0..c09c0d929 100644
--- a/packages/utils/test/abi_encoder/evm_data_types_test.ts
+++ b/packages/utils/test/abi_encoder/evm_data_types_test.ts
@@ -11,6 +11,7 @@ const expect = chai.expect;
describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately.
+ const nullEncodedArgs = '0x';
describe('Array', () => {
it('Fixed size; Static elements', async () => {
// Create DataType object
@@ -207,6 +208,51 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw('Tried to assign NaN value');
});
+ it('Should decode NULL to default values (Fixed size; Static elements)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'int[2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ const args = [new BigNumber(0), new BigNumber(0)];
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default values (Dynamic size; Static elements)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'int[]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ const args: BigNumber[] = [];
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default values (Fixed size; Dynamic elements)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[2]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ const args = ['', ''];
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default values (Dynamic size; Dynamic elements)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'string[]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ const args: string[] = [];
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default values (Dynamic Size; Multidimensional; Dynamic Elements)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'testArray', type: 'bytes[][]' };
+ const dataType = new AbiEncoder.Array(testDataItem);
+ const args: string[][] = [];
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Tuple', () => {
@@ -407,6 +453,46 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw('Could not assign tuple to object: missing key \'field_2\' in object {"field_1":"-5"}');
});
+ it('Should decode NULL to default values (static elements only)', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [{ name: 'field_1', type: 'int32' }, { name: 'field_2', type: 'bool' }],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = { field_1: new BigNumber(0), field_2: false };
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
+ const decodedArgs = dataType.decode(nullEncodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default values (static and dynamic elements)', async () => {
+ // Create DataType object
+ const testDataItem = {
+ name: 'Tuple',
+ type: 'tuple',
+ components: [
+ { name: 'field_1', type: 'int32' },
+ { name: 'field_2', type: 'string' },
+ { name: 'field_3', type: 'bool' },
+ { name: 'field_4', type: 'bytes' },
+ ],
+ };
+ const dataType = new AbiEncoder.Tuple(testDataItem);
+ // Construct args to be encoded
+ const args = {
+ field_1: new BigNumber(0),
+ field_2: '',
+ field_3: false,
+ field_4: '0x',
+ };
+ // Decode Encoded Args and validate result
+ const decodingRules: AbiEncoder.DecodingRules = { shouldConvertStructsToObjects: true };
+ const decodedArgs = dataType.decode(nullEncodedArgs, decodingRules);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Address', () => {
@@ -450,6 +536,15 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw(`Invalid address: '${args}'`);
});
+ it('Should decode NULL to default value of address zero', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Address', type: 'address' };
+ const dataType = new AbiEncoder.Address(testDataItem);
+ const args = '0x0000000000000000000000000000000000000000';
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Bool', () => {
@@ -489,23 +584,14 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
const argsEncodedFromSignature = dataTypeFromSignature.encode(args);
expect(argsEncodedFromSignature).to.be.deep.equal(expectedEncodedArgs);
});
- it('Null should decode as False', async () => {
- // Hack @hysz: there are some cases where `false` is encoded as 0x instead of 0x0.
+ it('Should decode NULL to default value of False', async () => {
// Create DataType object
const testDataItem = { name: 'Boolean', type: 'bool' };
const dataType = new AbiEncoder.Bool(testDataItem);
- // Construct args to be encoded
const args = false;
- // Encode Args and validate result
- const encodedArgs = '0x';
- const expectedEncodedArgs = '0x0000000000000000000000000000000000000000000000000000000000000000';
// Decode Encoded Args and validate result
- const decodedArgs = dataType.decode(encodedArgs);
+ const decodedArgs = dataType.decode(nullEncodedArgs);
expect(decodedArgs).to.be.deep.equal(args);
- // Validate signature
- const dataTypeFromSignature = AbiEncoder.create(dataType.getSignature(true));
- const argsEncodedFromSignature = dataTypeFromSignature.encode(args);
- expect(argsEncodedFromSignature).to.be.deep.equal(expectedEncodedArgs);
});
});
@@ -705,6 +791,15 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw();
});
+ it('Should decode NULL to default value of 0', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Integer (256)', type: 'int' };
+ const dataType = new AbiEncoder.Int(testDataItem);
+ const args = new BigNumber(0);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Unsigned Integer', () => {
@@ -867,6 +962,15 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw();
});
+ it('Should decode NULL to default value of 0', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Unsigned Integer (256)', type: 'uint' };
+ const dataType = new AbiEncoder.UInt(testDataItem);
+ const args = new BigNumber(0);
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Static Bytes', () => {
@@ -1030,6 +1134,33 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.');
});
+ it('Should decode NULL to default value - Single Byte (byte)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Byte', type: 'byte' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ const args = '0x00';
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default value - 4 Bytes (bytes4)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes4', type: 'bytes4' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ const args = '0x00000000';
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
+ it('Should decode NULL to default value - 32 Bytes (bytes32)', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Static Bytes32', type: 'bytes32' };
+ const dataType = new AbiEncoder.StaticBytes(testDataItem);
+ const args = '0x0000000000000000000000000000000000000000000000000000000000000000';
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('Dynamic Bytes', () => {
@@ -1117,6 +1248,15 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
dataType.encode(args, encodingRules);
}).to.throw('Tried to assign 0x010, which is contains a half-byte. Use full bytes only.');
});
+ it('Should decode NULL to empty byte array', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'Dynamic Bytes', type: 'bytes' };
+ const dataType = new AbiEncoder.DynamicBytes(testDataItem);
+ const args = '0x';
+ // Decode Encoded Args and validate result
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
describe('String', () => {
@@ -1182,5 +1322,14 @@ describe('ABI Encoder: EVM Data Type Encoding/Decoding', () => {
const argsEncodedFromSignature = dataTypeFromSignature.encode(args);
expect(argsEncodedFromSignature).to.be.deep.equal(expectedEncodedArgs);
});
+ it('Should decode NULL to empty string', async () => {
+ // Create DataType object
+ const testDataItem = { name: 'String', type: 'string' };
+ const dataType = new AbiEncoder.String(testDataItem);
+ // Decode Encoded Args and validate result
+ const args = '';
+ const decodedArgs = dataType.decode(nullEncodedArgs);
+ expect(decodedArgs).to.be.deep.equal(args);
+ });
});
});
diff --git a/packages/utils/test/abi_encoder/return_values_test.ts b/packages/utils/test/abi_encoder/return_values_test.ts
index 308d11ee5..ccdac510d 100644
--- a/packages/utils/test/abi_encoder/return_values_test.ts
+++ b/packages/utils/test/abi_encoder/return_values_test.ts
@@ -1,7 +1,7 @@
import * as chai from 'chai';
import 'mocha';
-import { AbiEncoder } from '../../src/';
+import { AbiEncoder, BigNumber } from '../../src/';
import { chaiSetup } from '../utils/chai_setup';
import * as ReturnValueAbis from './abi_samples/return_value_abis';
@@ -11,67 +11,194 @@ const expect = chai.expect;
describe('ABI Encoder: Return Value Encoding/Decoding', () => {
const encodingRules: AbiEncoder.EncodingRules = { shouldOptimize: false }; // optimizer is tested separately.
- it('No Return Value', async () => {
- // Decode return value
- const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues);
- const returnValue = '0x';
- const decodedReturnValue = method.decodeReturnValues(returnValue, { shouldConvertStructsToObjects: false });
- const expectedDecodedReturnValue: any[] = [];
- expect(decodedReturnValue).to.be.deep.equal(expectedDecodedReturnValue);
- });
- it('Single static return value', async () => {
- // Generate Return Value
- const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
- const returnValue = ['0x01020304'];
- const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
- const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
- shouldConvertStructsToObjects: false,
- });
- // Validate decoded return value
- expect(decodedReturnValue).to.be.deep.equal(returnValue);
- });
- it('Multiple static return values', async () => {
- // Generate Return Value
- const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
- const returnValue = ['0x01020304', '0x05060708'];
- const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
- const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
- shouldConvertStructsToObjects: false,
- });
- // Validate decoded return value
- expect(decodedReturnValue).to.be.deep.equal(returnValue);
- });
- it('Single dynamic return value', async () => {
- // Generate Return Value
- const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
- const returnValue = ['0x01020304'];
- const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
- const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
- shouldConvertStructsToObjects: false,
- });
- // Validate decoded return value
- expect(decodedReturnValue).to.be.deep.equal(returnValue);
- });
- it('Multiple dynamic return values', async () => {
- // Generate Return Value
- const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues);
- const returnValue = ['0x01020304', '0x05060708'];
- const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
- const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
- shouldConvertStructsToObjects: false,
- });
- // Validate decoded return value
- expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ const nullEncodedReturnValue = '0x';
+ describe('Standard encoding/decoding', () => {
+ it('No Return Value', async () => {
+ // Decode return value
+ const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues);
+ const returnValue = '0x';
+ const decodedReturnValue = method.decodeReturnValues(returnValue, { shouldConvertStructsToObjects: false });
+ const expectedDecodedReturnValue: any[] = [];
+ expect(decodedReturnValue).to.be.deep.equal(expectedDecodedReturnValue);
+ });
+ it('Single static return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Multiple static return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Single dynamic return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Multiple dynamic return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Mixed static/dynamic return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Should decode NULL as default value (single; static)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
+ const returnValue = ['0x00000000'];
+
+ const decodedReturnValue = method.decodeReturnValues(nullEncodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Should decode NULL as default value (multiple; static)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
+ const returnValue = ['0x00000000', '0x00000000'];
+ const decodedReturnValue = method.decodeReturnValues(nullEncodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Should decode NULL as default value (single; dynamic)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
+ const returnValue = ['0x'];
+ const decodedReturnValue = method.decodeReturnValues(nullEncodedReturnValue, {
+ shouldConvertStructsToObjects: false,
+ });
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
});
- it('Mixed static/dynamic return values', async () => {
- // Generate Return Value
- const method = new AbiEncoder.Method(ReturnValueAbis.mixedStaticAndDynamicReturnValues);
- const returnValue = ['0x01020304', '0x05060708'];
- const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
- const decodedReturnValue = method.decodeReturnValues(encodedReturnValue, {
- shouldConvertStructsToObjects: false,
- });
- // Validate decoded return value
- expect(decodedReturnValue).to.be.deep.equal(returnValue);
+
+ describe('Strict encoding/decoding', () => {
+ it('No Return Value', async () => {
+ // Decode return value
+ const method = new AbiEncoder.Method(ReturnValueAbis.noReturnValues);
+ const returnValue = '0x';
+ const decodedReturnValue = method.strictDecodeReturnValue<void>(returnValue);
+ const expectedDecodedReturnValue = undefined;
+ expect(decodedReturnValue).to.be.deep.equal(expectedDecodedReturnValue);
+ });
+ it('Single static return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.strictDecodeReturnValue<string>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue[0]);
+ });
+ it('Multiple static return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.strictDecodeReturnValue<[string, string]>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Single dynamic return value', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
+ const returnValue = ['0x01020304'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.strictDecodeReturnValue<string>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue[0]);
+ });
+ it('Multiple dynamic return values', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleDynamicReturnValues);
+ const returnValue = ['0x01020304', '0x05060708'];
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.strictDecodeReturnValue<[string, string]>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Struct should include fields', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.structuredReturnValue);
+ const returnValue = {
+ fillResults: {
+ makerAssetFilledAmount: new BigNumber(50),
+ takerAssetFilledAmount: new BigNumber(40),
+ },
+ };
+ const encodedReturnValue = method.encodeReturnValues(returnValue, encodingRules);
+ const decodedReturnValue = method.strictDecodeReturnValue<{
+ makerAssetFilledAmount: BigNumber;
+ takerAssetFilledAmount: BigNumber;
+ }>(encodedReturnValue);
+ // Validate decoded return value
+ // Note that only the contents of `fillResults`, not the key itself, is decoded.
+ // This is by design, as only a struct's contents are encoded and returned by a funciton call.
+ expect(decodedReturnValue).to.be.deep.equal(returnValue.fillResults);
+ });
+ it('Should decode NULL as default value (single; static)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleStaticReturnValue);
+ const returnValue = '0x00000000';
+ const encodedReturnValue = '0x';
+ const decodedReturnValue = method.strictDecodeReturnValue<string>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Should decode NULL as default value (multiple; static)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.multipleStaticReturnValues);
+ const returnValue = ['0x00000000', '0x00000000'];
+ const encodedReturnValue = '0x';
+ const decodedReturnValue = method.strictDecodeReturnValue<[string, string]>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
+ it('Should decode NULL as default value (single; dynamic)', async () => {
+ // Generate Return Value
+ const method = new AbiEncoder.Method(ReturnValueAbis.singleDynamicReturnValue);
+ const returnValue = '0x';
+ const encodedReturnValue = '0x';
+ const decodedReturnValue = method.strictDecodeReturnValue<string>(encodedReturnValue);
+ // Validate decoded return value
+ expect(decodedReturnValue).to.be.deep.equal(returnValue);
+ });
});
});