@sentio/sdk 1.8.0 → 1.8.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.
- package/lib/base-processor.d.ts +10 -3
- package/lib/base-processor.js +31 -1
- package/lib/base-processor.js.map +1 -1
- package/lib/context.d.ts +15 -11
- package/lib/context.js +21 -13
- package/lib/context.js.map +1 -1
- package/lib/gen/processor/protos/processor.d.ts +56 -21
- package/lib/gen/processor/protos/processor.js +269 -51
- package/lib/gen/processor/protos/processor.js.map +1 -1
- package/lib/meter.d.ts +4 -4
- package/lib/meter.js +11 -0
- package/lib/meter.js.map +1 -1
- package/lib/service.d.ts +4 -2
- package/lib/service.js +39 -4
- package/lib/service.js.map +1 -1
- package/lib/solana-processor.d.ts +2 -2
- package/lib/solana-processor.js +1 -0
- package/lib/solana-processor.js.map +1 -1
- package/lib/test/erc20.test.js +9 -11
- package/lib/test/erc20.test.js.map +1 -1
- package/lib/test/metric-utils.d.ts +3 -3
- package/lib/test/metric-utils.js.map +1 -1
- package/lib/trace.d.ts +35 -0
- package/lib/trace.js +22 -0
- package/lib/trace.js.map +1 -0
- package/package.json +1 -1
- package/src/base-processor.ts +40 -3
- package/src/context.ts +23 -14
- package/src/gen/processor/protos/processor.ts +330 -70
- package/src/meter.ts +19 -8
- package/src/service.ts +59 -15
- package/src/solana-processor.ts +3 -2
- package/src/test/erc20.test.ts +9 -11
- package/src/test/metric-utils.ts +3 -3
- package/src/trace.ts +64 -0
package/src/meter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RecordMetaData } from './gen/processor/protos/processor'
|
|
2
|
-
import {
|
|
2
|
+
import { BaseContext, Context, SolanaContext } from './context'
|
|
3
3
|
import { toMetricValue, Numberish } from './numberish'
|
|
4
4
|
import Long from 'long'
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ export function normalizeName(name: string) {
|
|
|
8
8
|
return name.slice(0, 100).replace(regex, '_')
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
function GetRecordMetaData(ctx:
|
|
11
|
+
function GetRecordMetaData(ctx: BaseContext, name: string, labels: Labels): RecordMetaData {
|
|
12
12
|
name = normalizeName(name)
|
|
13
13
|
|
|
14
14
|
if (ctx instanceof Context) {
|
|
@@ -34,6 +34,17 @@ function GetRecordMetaData(ctx: EthContext | SolanaContext, name: string, labels
|
|
|
34
34
|
labels: labels,
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
if (ctx.trace) {
|
|
38
|
+
return {
|
|
39
|
+
contractAddress: ctx.contract.rawContract.address,
|
|
40
|
+
blockNumber: Long.fromNumber(ctx.trace.blockNumber, true),
|
|
41
|
+
transactionIndex: ctx.trace.transactionPosition, // TODO make sure if this is the right value to set
|
|
42
|
+
logIndex: -1,
|
|
43
|
+
chainId: ctx.chainId.toString(),
|
|
44
|
+
name: name,
|
|
45
|
+
labels: labels,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
37
48
|
} else if (ctx instanceof SolanaContext) {
|
|
38
49
|
return {
|
|
39
50
|
contractAddress: ctx.address,
|
|
@@ -51,10 +62,10 @@ function GetRecordMetaData(ctx: EthContext | SolanaContext, name: string, labels
|
|
|
51
62
|
export type Labels = { [key: string]: string }
|
|
52
63
|
|
|
53
64
|
export class Counter {
|
|
54
|
-
private readonly ctx:
|
|
65
|
+
private readonly ctx: BaseContext
|
|
55
66
|
private readonly name: string
|
|
56
67
|
|
|
57
|
-
constructor(name: string, ctx:
|
|
68
|
+
constructor(name: string, ctx: BaseContext) {
|
|
58
69
|
this.name = name
|
|
59
70
|
this.ctx = ctx
|
|
60
71
|
}
|
|
@@ -79,9 +90,9 @@ export class Counter {
|
|
|
79
90
|
|
|
80
91
|
export class Gauge {
|
|
81
92
|
private readonly name: string
|
|
82
|
-
private readonly ctx:
|
|
93
|
+
private readonly ctx: BaseContext
|
|
83
94
|
|
|
84
|
-
constructor(name: string, ctx:
|
|
95
|
+
constructor(name: string, ctx: BaseContext) {
|
|
85
96
|
this.name = name
|
|
86
97
|
this.ctx = ctx
|
|
87
98
|
}
|
|
@@ -96,13 +107,13 @@ export class Gauge {
|
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
export class Meter {
|
|
99
|
-
private readonly ctx:
|
|
110
|
+
private readonly ctx: BaseContext
|
|
100
111
|
|
|
101
112
|
// TODO is map necessary since we are sending request remotely?
|
|
102
113
|
// counterMap = new Map<string, Counter>()
|
|
103
114
|
// gaugeMap = new Map<string, Gauge>()
|
|
104
115
|
|
|
105
|
-
constructor(ctx:
|
|
116
|
+
constructor(ctx: BaseContext) {
|
|
106
117
|
this.ctx = ctx
|
|
107
118
|
}
|
|
108
119
|
|
package/src/service.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
HandlerType,
|
|
8
8
|
LogFilter,
|
|
9
9
|
LogHandlerConfig,
|
|
10
|
-
|
|
10
|
+
ProcessResult,
|
|
11
11
|
ProcessBlocksRequest,
|
|
12
12
|
ProcessBlocksResponse,
|
|
13
13
|
ProcessConfigRequest,
|
|
@@ -23,20 +23,23 @@ import {
|
|
|
23
23
|
ProcessTransactionsResponse,
|
|
24
24
|
StartRequest,
|
|
25
25
|
TemplateInstance,
|
|
26
|
+
TraceBinding,
|
|
26
27
|
} from './gen/processor/protos/processor'
|
|
27
28
|
|
|
28
29
|
import { Empty } from './gen/google/protobuf/empty'
|
|
29
30
|
import Long from 'long'
|
|
30
31
|
import { TextDecoder } from 'util'
|
|
32
|
+
import { Trace } from './trace'
|
|
31
33
|
|
|
32
34
|
const DEFAULT_MAX_BLOCK = Long.ZERO
|
|
33
35
|
|
|
34
36
|
export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
35
|
-
private eventHandlers: ((event: Log) => Promise<
|
|
36
|
-
private
|
|
37
|
+
private eventHandlers: ((event: Log) => Promise<ProcessResult>)[] = []
|
|
38
|
+
private traceHandlers: ((trace: Trace) => Promise<ProcessResult>)[] = []
|
|
39
|
+
private blockHandlers: ((block: Block) => Promise<ProcessResult>)[] = []
|
|
37
40
|
|
|
38
41
|
// map from chain id to list of processors
|
|
39
|
-
// private blockHandlers = new Map<string, ((block: Block) => Promise<
|
|
42
|
+
// private blockHandlers = new Map<string, ((block: Block) => Promise<ProcessResult>)[]>()
|
|
40
43
|
// private processorsByChainId = new Map<string, BaseProcessor<BaseContract, BoundContractView<BaseContract, any>>>()
|
|
41
44
|
|
|
42
45
|
private started = false
|
|
@@ -88,6 +91,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
88
91
|
},
|
|
89
92
|
blockConfigs: [],
|
|
90
93
|
logConfigs: [],
|
|
94
|
+
traceConfigs: [],
|
|
91
95
|
startBlock: processor.config.startBlock,
|
|
92
96
|
endBlock: DEFAULT_MAX_BLOCK,
|
|
93
97
|
instructionConfig: undefined,
|
|
@@ -104,7 +108,16 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
104
108
|
})
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
// Step 2. Prepare all
|
|
111
|
+
// Step 2. Prepare all trace handlers
|
|
112
|
+
for (const traceHandler of processor.traceHandlers) {
|
|
113
|
+
const handlerId = this.traceHandlers.push(traceHandler.handler) - 1
|
|
114
|
+
contractConfig.traceConfigs.push({
|
|
115
|
+
signature: traceHandler.signature,
|
|
116
|
+
handlerId: handlerId,
|
|
117
|
+
})
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Step 3. Prepare all the event handlers
|
|
108
121
|
for (const eventsHandler of processor.eventHandlers) {
|
|
109
122
|
// associate id with filter
|
|
110
123
|
const handlerId = this.eventHandlers.push(eventsHandler.handler) - 1
|
|
@@ -151,6 +164,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
151
164
|
},
|
|
152
165
|
blockConfigs: [],
|
|
153
166
|
logConfigs: [],
|
|
167
|
+
traceConfigs: [],
|
|
154
168
|
startBlock: solanaProcessor.config.startSlot,
|
|
155
169
|
endBlock: DEFAULT_MAX_BLOCK,
|
|
156
170
|
instructionConfig: {
|
|
@@ -204,12 +218,13 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
204
218
|
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
205
219
|
}
|
|
206
220
|
|
|
207
|
-
const resp:
|
|
221
|
+
const resp: ProcessResult = {
|
|
208
222
|
gauges: [],
|
|
209
223
|
counters: [],
|
|
224
|
+
logs: [],
|
|
210
225
|
}
|
|
211
226
|
|
|
212
|
-
const promises: Promise<
|
|
227
|
+
const promises: Promise<ProcessResult>[] = []
|
|
213
228
|
for (const l of request.logBindings) {
|
|
214
229
|
if (!l.log) {
|
|
215
230
|
throw new ServerError(Status.INVALID_ARGUMENT, "Log can't be null")
|
|
@@ -272,9 +287,10 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
272
287
|
throw new ServerError(Status.UNAVAILABLE, 'Service not started.')
|
|
273
288
|
}
|
|
274
289
|
|
|
275
|
-
const result:
|
|
290
|
+
const result: ProcessResult = {
|
|
276
291
|
gauges: [],
|
|
277
292
|
counters: [],
|
|
293
|
+
logs: [],
|
|
278
294
|
}
|
|
279
295
|
|
|
280
296
|
// Only have instruction handlers for solana processors
|
|
@@ -289,7 +305,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
289
305
|
new Promise((resolve, _) => {
|
|
290
306
|
for (const processor of global.PROCESSOR_STATE.solanaProcessors) {
|
|
291
307
|
if (processor.address === instruction.programAccountId) {
|
|
292
|
-
let res:
|
|
308
|
+
let res: ProcessResult | null
|
|
293
309
|
if (instruction.parsed) {
|
|
294
310
|
res = processor.handleInstruction(JSON.parse(new TextDecoder().decode(instruction.parsed)))
|
|
295
311
|
} else if (instruction.instructionData) {
|
|
@@ -343,7 +359,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
343
359
|
const promises = request.blockBindings.map((binding) => this.processBlock(binding))
|
|
344
360
|
const results = await Promise.all(promises)
|
|
345
361
|
|
|
346
|
-
const res =
|
|
362
|
+
const res = ProcessResult.fromPartial({})
|
|
347
363
|
|
|
348
364
|
for (const r of results) {
|
|
349
365
|
res.counters = res.counters.concat(r.counters)
|
|
@@ -356,7 +372,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
356
372
|
}
|
|
357
373
|
}
|
|
358
374
|
|
|
359
|
-
async processBlock(binding: BlockBinding): Promise<
|
|
375
|
+
async processBlock(binding: BlockBinding): Promise<ProcessResult> {
|
|
360
376
|
if (!binding.block) {
|
|
361
377
|
throw new ServerError(Status.INVALID_ARGUMENT, "Block can't be empty")
|
|
362
378
|
}
|
|
@@ -364,12 +380,13 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
364
380
|
|
|
365
381
|
const block: Block = JSON.parse(jsonString)
|
|
366
382
|
|
|
367
|
-
const resp:
|
|
383
|
+
const resp: ProcessResult = {
|
|
368
384
|
gauges: [],
|
|
369
385
|
counters: [],
|
|
386
|
+
logs: [],
|
|
370
387
|
}
|
|
371
388
|
|
|
372
|
-
const promises: Promise<
|
|
389
|
+
const promises: Promise<ProcessResult>[] = []
|
|
373
390
|
for (const handlerId of binding.handlerIds) {
|
|
374
391
|
const promise = this.blockHandlers[handlerId](block).catch((e) => {
|
|
375
392
|
throw new ServerError(Status.INTERNAL, 'error processing block: ' + block.number + '\n' + e.toString())
|
|
@@ -385,10 +402,37 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
|
385
402
|
}
|
|
386
403
|
|
|
387
404
|
async processTraces(request: ProcessTracesRequest, context: CallContext): Promise<ProcessTracesResponse> {
|
|
405
|
+
if (!this.started) {
|
|
406
|
+
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const promises = request.traceBindings.map((binding) => this.processTrace(binding))
|
|
410
|
+
const results = await Promise.all(promises)
|
|
411
|
+
|
|
412
|
+
const res = ProcessResult.fromPartial({})
|
|
413
|
+
|
|
414
|
+
for (const r of results) {
|
|
415
|
+
res.counters = res.counters.concat(r.counters)
|
|
416
|
+
res.gauges = res.gauges.concat(r.gauges)
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
recordRuntimeInfo(res, HandlerType.TRACE)
|
|
388
420
|
return {
|
|
389
|
-
result:
|
|
421
|
+
result: res,
|
|
390
422
|
}
|
|
391
423
|
}
|
|
424
|
+
|
|
425
|
+
async processTrace(binding: TraceBinding): Promise<ProcessResult> {
|
|
426
|
+
if (!binding.trace) {
|
|
427
|
+
throw new ServerError(Status.INVALID_ARGUMENT, "Trace can't be empty")
|
|
428
|
+
}
|
|
429
|
+
const jsonString = Utf8ArrayToStr(binding.trace.raw)
|
|
430
|
+
const trace: Trace = JSON.parse(jsonString)
|
|
431
|
+
|
|
432
|
+
return this.traceHandlers[binding.handlerId](trace).catch((e) => {
|
|
433
|
+
throw new ServerError(Status.INTERNAL, 'error processing trace: ' + jsonString + '\n' + e.toString())
|
|
434
|
+
})
|
|
435
|
+
}
|
|
392
436
|
}
|
|
393
437
|
|
|
394
438
|
// https://ourcodeworld.com/articles/read/164/how-to-convert-an-uint8array-to-string-in-javascript
|
|
@@ -432,7 +476,7 @@ function Utf8ArrayToStr(array: Uint8Array) {
|
|
|
432
476
|
return out
|
|
433
477
|
}
|
|
434
478
|
|
|
435
|
-
function recordRuntimeInfo(results:
|
|
479
|
+
function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
|
|
436
480
|
results.gauges.forEach((e) => {
|
|
437
481
|
e.runtimeInfo = {
|
|
438
482
|
from: handlerType,
|
package/src/solana-processor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ProcessResult } from './gen/processor/protos/processor'
|
|
2
2
|
import { SolanaContext } from './context'
|
|
3
3
|
import Long from 'long'
|
|
4
4
|
import { Instruction } from '@project-serum/anchor'
|
|
@@ -54,7 +54,7 @@ export class SolanaBaseProcessor {
|
|
|
54
54
|
return this
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
public handleInstruction(ins: string | { type: string; info: any }):
|
|
57
|
+
public handleInstruction(ins: string | { type: string; info: any }): ProcessResult | null {
|
|
58
58
|
const ctx = new SolanaContext(this.address)
|
|
59
59
|
let parsedInstruction: Instruction | null = null
|
|
60
60
|
|
|
@@ -80,6 +80,7 @@ export class SolanaBaseProcessor {
|
|
|
80
80
|
return {
|
|
81
81
|
gauges: ctx.gauges,
|
|
82
82
|
counters: ctx.counters,
|
|
83
|
+
logs: [],
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
package/src/test/erc20.test.ts
CHANGED
|
@@ -28,21 +28,19 @@ describe('Test Basic Examples', () => {
|
|
|
28
28
|
})
|
|
29
29
|
|
|
30
30
|
test('Check block dispatch', async () => {
|
|
31
|
-
const res = await service.testBlock(blockData)
|
|
32
|
-
|
|
33
|
-
expect(
|
|
34
|
-
expect(
|
|
35
|
-
expect(firstGaugeValue(o11yRes, 'g1')).equals(10n)
|
|
31
|
+
const res = (await service.testBlock(blockData)).result
|
|
32
|
+
expect(res?.counters).length(0)
|
|
33
|
+
expect(res?.gauges).length(1)
|
|
34
|
+
expect(firstGaugeValue(res, 'g1')).equals(10n)
|
|
36
35
|
|
|
37
|
-
const gauge =
|
|
36
|
+
const gauge = res?.gauges?.[0]
|
|
38
37
|
expect(gauge?.metadata?.blockNumber?.toString()).equals('14373295')
|
|
39
38
|
expect(gauge?.runtimeInfo?.from).equals(HandlerType.BLOCK)
|
|
40
39
|
|
|
41
|
-
const res2 = await service.testBlock(blockData, 56)
|
|
42
|
-
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
expect(firstGaugeValue(o11yRes2, 'g2')).equals(20n)
|
|
40
|
+
const res2 = (await service.testBlock(blockData, 56)).result
|
|
41
|
+
expect(res2?.counters).length(0)
|
|
42
|
+
expect(res2?.gauges).length(1)
|
|
43
|
+
expect(firstGaugeValue(res2, 'g2')).equals(20n)
|
|
46
44
|
})
|
|
47
45
|
|
|
48
46
|
test('Check log dispatch', async () => {
|
package/src/test/metric-utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DeepPartial } from '../gen/builtin'
|
|
2
|
-
import { BigDecimal, MetricValue,
|
|
2
|
+
import { BigDecimal, MetricValue, ProcessResult } from '@sentio/sdk'
|
|
3
3
|
import { Numberish } from '../numberish'
|
|
4
4
|
import { BigNumber } from 'ethers'
|
|
5
5
|
|
|
@@ -24,7 +24,7 @@ export function MetricValueToNumber(v: DeepPartial<MetricValue> | undefined): Nu
|
|
|
24
24
|
return undefined
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function firstCounterValue(result:
|
|
27
|
+
export function firstCounterValue(result: ProcessResult | undefined, name: string): Numberish | undefined {
|
|
28
28
|
if (!result) {
|
|
29
29
|
return undefined
|
|
30
30
|
}
|
|
@@ -36,7 +36,7 @@ export function firstCounterValue(result: O11yResult | undefined, name: string):
|
|
|
36
36
|
return undefined
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
export function firstGaugeValue(result:
|
|
39
|
+
export function firstGaugeValue(result: ProcessResult | undefined, name: string): Numberish | undefined {
|
|
40
40
|
if (!result) {
|
|
41
41
|
return undefined
|
|
42
42
|
}
|
package/src/trace.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// https://github.com/openethereum/parity-ethereum/blob/55c90d4016505317034e3e98f699af07f5404b63/rpc/src/v1/types/trace.rs#L482
|
|
2
|
+
import { Result } from '@ethersproject/abi'
|
|
3
|
+
|
|
4
|
+
export interface TypedTrace<TArgsArray extends Array<any> = any, TArgsObject = any> extends Trace {
|
|
5
|
+
args: TArgsArray & TArgsObject
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Trace {
|
|
9
|
+
args?: Result
|
|
10
|
+
|
|
11
|
+
action: TraceAction
|
|
12
|
+
blockHash: string
|
|
13
|
+
blockNumber: number
|
|
14
|
+
result: TraceResult
|
|
15
|
+
subtraces: number
|
|
16
|
+
traceAddress: number[]
|
|
17
|
+
transactionHash: string
|
|
18
|
+
transactionPosition: number
|
|
19
|
+
type: string
|
|
20
|
+
error?: string
|
|
21
|
+
}
|
|
22
|
+
// export type CallType = "call" | "callcode" | "delegatecall" | "staticcall"
|
|
23
|
+
|
|
24
|
+
export interface TraceAction {
|
|
25
|
+
from: string
|
|
26
|
+
to: string
|
|
27
|
+
value: number
|
|
28
|
+
gas: number
|
|
29
|
+
input: string
|
|
30
|
+
callType: string
|
|
31
|
+
|
|
32
|
+
init?: string
|
|
33
|
+
address?: string
|
|
34
|
+
balance?: string
|
|
35
|
+
refundAddress?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// TODO are more field missing for FailedCall, FailedCreate
|
|
39
|
+
export interface TraceResult {
|
|
40
|
+
gasUsed: number
|
|
41
|
+
output: string
|
|
42
|
+
address?: string
|
|
43
|
+
code?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// const TRACE: Trace = {
|
|
47
|
+
// action: {
|
|
48
|
+
// from: '0xd771111cbfa2bbdafbf9f0e58b49b3f827da31f5',
|
|
49
|
+
// callType: 'call',
|
|
50
|
+
// gas: 0x12154,
|
|
51
|
+
// input:
|
|
52
|
+
// '0xb1a417f4000000000000000000000000d771111cbfa2bbdafbf9f0e58b49b3f827da31f5000000000000000000000000d771111cbfa2bbdafbf9f0e58b49b3f827da31f500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000131888b5aaf000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000',
|
|
53
|
+
// to: '0x0baba1ad5be3a5c0a66e7ac838a129bf948f1ea4',
|
|
54
|
+
// value: 0x131888b5aaf0000,
|
|
55
|
+
// },
|
|
56
|
+
// blockHash: '0x5451711bc530a7c04128fedbe149eb359c10eccd44a83909d448c5244c7eea26',
|
|
57
|
+
// blockNumber: 15533908,
|
|
58
|
+
// result: { gasUsed: 0x114c1, output: '0x' },
|
|
59
|
+
// subtraces: 1,
|
|
60
|
+
// traceAddress: [],
|
|
61
|
+
// transactionHash: '0x66dce11d6217042ed709a38e507e7762c93b1bde4a0447ae7a243493bbdffc0e',
|
|
62
|
+
// transactionPosition: 73,
|
|
63
|
+
// type: 'call',
|
|
64
|
+
// }
|