1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | var _ = require('underscore');
|
24 | var BN = require('bn.js');
|
25 | var utils = require('./utils.js');
|
26 |
|
27 |
|
28 | var _elementaryName = function (name) {
|
29 |
|
30 |
|
31 | if (name.startsWith('int[')) {
|
32 | return 'int256' + name.slice(3);
|
33 | } else if (name === 'int') {
|
34 | return 'int256';
|
35 | } else if (name.startsWith('uint[')) {
|
36 | return 'uint256' + name.slice(4);
|
37 | } else if (name === 'uint') {
|
38 | return 'uint256';
|
39 | } else if (name.startsWith('fixed[')) {
|
40 | return 'fixed128x128' + name.slice(5);
|
41 | } else if (name === 'fixed') {
|
42 | return 'fixed128x128';
|
43 | } else if (name.startsWith('ufixed[')) {
|
44 | return 'ufixed128x128' + name.slice(6);
|
45 | } else if (name === 'ufixed') {
|
46 | return 'ufixed128x128';
|
47 | }
|
48 | return name;
|
49 | };
|
50 |
|
51 |
|
52 | var _parseTypeN = function (type) {
|
53 | var typesize = /^\D+(\d+).*$/.exec(type);
|
54 | return typesize ? parseInt(typesize[1], 10) : null;
|
55 | };
|
56 |
|
57 |
|
58 | var _parseTypeNArray = function (type) {
|
59 | var arraySize = /^\D+\d*\[(\d+)\]$/.exec(type);
|
60 | return arraySize ? parseInt(arraySize[1], 10) : null;
|
61 | };
|
62 |
|
63 | var _parseNumber = function (arg) {
|
64 | var type = typeof arg;
|
65 | if (type === 'string') {
|
66 | if (utils.isHexStrict(arg)) {
|
67 | return new BN(arg.replace(/0x/i,''), 16);
|
68 | } else {
|
69 | return new BN(arg, 10);
|
70 | }
|
71 | } else if (type === 'number') {
|
72 | return new BN(arg);
|
73 | } else if (utils.isBigNumber(arg)) {
|
74 | return new BN(arg.toString(10));
|
75 | } else if (utils.isBN(arg)) {
|
76 | return arg;
|
77 | } else {
|
78 | throw new Error(arg +' is not a number');
|
79 | }
|
80 | };
|
81 |
|
82 | var _solidityPack = function (type, value, arraySize) {
|
83 |
|
84 |
|
85 | var size, num;
|
86 | type = _elementaryName(type);
|
87 |
|
88 |
|
89 | if (type === 'bytes') {
|
90 |
|
91 | if (value.replace(/^0x/i,'').length % 2 !== 0) {
|
92 | throw new Error('Invalid bytes characters '+ value.length);
|
93 | }
|
94 |
|
95 | return value;
|
96 | } else if (type === 'string') {
|
97 | return utils.utf8ToHex(value);
|
98 | } else if (type === 'bool') {
|
99 | return value ? '01' : '00';
|
100 | } else if (type.startsWith('address')) {
|
101 | if(arraySize) {
|
102 | size = 64;
|
103 | } else {
|
104 | size = 40;
|
105 | }
|
106 |
|
107 | if(!utils.isAddress(value)) {
|
108 | throw new Error(value +' is not a valid address, or the checksum is invalid.');
|
109 | }
|
110 |
|
111 | return utils.leftPad(value.toLowerCase(), size);
|
112 | }
|
113 |
|
114 | size = _parseTypeN(type);
|
115 |
|
116 | if (type.startsWith('bytes')) {
|
117 |
|
118 | if(!size) {
|
119 | throw new Error('bytes[] not yet supported in solidity');
|
120 | }
|
121 |
|
122 |
|
123 | if(arraySize) {
|
124 | size = 32;
|
125 | }
|
126 |
|
127 | if (size < 1 || size > 32 || size < value.replace(/^0x/i,'').length / 2 ) {
|
128 | throw new Error('Invalid bytes' + size +' for '+ value);
|
129 | }
|
130 |
|
131 | return utils.rightPad(value, size * 2);
|
132 | } else if (type.startsWith('uint')) {
|
133 |
|
134 | if ((size % 8) || (size < 8) || (size > 256)) {
|
135 | throw new Error('Invalid uint'+size+' size');
|
136 | }
|
137 |
|
138 | num = _parseNumber(value);
|
139 | if (num.bitLength() > size) {
|
140 | throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength());
|
141 | }
|
142 |
|
143 | if(num.lt(new BN(0))) {
|
144 | throw new Error('Supplied uint '+ num.toString() +' is negative');
|
145 | }
|
146 |
|
147 | return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
|
148 | } else if (type.startsWith('int')) {
|
149 |
|
150 | if ((size % 8) || (size < 8) || (size > 256)) {
|
151 | throw new Error('Invalid int'+size+' size');
|
152 | }
|
153 |
|
154 | num = _parseNumber(value);
|
155 | if (num.bitLength() > size) {
|
156 | throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength());
|
157 | }
|
158 |
|
159 | if(num.lt(new BN(0))) {
|
160 | return num.toTwos(size).toString('hex');
|
161 | } else {
|
162 | return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
|
163 | }
|
164 |
|
165 | } else {
|
166 |
|
167 | throw new Error('Unsupported or invalid type: ' + type);
|
168 | }
|
169 | };
|
170 |
|
171 |
|
172 | var _processSoliditySha3Args = function (arg) {
|
173 |
|
174 |
|
175 | if(_.isArray(arg)) {
|
176 | throw new Error('Autodetection of array types is not supported.');
|
177 | }
|
178 |
|
179 | var type, value = '';
|
180 | var hexArg, arraySize;
|
181 |
|
182 |
|
183 | if (_.isObject(arg) && (arg.hasOwnProperty('v') || arg.hasOwnProperty('t') || arg.hasOwnProperty('value') || arg.hasOwnProperty('type'))) {
|
184 | type = arg.hasOwnProperty('t') ? arg.t : arg.type;
|
185 | value = arg.hasOwnProperty('v') ? arg.v : arg.value;
|
186 |
|
187 |
|
188 | } else {
|
189 |
|
190 | type = utils.toHex(arg, true);
|
191 | value = utils.toHex(arg);
|
192 |
|
193 | if (!type.startsWith('int') && !type.startsWith('uint')) {
|
194 | type = 'bytes';
|
195 | }
|
196 | }
|
197 |
|
198 | if ((type.startsWith('int') || type.startsWith('uint')) && typeof value === 'string' && !/^(-)?0x/i.test(value)) {
|
199 | value = new BN(value);
|
200 | }
|
201 |
|
202 |
|
203 | if(_.isArray(value)) {
|
204 | arraySize = _parseTypeNArray(type);
|
205 | if(arraySize && value.length !== arraySize) {
|
206 | throw new Error(type +' is not matching the given array '+ JSON.stringify(value));
|
207 | } else {
|
208 | arraySize = value.length;
|
209 | }
|
210 | }
|
211 |
|
212 |
|
213 | if (_.isArray(value)) {
|
214 | hexArg = value.map(function (val) {
|
215 | return _solidityPack(type, val, arraySize).toString('hex').replace('0x','');
|
216 | });
|
217 | return hexArg.join('');
|
218 | } else {
|
219 | hexArg = _solidityPack(type, value, arraySize);
|
220 | return hexArg.toString('hex').replace('0x','');
|
221 | }
|
222 |
|
223 | };
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | var soliditySha3 = function () {
|
232 |
|
233 |
|
234 | var args = Array.prototype.slice.call(arguments);
|
235 |
|
236 | var hexArgs = _.map(args, _processSoliditySha3Args);
|
237 |
|
238 |
|
239 |
|
240 |
|
241 | return utils.sha3('0x'+ hexArgs.join(''));
|
242 | };
|
243 |
|
244 |
|
245 | module.exports = soliditySha3;
|