diff options
-rw-r--r-- | packages/utils/src/abi_encoder/abstract_data_types/types/set.ts | 77 |
1 files changed, 35 insertions, 42 deletions
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 77fd7b3ea..427122ad6 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 @@ -49,18 +49,19 @@ export abstract class Set extends DataType { public generateValue(calldata: RawCalldata, rules: DecodingRules): any[] | object { let members = this._members; + // Case 1: This is an array of undefined length, which means that `this._members` was not + // populated in the constructor. So, construct the set of members it now. if (this._isArray && this._arrayLength === undefined) { const arrayLengthBuf = calldata.popWord(); const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf); - const hexBase = 16; - const arrayLength = new BigNumber(arrayLengthHex, hexBase); - + const arrayLength = new BigNumber(arrayLengthHex, Constants.HEX_BASE); [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber()); } - + // Create a new scope in the calldata, before descending into the members of this set. calldata.startScope(); let value: any[] | object; if (rules.structsAsObjects && !this._isArray) { + // Construct an object with values for each member of the set. value = {}; _.each(this._memberIndexByName, (idx: number, key: string) => { const member = this._members[idx]; @@ -68,41 +69,33 @@ export abstract class Set extends DataType { (value as { [key: string]: any })[key] = memberValue; }); } else { + // Construct an array with values for each member of the set. value = []; _.each(members, (member: DataType, idx: number) => { const memberValue = member.generateValue(calldata, rules); (value as any[]).push(memberValue); }); } + // Close this scope and return tetheh value. calldata.endScope(); return value; } 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 - - Otherwise if the first element is a Pointer then false - */ - + // An array with an undefined length is never static. if (this._isArray && this._arrayLength === undefined) { return false; } - - // Search for dependent members + // If any member of the set is a pointer then the set is not static. const dependentMember = _.find(this._members, (member: DataType) => { return member instanceof Pointer; }); - const isStatic = dependentMember === undefined; // static if we couldn't find a dependent member + const isStatic = dependentMember === undefined; return isStatic; } protected _generateCalldataBlockFromArray(value: any[], parentBlock?: CalldataBlock): CalldataBlocks.Set { - // Sanity check length + // Sanity check: if the set has a defined length then `value` must have the same length. if (this._arrayLength !== undefined && value.length !== this._arrayLength) { throw new Error( `Expected array of ${JSON.stringify( @@ -110,41 +103,42 @@ export abstract class Set extends DataType { )} elements, but got array of length ${JSON.stringify(value.length)}`, ); } - + // Create a new calldata block for this set. const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), parentName, ); - + // If this set has an undefined length then set its header to be the number of elements. 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(Constants.HEX_BASE)}`), Constants.EVM_WORD_WIDTH_IN_BYTES, ); - methodBlock.setHeader(lenBuf); + block.setHeader(lenBuf); } - + // Create blocks for members of set. const memberCalldataBlocks: CalldataBlock[] = []; _.each(members, (member: DataType, idx: number) => { - const block = member.generateCalldataBlock(value[idx], methodBlock); - memberCalldataBlocks.push(block); + const memberBlock = member.generateCalldataBlock(value[idx], block); + memberCalldataBlocks.push(memberBlock); }); - methodBlock.setMembers(memberCalldataBlocks); - return methodBlock; + block.setMembers(memberCalldataBlocks); + return block; } protected _generateCalldataBlockFromObject(obj: object, parentBlock?: CalldataBlock): CalldataBlocks.Set { + // Create a new calldata block for this set. const parentName = parentBlock === undefined ? '' : parentBlock.getName(); - const methodBlock: CalldataBlocks.Set = new CalldataBlocks.Set( + const block: CalldataBlocks.Set = new CalldataBlocks.Set( this.getDataItem().name, this.getSignature(), parentName, ); + // Create blocks for members of set. const memberCalldataBlocks: CalldataBlock[] = []; const childMap = _.cloneDeep(this._memberIndexByName); _.forOwn(obj, (value: any, key: string) => { @@ -153,17 +147,17 @@ export abstract class Set extends DataType { `Could not assign tuple to object: unrecognized key '${key}' in object ${this.getDataItem().name}`, ); } - const block = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, methodBlock); - memberCalldataBlocks.push(block); + const memberBlock = this._members[this._memberIndexByName[key]].generateCalldataBlock(value, block); + memberCalldataBlocks.push(memberBlock); delete childMap[key]; }); - + // Sanity check that all members have been included. if (Object.keys(childMap).length !== 0) { throw new Error(`Could not assign tuple to object: missing keys ${Object.keys(childMap)}`); } - - methodBlock.setMembers(memberCalldataBlocks); - return methodBlock; + // Associate member blocks with Set block. + block.setMembers(memberCalldataBlocks); + return block; } protected _computeSignatureOfMembers(): string { @@ -184,7 +178,7 @@ export abstract class Set extends DataType { if (dataItem.components === undefined) { throw new Error(`Expected components`); } - + // Create one member for each component of `dataItem` const members: DataType[] = []; const memberIndexByName: MemberIndexByName = {}; _.each(dataItem.components, (memberItem: DataItem) => { @@ -200,28 +194,27 @@ export abstract class Set extends DataType { memberIndexByName[memberItem.name] = members.length; members.push(child); }); - return [members, memberIndexByName]; } private _createMembersWithLength(dataItem: DataItem, length: number): [DataType[], MemberIndexByName] { + // Create `length` members, deriving the type from `dataItem` const members: DataType[] = []; const memberIndexByName: MemberIndexByName = {}; const range = _.range(length); _.each(range, (idx: number) => { - const childDataItem: DataItem = { + const memberDataItem: DataItem = { type: this._arrayElementType ? this._arrayElementType : '', name: `${dataItem.name}[${idx.toString(Constants.DEC_BASE)}]`, }; const components = dataItem.components; if (components !== undefined) { - childDataItem.components = components; + memberDataItem.components = components; } - const child = this.getFactory().create(childDataItem, this); + const memberType = this.getFactory().create(memberDataItem, this); memberIndexByName[idx.toString(Constants.DEC_BASE)] = members.length; - members.push(child); + members.push(memberType); }); - return [members, memberIndexByName]; } } |