@sentio/runtime 2.40.0-rc.21 → 2.40.0-rc.23

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.
@@ -25,6 +25,7 @@ import { NodeSDK } from '@opentelemetry/sdk-node'
25
25
  import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc'
26
26
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'
27
27
  import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'
28
+ import { diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api'
28
29
 
29
30
  const sdk = new NodeSDK({
30
31
  traceExporter: new OTLPTraceExporter(),
@@ -63,6 +64,10 @@ const logLevel = process.env['LOG_LEVEL']?.toUpperCase()
63
64
  setupLogger(options['log-format'] === 'json', logLevel === 'debug' ? true : options.debug)
64
65
  console.debug('Starting with', options.target)
65
66
 
67
+ if (options.debug) {
68
+ diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG)
69
+ }
70
+
66
71
  Error.stackTraceLimit = 20
67
72
 
68
73
  const fullPath = path.resolve(options['chains-config'])
package/src/provider.ts CHANGED
@@ -6,6 +6,7 @@ import { EthChainId } from '@sentio/chain'
6
6
  import { LRUCache } from 'lru-cache'
7
7
  import { providerMetrics } from './metrics.js'
8
8
  const { miss_count, hit_count, total_duration, total_queued, queue_size } = providerMetrics
9
+
9
10
  export const DummyProvider = new JsonRpcProvider('', Network.from(1))
10
11
 
11
12
  const providers = new Map<string, JsonRpcProvider>()
package/src/service.ts CHANGED
@@ -33,6 +33,8 @@ import { Subject } from 'rxjs'
33
33
  import { getProvider } from './provider.js'
34
34
  import { EthChainId } from '@sentio/chain'
35
35
  import { Provider } from 'ethers'
36
+ import { decodeMulticallResult, encodeMulticallData, getMulticallAddress, Multicall3Call } from './multicall.js'
37
+
36
38
  import { processMetrics, providerMetrics, dbMetrics } from './metrics.js'
37
39
  const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
38
40
 
@@ -174,11 +176,11 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
174
176
  const providers = new Map<string, Provider>()
175
177
  for (const result of preprocessResults) {
176
178
  for (const param of result.ethCallParams) {
177
- const { chainId, address, blockTag } = param.context!
179
+ const { chainId, blockTag } = param.context!
178
180
  if (!providers.has(chainId)) {
179
181
  providers.set(chainId, getProvider(chainId as EthChainId))
180
182
  }
181
- const key = [chainId, address, blockTag].join('|')
183
+ const key = [chainId, blockTag].join('|')
182
184
  if (!groupedRequests.has(key)) {
183
185
  groupedRequests.set(key, [])
184
186
  }
@@ -187,30 +189,76 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
187
189
  }
188
190
 
189
191
  const start = Date.now()
190
- const callPromises = []
192
+ const MULTICALL_THRESHOLD = 1
193
+ const callPromises: Promise<[string, string]>[] = []
194
+ const multicallPromises: Promise<[string, string][]>[] = []
195
+
191
196
  for (const params of groupedRequests.values()) {
192
- const { chainId, address, blockTag } = params[0].context!
193
- // TODO multicall
194
- for (const param of params) {
195
- callPromises.push(
197
+ const { chainId, blockTag } = params[0].context!
198
+ const multicallAddress = getMulticallAddress(chainId as EthChainId)
199
+ if (params.length <= MULTICALL_THRESHOLD || !multicallAddress) {
200
+ for (const param of params) {
201
+ callPromises.push(
202
+ providers
203
+ .get(chainId)!
204
+ .call({
205
+ to: param.context!.address,
206
+ data: param.calldata,
207
+ blockTag
208
+ })
209
+ .then((result) => [makeEthCallKey(param), result])
210
+ )
211
+ }
212
+ continue
213
+ }
214
+
215
+ // construct multicalls
216
+ const CHUNK_SIZE = 128
217
+ for (let i = 0; i < params.length; i += CHUNK_SIZE) {
218
+ const chunk = params.slice(i, i + CHUNK_SIZE)
219
+ const calls: Multicall3Call[] = chunk.map((param) => ({
220
+ target: param.context!.address,
221
+ callData: param.calldata
222
+ }))
223
+ const data = encodeMulticallData(calls)
224
+ multicallPromises.push(
196
225
  providers
197
226
  .get(chainId)!
198
227
  .call({
199
- to: address,
200
- data: param.calldata,
228
+ to: multicallAddress,
229
+ data: data,
201
230
  blockTag
202
231
  })
203
- .then((result) => [makeEthCallKey(param), result])
232
+ .then((raw) => {
233
+ const result = decodeMulticallResult(raw).returnData
234
+ if (result.length != chunk.length) {
235
+ throw new Error(`multicall result length mismatch, params: ${chunk.length}, result: ${result.length}`)
236
+ }
237
+ const ret: [string, string][] = []
238
+ for (let i = 0; i < chunk.length; i++) {
239
+ ret.push([makeEthCallKey(chunk[i]), result[i]])
240
+ }
241
+ return ret
242
+ })
204
243
  )
205
244
  }
206
245
  }
246
+
207
247
  let results: { [p: string]: string } = {}
208
248
  try {
209
249
  results = Object.fromEntries(await Promise.all(callPromises))
250
+ for (const multicallResult of await Promise.all(multicallPromises)) {
251
+ results = {
252
+ ...results,
253
+ ...Object.fromEntries(multicallResult)
254
+ }
255
+ }
210
256
  } catch (e) {
211
257
  console.error(`eth call error: ${e}`)
212
258
  }
213
- console.log(`${callPromises.length} calls finished, elapsed: ${Date.now() - start}ms`)
259
+ console.log(
260
+ `${Object.keys(results).length} calls finished, actual calls: ${callPromises.length + multicallPromises.length}, elapsed: ${Date.now() - start}ms`
261
+ )
214
262
  return {
215
263
  ethCallResults: results
216
264
  }