diff options
Diffstat (limited to 'packages/utils/src/abi_encoder')
-rw-r--r-- | packages/utils/src/abi_encoder/calldata.ts | 6 | ||||
-rw-r--r-- | packages/utils/src/abi_encoder/constants.ts | 1 | ||||
-rw-r--r-- | packages/utils/src/abi_encoder/data_type.ts | 312 | ||||
-rw-r--r-- | packages/utils/src/abi_encoder/evm_data_types.ts | 14 |
4 files changed, 171 insertions, 162 deletions
diff --git a/packages/utils/src/abi_encoder/calldata.ts b/packages/utils/src/abi_encoder/calldata.ts index 0c45d6198..994b0fb81 100644 --- a/packages/utils/src/abi_encoder/calldata.ts +++ b/packages/utils/src/abi_encoder/calldata.ts @@ -1,5 +1,7 @@ -import ethUtil = require('ethereumjs-util'); -const _ = require('lodash'); + +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + import * as Constants from './constants'; export interface DecodingRules { diff --git a/packages/utils/src/abi_encoder/constants.ts b/packages/utils/src/abi_encoder/constants.ts index bc5b985e0..52029695f 100644 --- a/packages/utils/src/abi_encoder/constants.ts +++ b/packages/utils/src/abi_encoder/constants.ts @@ -1,5 +1,6 @@ export const EVM_WORD_WIDTH_IN_BYTES = 32; export const HEX_BASE = 16; +export const DEC_BASE = 10; export const BIN_BASE = 2; export const HEX_SELECTOR_LENGTH_IN_CHARS = 10; export const HEX_SELECTOR_LENGTH_IN_BYTES = 4; diff --git a/packages/utils/src/abi_encoder/data_type.ts b/packages/utils/src/abi_encoder/data_type.ts index 4370cb253..ce7b11ef6 100644 --- a/packages/utils/src/abi_encoder/data_type.ts +++ b/packages/utils/src/abi_encoder/data_type.ts @@ -1,16 +1,21 @@ +import { DataItem } from 'ethereum-types'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; + +import { BigNumber } from '../configured_bignumber'; + +import * as Constants from './constants'; + import { - RawCalldata, Calldata, CalldataBlock, - PayloadCalldataBlock, + DecodingRules, DependentCalldataBlock, + EncodingRules, MemberCalldataBlock, + PayloadCalldataBlock, + RawCalldata, } from './calldata'; -import { MethodAbi, DataItem } from 'ethereum-types'; -import { DecodingRules, EncodingRules } from './calldata'; -import { BigNumber } from '../configured_bignumber'; -import ethUtil = require('ethereumjs-util'); -var _ = require('lodash'); export interface DataTypeFactory { create: (dataItem: DataItem, parentDataType?: DataType) => DataType; @@ -18,29 +23,30 @@ export interface DataTypeFactory { } export abstract class DataType { - private static DEFAULT_ENCODING_RULES = { optimize: false, annotate: false } as EncodingRules; - private static DEFAULT_DECODING_RULES = { structsAsObjects: false } as DecodingRules; - - private dataItem: DataItem; - private factory: DataTypeFactory; + private static readonly _DEFAULT_ENCODING_RULES: EncodingRules = { optimize: false, annotate: false }; + private static readonly _DEFAULT_DECODING_RULES: DecodingRules = { structsAsObjects: false }; + private readonly _dataItem: DataItem; + private readonly _factory: DataTypeFactory; constructor(dataItem: DataItem, factory: DataTypeFactory) { - this.dataItem = dataItem; - this.factory = factory; + this._dataItem = dataItem; + this._factory = factory; } public getDataItem(): DataItem { - return this.dataItem; + return this._dataItem; } public getFactory(): DataTypeFactory { - return this.factory; + return this._factory; } public encode(value: any, rules?: EncodingRules, selector?: string): string { - const rules_ = rules ? rules : DataType.DEFAULT_ENCODING_RULES; + const rules_ = rules ? rules : DataType._DEFAULT_ENCODING_RULES; const calldata = new Calldata(rules_); - if (selector) calldata.setSelector(selector); + if (selector) { + calldata.setSelector(selector); + } const block = this.generateCalldataBlock(value); calldata.setRoot(block); // @TODO CHANGE const calldataHex = calldata.toHexString(); @@ -49,7 +55,7 @@ export abstract class DataType { public decode(calldata: string, rules?: DecodingRules, hasSelector: boolean = false): any { const rawCalldata = new RawCalldata(calldata, hasSelector); - const rules_ = rules ? rules : DataType.DEFAULT_DECODING_RULES; + const rules_ = rules ? rules : DataType._DEFAULT_DECODING_RULES; const value = this.generateValue(rawCalldata, rules_); return value; } @@ -61,11 +67,11 @@ export abstract class DataType { } export abstract class PayloadDataType extends DataType { - protected hasConstantSize: boolean; + protected _hasConstantSize: boolean; public constructor(dataItem: DataItem, factory: DataTypeFactory, hasConstantSize: boolean) { super(dataItem, factory); - this.hasConstantSize = hasConstantSize; + this._hasConstantSize = hasConstantSize; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): PayloadCalldataBlock { @@ -73,7 +79,6 @@ export abstract class PayloadDataType extends DataType { const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const relocatable = false; const block = new PayloadCalldataBlock(name, signature, parentName, encodedValue); return block; } @@ -84,8 +89,7 @@ export abstract class PayloadDataType extends DataType { } public isStatic(): boolean { - // If a payload has a constant size then it's static - return this.hasConstantSize; + return this._hasConstantSize; } public abstract encodeValue(value: any): Buffer; @@ -93,20 +97,22 @@ export abstract class PayloadDataType extends DataType { } export abstract class DependentDataType extends DataType { - protected dependency: DataType; - protected parent: DataType; + protected _dependency: DataType; + protected _parent: DataType; + private readonly _isStatic: boolean; public constructor(dataItem: DataItem, factory: DataTypeFactory, dependency: DataType, parent: DataType) { super(dataItem, factory); - this.dependency = dependency; - this.parent = parent; + this._dependency = dependency; + this._parent = parent; + this._isStatic = true; } public generateCalldataBlock(value: any, parentBlock?: CalldataBlock): DependentCalldataBlock { if (parentBlock === undefined) { throw new Error(`DependentDataType requires a parent block to generate its block`); } - const dependencyBlock = this.dependency.generateCalldataBlock(value, parentBlock); + const dependencyBlock = this._dependency.generateCalldataBlock(value, parentBlock); const name = this.getDataItem().name; const signature = this.getSignature(); const parentName = parentBlock === undefined ? '' : parentBlock.getName(); @@ -117,16 +123,16 @@ export abstract class DependentDataType extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any { const destinationOffsetBuf = calldata.popWord(); const currentOffset = calldata.getOffset(); - const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), 16); + const destinationOffsetRelative = parseInt(ethUtil.bufferToHex(destinationOffsetBuf), Constants.HEX_BASE); const destinationOffsetAbsolute = calldata.toAbsoluteOffset(destinationOffsetRelative); calldata.setOffset(destinationOffsetAbsolute); - const value = this.dependency.generateValue(calldata, rules); + const value = this._dependency.generateValue(calldata, rules); calldata.setOffset(currentOffset); return value; } public isStatic(): boolean { - return true; + return this._isStatic; } } @@ -135,11 +141,11 @@ export interface MemberMap { } export abstract class MemberDataType extends DataType { - private memberMap: MemberMap; - private members: DataType[]; - private isArray: boolean; - protected arrayLength: number | undefined; - protected arrayElementType: string | undefined; + protected readonly _arrayLength: number | undefined; + protected readonly _arrayElementType: string | undefined; + private readonly _memberMap: MemberMap; + private readonly _members: DataType[]; + private readonly _isArray: boolean; public constructor( dataItem: DataItem, @@ -149,70 +155,86 @@ export abstract class MemberDataType extends DataType { arrayElementType?: string, ) { super(dataItem, factory); - this.memberMap = {}; - this.members = []; - this.isArray = isArray; - this.arrayLength = arrayLength; - this.arrayElementType = arrayElementType; + this._memberMap = {}; + this._members = []; + this._isArray = isArray; + this._arrayLength = arrayLength; + this._arrayElementType = arrayElementType; if (isArray && arrayLength !== undefined) { - [this.members, this.memberMap] = this.createMembersWithLength(dataItem, arrayLength); + [this._members, this._memberMap] = this._createMembersWithLength(dataItem, arrayLength); } else if (!isArray) { - [this.members, this.memberMap] = this.createMembersWithKeys(dataItem); + [this._members, this._memberMap] = this._createMembersWithKeys(dataItem); } } - private createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { - // Sanity check - if (dataItem.components === undefined) { - throw new Error(`Expected components`); - } + public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { + const block = + value instanceof Array + ? this._generateCalldataBlockFromArray(value, parentBlock) + : this._generateCalldataBlockFromObject(value, parentBlock); + return block; + } - let members: DataType[] = []; - let memberMap: MemberMap = {}; - _.each(dataItem.components, (memberItem: DataItem) => { - const childDataItem = { - type: memberItem.type, - name: `${dataItem.name}.${memberItem.name}`, - } as DataItem; - const components = memberItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[memberItem.name] = members.length; - members.push(child); - }); + public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + const arrayLengthBuf = calldata.popWord(); + const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); + const hexBase = 16; + const arrayLength = new BigNumber(arrayLengthHex, hexBase); - return [members, memberMap]; + [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); + } + + calldata.startScope(); + let value: any[] | object; + if (rules.structsAsObjects && !this._isArray) { + value = {}; + _.each(this._memberMap, (idx: number, key: string) => { + const member = this._members[idx]; + const memberValue = member.generateValue(calldata, rules); + (value as { [key: string]: any })[key] = memberValue; + }); + } else { + value = []; + _.each(members, (member: DataType, idx: number) => { + const memberValue = member.generateValue(calldata, rules); + (value as any[]).push(memberValue); + }); + } + calldata.endScope(); + return value; } - private createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { - let members: DataType[] = []; - let memberMap: MemberMap = {}; - const range = _.range(length); - _.each(range, (idx: number) => { - const childDataItem = { - type: this.arrayElementType, - name: `${dataItem.name}[${idx.toString(10)}]`, - } as DataItem; - const components = dataItem.components; - if (components !== undefined) { - childDataItem.components = components; - } - const child = this.getFactory().create(childDataItem, this); - memberMap[idx.toString(10)] = members.length; - members.push(child); - }); + public isStatic(): boolean { + /* For Tuple: + const isStaticTuple = this.children.length === 0; + return isStaticTuple; // @TODO: True in every case or only when dynamic data? - return [members, memberMap]; + For Array: + if isLengthDefined = false then this is false + + Otherwise if the first element is a Pointer then false + */ + + if (this._isArray && this._arrayLength === undefined) { + return false; + } + + // Search for dependent members + const dependentMember = _.find(this._members, (member: DataType) => { + return member instanceof DependentDataType; + }); + const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + return isStatic; } - protected generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { + protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): MemberCalldataBlock { // Sanity check length - if (this.arrayLength !== undefined && value.length !== this.arrayLength) { + if (this._arrayLength !== undefined && value.length !== this._arrayLength) { throw new Error( `Expected array of ${JSON.stringify( - this.arrayLength, + this._arrayLength, )} elements, but got array of length ${JSON.stringify(value.length)}`, ); } @@ -224,11 +246,11 @@ export abstract class MemberDataType extends DataType { parentName, ); - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - [members] = this.createMembersWithLength(this.getDataItem(), value.length); + let members = this._members; + if (this._isArray && this._arrayLength === undefined) { + [members] = this._createMembersWithLength(this.getDataItem(), value.length); - const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(16)}`), 32); + const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES); methodBlock.setHeader(lenBuf); } @@ -241,7 +263,7 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - protected generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { + protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): MemberCalldataBlock { const parentName = parentBlock === undefined ? '' : parentBlock.getName(); const methodBlock: MemberCalldataBlock = new MemberCalldataBlock( this.getDataItem().name, @@ -249,14 +271,14 @@ export abstract class MemberDataType extends DataType { parentName, ); const memberBlocks: CalldataBlock[] = []; - let childMap = _.cloneDeep(this.memberMap); + const childMap = _.cloneDeep(this._memberMap); _.forOwn(obj, (value: any, key: string) => { - if (key in childMap === false) { + if (!(key in childMap)) { throw new Error( `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, ); } - const block = this.members[this.memberMap[key]].generateCalldataBlock(value, methodBlock); + const block = this._members[this._memberMap[key]].generateCalldataBlock(value, methodBlock); memberBlocks.push(block); delete childMap[key]; }); @@ -269,51 +291,12 @@ export abstract class MemberDataType extends DataType { return methodBlock; } - public generateCalldataBlock(value: any[] | object, parentBlock?: CalldataBlock): MemberCalldataBlock { - const block = - value instanceof Array - ? this.generateCalldataBlockFromArray(value, parentBlock) - : this.generateCalldataBlockFromObject(value, parentBlock); - return block; - } - - public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { - let members = this.members; - if (this.isArray && this.arrayLength === undefined) { - const arrayLengthBuf = calldata.popWord(); - const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - - [members] = this.createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); - } - - calldata.startScope(); - let value: any[] | object; - if (rules.structsAsObjects && !this.isArray) { - value = {}; - _.each(this.memberMap, (idx: number, key: string) => { - const member = this.members[idx]; - let memberValue = member.generateValue(calldata, rules); - (value as { [key: string]: any })[key] = memberValue; - }); - } else { - value = []; - _.each(members, (member: DataType, idx: number) => { - let memberValue = member.generateValue(calldata, rules); - (value as any[]).push(memberValue); - }); - } - calldata.endScope(); - return value; - } - - protected computeSignatureOfMembers(): string { + protected _computeSignatureOfMembers(): string { // Compute signature of members let signature = `(`; - _.each(this.members, (member: DataType, i: number) => { + _.each(this._members, (member: DataType, i: number) => { signature += member.getSignature(); - if (i < this.members.length - 1) { + if (i < this._members.length - 1) { signature += ','; } }); @@ -321,26 +304,49 @@ export abstract class MemberDataType extends DataType { return signature; } - public isStatic(): boolean { - /* For Tuple: - const isStaticTuple = this.children.length === 0; - return isStaticTuple; // @TODO: True in every case or only when dynamic data? - - For Array: - if isLengthDefined = false then this is false + private _createMembersWithKeys(dataItem: DataItem): [DataType[], MemberMap] { + // Sanity check + if (dataItem.components === undefined) { + throw new Error(`Expected components`); + } - Otherwise if the first element is a Pointer then false - */ + const members: DataType[] = []; + const memberMap: MemberMap = {}; + _.each(dataItem.components, (memberItem: DataItem) => { + const childDataItem: DataItem = { + type: memberItem.type, + name: `${dataItem.name}.${memberItem.name}`, + }; + const components = memberItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[memberItem.name] = members.length; + members.push(child); + }); - if (this.isArray && this.arrayLength === undefined) { - return false; - } + return [members, memberMap]; + } - // Search for dependent members - const dependentMember = _.find(this.members, (member: DataType) => { - return member instanceof DependentDataType; + private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberMap] { + const members: DataType[] = []; + const memberMap: MemberMap = {}; + const range = _.range(length); + _.each(range, (idx: number) => { + const childDataItem: DataItem = { + type: this._arrayElementType ? this._arrayElementType : '', + name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, + }; + const components = dataItem.components; + if (components !== undefined) { + childDataItem.components = components; + } + const child = this.getFactory().create(childDataItem, this); + memberMap[idx.toString(Constants.DEC_BASE)] = members.length; + members.push(child); }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member - return isStatic; + + return [members, memberMap]; } } diff --git a/packages/utils/src/abi_encoder/evm_data_types.ts b/packages/utils/src/abi_encoder/evm_data_types.ts index 76837361e..1f5dff56f 100644 --- a/packages/utils/src/abi_encoder/evm_data_types.ts +++ b/packages/utils/src/abi_encoder/evm_data_types.ts @@ -276,7 +276,7 @@ export class Byte extends PayloadDataType { if (valueBuf.byteLength > this.width) { throw new Error( `Tried to assign ${value} (${ - valueBuf.byteLength + valueBuf.byteLength } bytes), which exceeds max bytes that can be stored in a ${this.getSignature()}`, ); } else if (value.length % 2 !== 0) { @@ -396,7 +396,7 @@ export class Pointer extends DependentDataType { } public getSignature(): string { - return this.dependency.getSignature(); + return this._dependency.getSignature(); } } @@ -408,7 +408,7 @@ export class Tuple extends MemberDataType { if (!Tuple.matchGrammar(dataItem.type)) { throw new Error(`Tried to instantiate Tuple with bad input: ${dataItem}`); } - this.tupleSignature = this.computeSignatureOfMembers(); + this.tupleSignature = this._computeSignatureOfMembers(); } public getSignature(): string { @@ -455,10 +455,10 @@ export class SolArray extends MemberDataType { } const elementDataType = this.getFactory().mapDataItemToDataType(dataItem); const type = elementDataType.getSignature(); - if (this.arrayLength === undefined) { + if (this._arrayLength === undefined) { return `${type}[]`; } else { - return `${type}[${this.arrayLength}]`; + return `${type}[${this._arrayLength}]`; } } @@ -493,7 +493,7 @@ export class Method extends MemberDataType { } private computeSignature(): string { - const memberSignature = this.computeSignatureOfMembers(); + const memberSignature = this._computeSignatureOfMembers(); const methodSignature = `${this.getDataItem().name}${memberSignature}`; return methodSignature; } @@ -548,7 +548,7 @@ export class Method extends MemberDataType { export class EvmDataTypeFactory implements DataTypeFactory { private static instance: DataTypeFactory; - private constructor() {} + private constructor() { } public static getInstance(): DataTypeFactory { if (!EvmDataTypeFactory.instance) { |