Handle NULL input for all data types
This commit is contained in:
@@ -47,7 +47,7 @@ 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 +71,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;
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -79,4 +79,9 @@ export class RawCalldata {
|
||||
public getSelector(): string {
|
||||
return this._selector;
|
||||
}
|
||||
|
||||
public getSizeInBytes(): number {
|
||||
const sizeInBytes = this._value.byteLength;
|
||||
return sizeInBytes;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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_UINT8) {
|
||||
return value.toNumber();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public getDefaultValue(): BigNumber | number {
|
||||
const defaultValue = IntDataType._DEFAULT_VALUE;
|
||||
if (this._width === constants.NUMBER_OF_BYTES_IN_UINT8) {
|
||||
return defaultValue.toNumber();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public getSignatureType(): string {
|
||||
return `${SolidityTypes.Int}${this._width}`;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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')) {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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_INT8) {
|
||||
return value.toNumber();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public getDefaultValue(): BigNumber | number {
|
||||
const defaultValue = UIntDataType._DEFAULT_VALUE;
|
||||
if (this._width === constants.NUMBER_OF_BYTES_IN_INT8) {
|
||||
return defaultValue.toNumber();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public getSignatureType(): string {
|
||||
return `${SolidityTypes.Uint}${this._width}`;
|
||||
}
|
||||
|
@@ -14,4 +14,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: new Buffer('0x0000000000000000000000000000000000000000000000000000000000000000'),
|
||||
NUMBER_OF_BYTES_IN_UINT8: 8,
|
||||
NUMBER_OF_BYTES_IN_INT8: 8,
|
||||
}
|
||||
|
Reference in New Issue
Block a user