@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/src/meter.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RecordMetaData } from './gen/processor/protos/processor'
2
- import { Context, EthContext, SolanaContext } from './context'
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: EthContext | SolanaContext, name: string, labels: Labels): RecordMetaData {
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: EthContext | SolanaContext
65
+ private readonly ctx: BaseContext
55
66
  private readonly name: string
56
67
 
57
- constructor(name: string, ctx: EthContext | SolanaContext) {
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: EthContext | SolanaContext
93
+ private readonly ctx: BaseContext
83
94
 
84
- constructor(name: string, ctx: EthContext | SolanaContext) {
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: EthContext | SolanaContext
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: EthContext | SolanaContext) {
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
- O11yResult,
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<O11yResult>)[] = []
36
- private blockHandlers: ((block: Block) => Promise<O11yResult>)[] = []
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<O11yResult>)[]>()
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 the event handlers
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: O11yResult = {
221
+ const resp: ProcessResult = {
208
222
  gauges: [],
209
223
  counters: [],
224
+ logs: [],
210
225
  }
211
226
 
212
- const promises: Promise<O11yResult>[] = []
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: O11yResult = {
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: O11yResult | null
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 = O11yResult.fromPartial({})
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<O11yResult> {
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: O11yResult = {
383
+ const resp: ProcessResult = {
368
384
  gauges: [],
369
385
  counters: [],
386
+ logs: [],
370
387
  }
371
388
 
372
- const promises: Promise<O11yResult>[] = []
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: undefined,
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: O11yResult, handlerType: HandlerType) {
479
+ function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
436
480
  results.gauges.forEach((e) => {
437
481
  e.runtimeInfo = {
438
482
  from: handlerType,
@@ -1,4 +1,4 @@
1
- import { O11yResult } from './gen/processor/protos/processor'
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 }): O11yResult | null {
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
 
@@ -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
- const o11yRes = res.result
33
- expect(o11yRes?.counters).length(0)
34
- expect(o11yRes?.gauges).length(1)
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 = o11yRes?.gauges?.[0]
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
- const o11yRes2 = res2.result
43
- expect(o11yRes2?.counters).length(0)
44
- expect(o11yRes2?.gauges).length(1)
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 () => {
@@ -1,5 +1,5 @@
1
1
  import { DeepPartial } from '../gen/builtin'
2
- import { BigDecimal, MetricValue, O11yResult } from '@sentio/sdk'
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: O11yResult | undefined, name: string): Numberish | undefined {
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: O11yResult | undefined, name: string): Numberish | undefined {
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
+ // }