UNPKG

9.71 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.AbstractSetDataType = void 0;
4const ethUtil = require("ethereumjs-util");
5const _ = require("lodash");
6const configured_bignumber_1 = require("../../../configured_bignumber");
7const set_1 = require("../../calldata/blocks/set");
8const constants_1 = require("../../utils/constants");
9const data_type_1 = require("../data_type");
10const pointer_1 = require("./pointer");
11class AbstractSetDataType extends data_type_1.DataType {
12 constructor(dataItem, factory, isArray = false, arrayLength, arrayElementType) {
13 super(dataItem, factory);
14 this._memberIndexByName = {};
15 this._members = [];
16 this._isArray = isArray;
17 this._arrayLength = arrayLength;
18 this._arrayElementType = arrayElementType;
19 if (isArray && arrayLength !== undefined) {
20 [this._members, this._memberIndexByName] = this._createMembersWithLength(dataItem, arrayLength);
21 }
22 else if (!isArray) {
23 [this._members, this._memberIndexByName] = this._createMembersWithKeys(dataItem);
24 }
25 }
26 generateCalldataBlock(value, parentBlock) {
27 const block = Array.isArray(value)
28 ? this._generateCalldataBlockFromArray(value, parentBlock)
29 : this._generateCalldataBlockFromObject(value, parentBlock);
30 return block;
31 }
32 generateValue(calldata, rules) {
33 let members = this._members;
34 // Case 1: This is an array of undefined length, which means that `this._members` was not
35 // populated in the constructor. So we must construct the set of members now.
36 if (this._isArray && this._arrayLength === undefined) {
37 const arrayLengthBuf = calldata.popWord();
38 const arrayLengthHex = ethUtil.bufferToHex(arrayLengthBuf);
39 const arrayLength = new configured_bignumber_1.BigNumber(arrayLengthHex, constants_1.constants.HEX_BASE);
40 [members] = this._createMembersWithLength(this.getDataItem(), arrayLength.toNumber());
41 }
42 // Create a new scope in the calldata, before descending into the members of this set.
43 calldata.startScope();
44 let value;
45 if (rules.shouldConvertStructsToObjects && !this._isArray) {
46 // Construct an object with values for each member of the set.
47 value = {};
48 _.each(this._memberIndexByName, (idx, key) => {
49 const member = this._members[idx];
50 const memberValue = member.generateValue(calldata, rules);
51 value[key] = memberValue;
52 });
53 }
54 else {
55 // Construct an array with values for each member of the set.
56 value = [];
57 _.each(members, (member, idx) => {
58 const memberValue = member.generateValue(calldata, rules);
59 value.push(memberValue);
60 });
61 }
62 // Close this scope and return tetheh value.
63 calldata.endScope();
64 return value;
65 }
66 isStatic() {
67 // An array with an undefined length is never static.
68 if (this._isArray && this._arrayLength === undefined) {
69 return false;
70 }
71 // If any member of the set is a pointer then the set is not static.
72 const dependentMember = _.find(this._members, (member) => {
73 return member instanceof pointer_1.AbstractPointerDataType;
74 });
75 const isStatic = dependentMember === undefined;
76 return isStatic;
77 }
78 getDefaultValue(rules) {
79 let defaultValue;
80 if (this._isArray && this._arrayLength === undefined) {
81 defaultValue = [];
82 }
83 else if (rules !== undefined && rules.shouldConvertStructsToObjects && !this._isArray) {
84 defaultValue = {};
85 _.each(this._memberIndexByName, (idx, key) => {
86 const member = this._members[idx];
87 const memberValue = member.getDefaultValue();
88 defaultValue[key] = memberValue;
89 });
90 }
91 else {
92 defaultValue = [];
93 _.each(this._members, (member, idx) => {
94 const memberValue = member.getDefaultValue();
95 defaultValue.push(memberValue);
96 });
97 }
98 return defaultValue;
99 }
100 _generateCalldataBlockFromArray(value, parentBlock) {
101 // Sanity check: if the set has a defined length then `value` must have the same length.
102 if (this._arrayLength !== undefined && value.length !== this._arrayLength) {
103 throw new Error(`Expected array of ${JSON.stringify(this._arrayLength)} elements, but got array of length ${JSON.stringify(value.length)}`);
104 }
105 // Create a new calldata block for this set.
106 const parentName = parentBlock === undefined ? '' : parentBlock.getName();
107 const block = new set_1.SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
108 // If this set has an undefined length then set its header to be the number of elements.
109 let members = this._members;
110 if (this._isArray && this._arrayLength === undefined) {
111 [members] = this._createMembersWithLength(this.getDataItem(), value.length);
112 const lenBuf = ethUtil.setLengthLeft(ethUtil.toBuffer(`0x${value.length.toString(constants_1.constants.HEX_BASE)}`), constants_1.constants.EVM_WORD_WIDTH_IN_BYTES);
113 block.setHeader(lenBuf);
114 }
115 // Create blocks for members of set.
116 const memberCalldataBlocks = [];
117 _.each(members, (member, idx) => {
118 const memberBlock = member.generateCalldataBlock(value[idx], block);
119 memberCalldataBlocks.push(memberBlock);
120 });
121 block.setMembers(memberCalldataBlocks);
122 return block;
123 }
124 _generateCalldataBlockFromObject(obj, parentBlock) {
125 // Create a new calldata block for this set.
126 const parentName = parentBlock === undefined ? '' : parentBlock.getName();
127 const block = new set_1.SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
128 // Create blocks for members of set.
129 const memberCalldataBlocks = [];
130 _.forEach(this._memberIndexByName, (memberIndex, memberName) => {
131 if (!(memberName in obj)) {
132 throw new Error(`Could not assign tuple to object: missing key '${memberName}' in object ${JSON.stringify(obj)}`);
133 }
134 const memberValue = obj[memberName];
135 const memberBlock = this._members[memberIndex].generateCalldataBlock(memberValue, block);
136 memberCalldataBlocks.push(memberBlock);
137 });
138 // Associate member blocks with Set block.
139 block.setMembers(memberCalldataBlocks);
140 return block;
141 }
142 _computeSignatureOfMembers(isDetailed) {
143 // Compute signature of members
144 let signature = `(`;
145 _.each(this._members, (member, i) => {
146 signature += member.getSignature(isDetailed);
147 if (i < this._members.length - 1) {
148 signature += ',';
149 }
150 });
151 signature += ')';
152 return signature;
153 }
154 _createMembersWithKeys(dataItem) {
155 // Sanity check
156 if (dataItem.components === undefined) {
157 throw new Error(`Tried to create a set using key/value pairs, but no components were defined by the input DataItem '${dataItem.name}'.`);
158 }
159 // Create one member for each component of `dataItem`
160 const members = [];
161 const memberIndexByName = {};
162 const memberNames = [];
163 _.each(dataItem.components, (memberItem) => {
164 // If a component with `name` already exists then
165 // rename to `name_nameIdx` to avoid naming conflicts.
166 let memberName = memberItem.name;
167 let nameIdx = 0;
168 while (_.includes(memberNames, memberName) || _.isEmpty(memberName)) {
169 nameIdx++;
170 memberName = `${memberItem.name}_${nameIdx}`;
171 }
172 memberNames.push(memberName);
173 const childDataItem = {
174 type: memberItem.type,
175 name: `${dataItem.name}.${memberName}`,
176 };
177 const components = memberItem.components;
178 if (components !== undefined) {
179 childDataItem.components = components;
180 }
181 const child = this.getFactory().create(childDataItem, this);
182 memberIndexByName[memberName] = members.length;
183 members.push(child);
184 });
185 return [members, memberIndexByName];
186 }
187 _createMembersWithLength(dataItem, length) {
188 // Create `length` members, deriving the type from `dataItem`
189 const members = [];
190 const memberIndexByName = {};
191 const range = _.range(length);
192 _.each(range, (idx) => {
193 const memberDataItem = {
194 type: this._arrayElementType === undefined ? '' : this._arrayElementType,
195 name: `${dataItem.name}[${idx.toString(constants_1.constants.DEC_BASE)}]`,
196 };
197 const components = dataItem.components;
198 if (components !== undefined) {
199 memberDataItem.components = components;
200 }
201 const memberType = this.getFactory().create(memberDataItem, this);
202 memberIndexByName[idx.toString(constants_1.constants.DEC_BASE)] = members.length;
203 members.push(memberType);
204 });
205 return [members, memberIndexByName];
206 }
207}
208exports.AbstractSetDataType = AbstractSetDataType;
209//# sourceMappingURL=set.js.map
\No newline at end of file