1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.AbstractSetDataType = void 0;
|
4 | const ethUtil = require("ethereumjs-util");
|
5 | const _ = require("lodash");
|
6 | const configured_bignumber_1 = require("../../../configured_bignumber");
|
7 | const set_1 = require("../../calldata/blocks/set");
|
8 | const constants_1 = require("../../utils/constants");
|
9 | const data_type_1 = require("../data_type");
|
10 | const pointer_1 = require("./pointer");
|
11 | class 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 |
|
35 |
|
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 |
|
43 | calldata.startScope();
|
44 | let value;
|
45 | if (rules.shouldConvertStructsToObjects && !this._isArray) {
|
46 |
|
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 |
|
56 | value = [];
|
57 | _.each(members, (member, idx) => {
|
58 | const memberValue = member.generateValue(calldata, rules);
|
59 | value.push(memberValue);
|
60 | });
|
61 | }
|
62 |
|
63 | calldata.endScope();
|
64 | return value;
|
65 | }
|
66 | isStatic() {
|
67 |
|
68 | if (this._isArray && this._arrayLength === undefined) {
|
69 | return false;
|
70 | }
|
71 |
|
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 |
|
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 |
|
106 | const parentName = parentBlock === undefined ? '' : parentBlock.getName();
|
107 | const block = new set_1.SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
|
108 |
|
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 |
|
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 |
|
126 | const parentName = parentBlock === undefined ? '' : parentBlock.getName();
|
127 | const block = new set_1.SetCalldataBlock(this.getDataItem().name, this.getSignature(), parentName);
|
128 |
|
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 |
|
139 | block.setMembers(memberCalldataBlocks);
|
140 | return block;
|
141 | }
|
142 | _computeSignatureOfMembers(isDetailed) {
|
143 |
|
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 |
|
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 |
|
160 | const members = [];
|
161 | const memberIndexByName = {};
|
162 | const memberNames = [];
|
163 | _.each(dataItem.components, (memberItem) => {
|
164 |
|
165 |
|
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 |
|
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 | }
|
208 | exports.AbstractSetDataType = AbstractSetDataType;
|
209 |
|
\ | No newline at end of file |