@sentio/sdk 2.35.0 → 2.35.1-rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. package/lib/eth/binds.d.ts +2 -2
  2. package/lib/eth/binds.d.ts.map +1 -1
  3. package/lib/eth/binds.js +4 -4
  4. package/lib/eth/binds.js.map +1 -1
  5. package/lib/eth/codegen/codegen.js +2 -0
  6. package/lib/eth/codegen/codegen.js.map +1 -1
  7. package/lib/eth/codegen/ethers-sentio.d.ts.map +1 -1
  8. package/lib/eth/codegen/ethers-sentio.js +2 -0
  9. package/lib/eth/codegen/ethers-sentio.js.map +1 -1
  10. package/lib/eth/eth-plugin.js +2 -2
  11. package/lib/eth/eth-plugin.js.map +1 -1
  12. package/lib/fuel/base-processor.d.ts +13 -0
  13. package/lib/fuel/base-processor.d.ts.map +1 -0
  14. package/lib/fuel/base-processor.js +26 -0
  15. package/lib/fuel/base-processor.js.map +1 -0
  16. package/lib/fuel/codegen/codegen.d.ts +2 -0
  17. package/lib/fuel/codegen/codegen.d.ts.map +1 -0
  18. package/lib/fuel/codegen/codegen.js +132 -0
  19. package/lib/fuel/codegen/codegen.js.map +1 -0
  20. package/lib/fuel/codegen/index.d.ts +2 -0
  21. package/lib/fuel/codegen/index.d.ts.map +1 -0
  22. package/lib/fuel/codegen/index.js +2 -0
  23. package/lib/fuel/codegen/index.js.map +1 -0
  24. package/lib/fuel/codegen/run.d.ts +2 -0
  25. package/lib/fuel/codegen/run.d.ts.map +1 -0
  26. package/lib/fuel/codegen/run.js +11 -0
  27. package/lib/fuel/codegen/run.js.map +1 -0
  28. package/lib/fuel/codegen/utils.d.ts +2 -0
  29. package/lib/fuel/codegen/utils.d.ts.map +1 -0
  30. package/lib/fuel/codegen/utils.js +4 -0
  31. package/lib/fuel/codegen/utils.js.map +1 -0
  32. package/lib/fuel/context.d.ts +14 -0
  33. package/lib/fuel/context.d.ts.map +1 -0
  34. package/lib/fuel/context.js +27 -0
  35. package/lib/fuel/context.js.map +1 -0
  36. package/lib/fuel/fuel-plugin.d.ts +17 -0
  37. package/lib/fuel/fuel-plugin.d.ts.map +1 -0
  38. package/lib/fuel/fuel-plugin.js +96 -0
  39. package/lib/fuel/fuel-plugin.js.map +1 -0
  40. package/lib/fuel/fuel-processor.d.ts +32 -0
  41. package/lib/fuel/fuel-processor.d.ts.map +1 -0
  42. package/lib/fuel/fuel-processor.js +94 -0
  43. package/lib/fuel/fuel-processor.js.map +1 -0
  44. package/lib/fuel/index.d.ts +7 -0
  45. package/lib/fuel/index.d.ts.map +1 -0
  46. package/lib/fuel/index.js +7 -0
  47. package/lib/fuel/index.js.map +1 -0
  48. package/lib/fuel/network.d.ts +8 -0
  49. package/lib/fuel/network.d.ts.map +1 -0
  50. package/lib/fuel/network.js +14 -0
  51. package/lib/fuel/network.js.map +1 -0
  52. package/lib/fuel/transaction.d.ts +8 -0
  53. package/lib/fuel/transaction.d.ts.map +1 -0
  54. package/lib/fuel/transaction.js +23 -0
  55. package/lib/fuel/transaction.js.map +1 -0
  56. package/lib/testing/fuel-facet.d.ts +9 -0
  57. package/lib/testing/fuel-facet.d.ts.map +1 -0
  58. package/lib/testing/fuel-facet.js +51 -0
  59. package/lib/testing/fuel-facet.js.map +1 -0
  60. package/lib/testing/test-processor-server.d.ts +2 -0
  61. package/lib/testing/test-processor-server.d.ts.map +1 -1
  62. package/lib/testing/test-processor-server.js +3 -0
  63. package/lib/testing/test-processor-server.js.map +1 -1
  64. package/package.json +12 -6
  65. package/src/eth/binds.ts +4 -4
  66. package/src/eth/codegen/codegen.ts +2 -0
  67. package/src/eth/codegen/ethers-sentio.ts +4 -1
  68. package/src/eth/eth-plugin.ts +2 -2
  69. package/src/fuel/base-processor.ts +38 -0
  70. package/src/fuel/codegen/codegen.ts +149 -0
  71. package/src/fuel/codegen/index.ts +1 -0
  72. package/src/fuel/codegen/run.ts +10 -0
  73. package/src/fuel/codegen/utils.ts +6 -0
  74. package/src/fuel/context.ts +34 -0
  75. package/src/fuel/fuel-plugin.ts +128 -0
  76. package/src/fuel/fuel-processor.ts +125 -0
  77. package/src/fuel/index.ts +6 -0
  78. package/src/fuel/network.ts +16 -0
  79. package/src/fuel/transaction.ts +43 -0
  80. package/src/testing/fuel-facet.ts +58 -0
  81. package/src/testing/test-processor-server.ts +4 -1
@@ -0,0 +1,149 @@
1
+ import fs, { readFileSync, writeFileSync } from 'fs'
2
+ import chalk from 'chalk'
3
+ import { AbiTypeGen, IFile, IFunction, ProgramTypeEnum } from '@fuel-ts/abi-typegen'
4
+ import mkdirp from 'mkdirp'
5
+ import path from 'path'
6
+ import { upperFirst } from './utils.js'
7
+
8
+ export async function codegen(abisDir: string, outDir: string) {
9
+ if (!fs.existsSync(abisDir)) {
10
+ return
11
+ }
12
+
13
+ const numFiles = await codegenInternal(abisDir, outDir)
14
+ console.log(chalk.green(`Generated ${numFiles} files for Fuel`))
15
+ }
16
+
17
+ function patchImport(contents: string) {
18
+ return contents.replace(/from\s+['"](\..+)['"]/g, `from '\$1.js'`)
19
+ }
20
+
21
+ function patchEnumType(contents: string) {
22
+ const matches = contents.matchAll(/export type (.+) = Enum<{ Ok: T, Err: E }>;/g)
23
+
24
+ for (const m of matches) {
25
+ const vname = m[1]
26
+ contents = contents.replace(m[0], `export type ${vname}<T,E> = Enum<{ Ok: T, Err: E }>;`)
27
+
28
+ const reg = new RegExp(`export type (.+) = ${vname}`, 'g')
29
+ contents = contents.replace(reg, `export type \$1<T,E> = ${vname}<T,E>`)
30
+ }
31
+ return contents
32
+ }
33
+
34
+ async function codegenInternal(abisDir: string, outDir: string): Promise<number> {
35
+ const allFiles = fs.readdirSync(abisDir)
36
+ if (allFiles.length === 0) {
37
+ return 0
38
+ }
39
+
40
+ const allABIFiles = []
41
+ for (const f of allFiles) {
42
+ if (f.toLowerCase().endsWith('-abi.json')) {
43
+ allABIFiles.push(path.join(abisDir, f))
44
+ }
45
+ }
46
+ if (allABIFiles.length === 0) {
47
+ return 0
48
+ }
49
+ const abiFiles = allABIFiles.map((filepath) => {
50
+ const abi: IFile = {
51
+ path: filepath,
52
+ contents: readFileSync(filepath, 'utf-8')
53
+ }
54
+ return abi
55
+ })
56
+
57
+ // fuels type gen
58
+ const abiTypeGen = new AbiTypeGen({
59
+ abiFiles,
60
+ binFiles: [],
61
+ storageSlotsFiles: [],
62
+ outputDir: outDir,
63
+ programType: ProgramTypeEnum.CONTRACT
64
+ })
65
+
66
+ mkdirp.sync(outDir)
67
+ mkdirp.sync(path.join(outDir, 'factories'))
68
+
69
+ abiTypeGen.files.forEach((file) => {
70
+ if (!file.path.endsWith('.hex.ts')) {
71
+ let content = patchImport(file.contents)
72
+ content = patchEnumType(content)
73
+ writeFileSync(file.path, content)
74
+ }
75
+ })
76
+
77
+ for (const abi of abiTypeGen.abis) {
78
+ const name = abi.name.endsWith('Abi') ? abi.name.slice(0, -3) : abi.name
79
+ const filePath = path.join(outDir, `${name}Processor.ts`)
80
+ const importedTypes = collectImportedTypes(abi.types)
81
+
82
+ const content = `/* Autogenerated file. Do not edit manually. */
83
+
84
+ /* tslint:disable */
85
+ /* eslint-disable */
86
+
87
+ import { FuelAbstractProcessor, FuelContext, FuelProcessorConfig, TypedCall, FuelFetchConfig} from '@sentio/sdk/fuel'
88
+ import {${abi.name}__factory } from './factories/${abi.name}__factory.js'
89
+ import {${abi.commonTypesInUse.join(',')}} from './common.js'
90
+ import {${importedTypes.join(',')}} from './${abi.name}.js'
91
+
92
+ import type {
93
+ BigNumberish,
94
+ BN,
95
+ BytesLike,
96
+ } from 'fuels';
97
+
98
+
99
+ namespace ${name} {
100
+ ${abi.functions.map(genCallType).join('\n')}
101
+ }
102
+
103
+ export class ${name}Processor extends FuelAbstractProcessor {
104
+ constructor(config?: FuelProcessorConfig) {
105
+ super(${abi.name}__factory.abi, config)
106
+ }
107
+
108
+ static bind(config?: FuelProcessorConfig) {
109
+ return new ${name}Processor(config)
110
+ }
111
+
112
+ ${abi.functions.map((f) => genOnCallFunction(name, f)).join('\n')}
113
+ }
114
+ `
115
+ writeFileSync(filePath, content)
116
+ }
117
+
118
+ return allABIFiles.length
119
+ }
120
+
121
+ function genCallType(f: IFunction) {
122
+ const name = upperFirst(f.name)
123
+ return `
124
+ export interface ${name}Call extends TypedCall<[${f.attributes.inputs}], ${f.attributes.output}> {
125
+ args: [${f.attributes.inputs}]
126
+ returnValue: ${f.attributes.output}
127
+ }
128
+ `
129
+ }
130
+
131
+ function genOnCallFunction(contractName: string, f: IFunction) {
132
+ const name = upperFirst(f.name)
133
+ return `
134
+ onCall${name}(handler: (call: ${contractName}.${name}Call, ctx: FuelContext) => void | Promise<void>, config: FuelFetchConfig) {
135
+ super.onCallMethod('${f.name}', handler, config)
136
+ }`
137
+ }
138
+
139
+ function collectImportedTypes(types: any[]): string[] {
140
+ const ret = new Set<string>()
141
+ for (const type of types) {
142
+ if ((type && type.name == 'struct') || type.name == 'enum') {
143
+ ret.add(type.attributes.inputLabel)
144
+ ret.add(type.attributes.outputLabel)
145
+ }
146
+ }
147
+
148
+ return Array.from(ret)
149
+ }
@@ -0,0 +1 @@
1
+ export * from './codegen.js'
@@ -0,0 +1,10 @@
1
+ import { codegen } from './index.js'
2
+
3
+ if (process.argv.length > 3) {
4
+ const abisDir = process.argv[2]
5
+ const targetDir = process.argv[3]
6
+ await codegen(abisDir, targetDir)
7
+ } else {
8
+ console.error('Not enough argument')
9
+ process.exit(1)
10
+ }
@@ -0,0 +1,6 @@
1
+
2
+
3
+
4
+ export function upperFirst(str: string): string {
5
+ return str.charAt(0).toUpperCase() + str.slice(1);
6
+ }
@@ -0,0 +1,34 @@
1
+ import { BaseContext, Labels, normalizeLabels } from '../core/index.js'
2
+ import { ChainId } from '@sentio/chain'
3
+ import { RecordMetaData } from '@sentio/protos'
4
+ import { InvocationCallResult } from 'fuels'
5
+ import { FuelTransaction } from './transaction.js'
6
+
7
+ export type FuelCall = InvocationCallResult
8
+
9
+ export class FuelContext extends BaseContext {
10
+ constructor(
11
+ readonly transaction: FuelTransaction | null,
12
+ readonly chainId: ChainId
13
+ ) {
14
+ super({})
15
+ }
16
+
17
+ getChainId(): ChainId {
18
+ return this.chainId
19
+ }
20
+
21
+ protected getMetaDataInternal(name: string, labels: Labels): RecordMetaData {
22
+ return {
23
+ address: this.transaction?.id || '',
24
+ contractName: this.transaction?.id || '', // TODO
25
+ blockNumber: 0n,
26
+ transactionIndex: 0,
27
+ transactionHash: this.transaction?.id || '', // TODO
28
+ chainId: this.getChainId(),
29
+ name: name,
30
+ logIndex: 0,
31
+ labels: normalizeLabels(labels)
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,128 @@
1
+ import { errorString, GLOBAL_CONFIG, mergeProcessResults, Plugin, PluginManager, USER_PROCESSOR } from '@sentio/runtime'
2
+ import {
3
+ ContractConfig,
4
+ Data_FuelCall,
5
+ DataBinding,
6
+ HandlerType,
7
+ ProcessConfigResponse,
8
+ ProcessResult,
9
+ StartRequest
10
+ } from '@sentio/protos'
11
+
12
+ import { ServerError, Status } from 'nice-grpc'
13
+ import { GlobalProcessorState } from '../eth/base-processor.js'
14
+ import { TemplateInstanceState } from '../core/template.js'
15
+ import { FuelProcessorState } from './fuel-processor.js'
16
+
17
+ interface Handlers {
18
+ callHandlers: ((trace: Data_FuelCall) => Promise<ProcessResult>)[]
19
+ }
20
+
21
+ export class FuelPlugin extends Plugin {
22
+ name: string = 'FuelPlugin'
23
+ handlers: Handlers = {
24
+ callHandlers: []
25
+ }
26
+
27
+ async configure(config: ProcessConfigResponse) {
28
+ const handlers: Handlers = {
29
+ callHandlers: []
30
+ }
31
+
32
+ for (const processor of FuelProcessorState.INSTANCE.getValues()) {
33
+ await processor.configure()
34
+ const contractConfig = ContractConfig.fromPartial({
35
+ processorType: USER_PROCESSOR,
36
+ contract: {
37
+ name: processor.config.name,
38
+ chainId: processor.config.chainId.toString(),
39
+ address: processor.config.address,
40
+ abi: ''
41
+ },
42
+ startBlock: processor.config.startBlock,
43
+ endBlock: processor.config.endBlock
44
+ })
45
+
46
+ for (const callHandler of processor.callHandlers) {
47
+ const handlerId = handlers.callHandlers.push(callHandler.handler) - 1
48
+ const fetchConfig = {
49
+ handlerId,
50
+ filters: callHandler.fetchConfig.filters || []
51
+ }
52
+ contractConfig.fuelCallConfigs.push(fetchConfig)
53
+ }
54
+
55
+ // Finish up a contract
56
+ config.contractConfigs.push(contractConfig)
57
+ }
58
+
59
+ for (const processor of GlobalProcessorState.INSTANCE.getValues()) {
60
+ const chainId = processor.getChainId()
61
+
62
+ const contractConfig = ContractConfig.fromPartial({
63
+ processorType: USER_PROCESSOR,
64
+ contract: {
65
+ name: processor.config.name,
66
+ chainId: chainId.toString(),
67
+ address: processor.config.address, // can only be *
68
+ abi: ''
69
+ },
70
+ startBlock: processor.config.startBlock,
71
+ endBlock: processor.config.endBlock
72
+ })
73
+
74
+ config.contractConfigs.push(contractConfig)
75
+ }
76
+
77
+ this.handlers = handlers
78
+ }
79
+
80
+ supportedHandlers = [HandlerType.FUEL_CALL]
81
+
82
+ processBinding(request: DataBinding): Promise<ProcessResult> {
83
+ // return Promise.resolve(undefined);
84
+ switch (request.handlerType) {
85
+ // case HandlerType.FUEL_LOG:
86
+ // return this.processLog(request)
87
+ // case HandlerType.FUEL_TRACE:
88
+ // return this.processTrace(request)
89
+ // case HandlerType.FUEL_BLOCK:
90
+ // return this.processBlock(request)
91
+ case HandlerType.FUEL_CALL:
92
+ return this.processTransaction(request)
93
+ default:
94
+ throw new ServerError(Status.INVALID_ARGUMENT, 'No handle type registered ' + request.handlerType)
95
+ }
96
+ }
97
+
98
+ async start(request: StartRequest) {}
99
+
100
+ stateDiff(config: ProcessConfigResponse): boolean {
101
+ return TemplateInstanceState.INSTANCE.getValues().length !== config.templateInstances.length
102
+ }
103
+
104
+ async processTransaction(binding: DataBinding): Promise<ProcessResult> {
105
+ if (!binding.data?.fuelCall?.transaction) {
106
+ throw new ServerError(Status.INVALID_ARGUMENT, "transaction can't be null")
107
+ }
108
+ const fuelTransaction = binding.data.fuelCall
109
+
110
+ const promises: Promise<ProcessResult>[] = []
111
+
112
+ for (const handlerId of binding.handlerIds) {
113
+ const promise = this.handlers.callHandlers[handlerId](fuelTransaction).catch((e) => {
114
+ throw new ServerError(
115
+ Status.INTERNAL,
116
+ 'error processing transaction: ' + JSON.stringify(fuelTransaction.transaction) + '\n' + errorString(e)
117
+ )
118
+ })
119
+ if (GLOBAL_CONFIG.execution.sequential) {
120
+ await promise
121
+ }
122
+ promises.push(promise)
123
+ }
124
+ return mergeProcessResults(await Promise.all(promises))
125
+ }
126
+ }
127
+
128
+ PluginManager.INSTANCE.register(new FuelPlugin())
@@ -0,0 +1,125 @@
1
+ import { ListStateStorage } from '@sentio/runtime'
2
+ import { Data_FuelCall, FuelCallFilter, FuelCallHandlerConfig, ProcessResult } from '@sentio/protos'
3
+ import { FuelCall, FuelContext } from './context.js'
4
+ import { bn, Contract, Interface, InvocationCallResult, JsonAbi, Provider } from 'fuels'
5
+ import { FuelNetwork, getRpcEndpoint } from './network.js'
6
+ import { decodeFuelTransaction, DEFAULT_FUEL_FETCH_CONFIG, FuelFetchConfig, FuelTransaction } from './transaction.js'
7
+
8
+ export class FuelProcessorState extends ListStateStorage<FuelProcessor> {
9
+ static INSTANCE = new FuelProcessorState()
10
+ }
11
+
12
+ export class FuelProcessor {
13
+ callHandlers: CallHandler<Data_FuelCall>[] = []
14
+
15
+ private provider: Provider
16
+
17
+ static bind(config: FuelProcessorConfig): FuelProcessor {
18
+ const processor = new FuelProcessor(config)
19
+ FuelProcessorState.INSTANCE.addValue(processor)
20
+ return processor
21
+ }
22
+
23
+ constructor(readonly config: FuelProcessorConfig) {}
24
+
25
+ async configure() {
26
+ const url = getRpcEndpoint(this.config.chainId)
27
+ this.provider = await Provider.create(url)
28
+ }
29
+
30
+ public onTransaction(
31
+ handler: (transaction: FuelTransaction, ctx: FuelContext) => void | Promise<void>,
32
+ config: FuelFetchConfig = DEFAULT_FUEL_FETCH_CONFIG
33
+ ) {
34
+ const callHandler = {
35
+ handler: async (call: Data_FuelCall) => {
36
+ const abiMap = this.config.abi
37
+ ? {
38
+ [this.config.address]: this.config.abi
39
+ }
40
+ : {}
41
+ const tx = decodeFuelTransaction(call.transaction, abiMap, this.provider)
42
+
43
+ const ctx = new FuelContext(tx, this.config.chainId)
44
+ await handler(tx, ctx)
45
+ return ctx.stopAndGetResult()
46
+ },
47
+ fetchConfig: {
48
+ filters: [],
49
+ ...config
50
+ }
51
+ }
52
+ this.callHandlers.push(callHandler)
53
+ return this
54
+ }
55
+
56
+ public onCall(
57
+ nameFilter: string | string[],
58
+ handler: (call: FuelCall, ctx: FuelContext) => void | Promise<void>,
59
+ config: FuelFetchConfig = DEFAULT_FUEL_FETCH_CONFIG
60
+ ) {
61
+ const names = new Set(Array.isArray(nameFilter) ? nameFilter : [nameFilter])
62
+
63
+ if (!this.config.abi) {
64
+ throw new Error('ABI must be provided to use onCall')
65
+ }
66
+ const abi = this.config.abi
67
+
68
+ const filters: Record<string, FuelCallFilter> = {}
69
+ const abiInterface = new Interface(abi)
70
+ for (const name of names) {
71
+ try {
72
+ const func = abiInterface.functions[name]
73
+ const filter = bn(func.selector, 'hex').toString()
74
+ filters[func.name] = {
75
+ function: filter,
76
+ includeFailed: !!config.includeFailed
77
+ }
78
+ } catch (e) {
79
+ console.error(e)
80
+ }
81
+ }
82
+
83
+ const callHandler = {
84
+ handler: async (call: Data_FuelCall) => {
85
+ const contract = new Contract(this.config.address, abi, this.provider)
86
+ const gqlTransaction = call.transaction
87
+ const tx = decodeFuelTransaction(gqlTransaction, { [this.config.address]: abi }, this.provider)
88
+
89
+ const ctx = new FuelContext(tx, this.config.chainId)
90
+ for (const op of tx.operations) {
91
+ for (const call of op.calls || []) {
92
+ if (names.has(call.functionName)) {
93
+ const fn = contract.functions[call.functionName]
94
+ const args = Object.values(call.argumentsProvided || {})
95
+ const scope = fn(...args)
96
+ const invocationResult = await InvocationCallResult.build(scope, tx, false)
97
+ await handler(invocationResult, ctx)
98
+ }
99
+ }
100
+ }
101
+
102
+ return ctx.stopAndGetResult()
103
+ },
104
+ fetchConfig: {
105
+ filters: Object.values(filters)
106
+ }
107
+ }
108
+ this.callHandlers.push(callHandler)
109
+ return this
110
+ }
111
+ }
112
+
113
+ export type CallHandler<T> = {
114
+ handler: (call: T) => Promise<ProcessResult>
115
+ fetchConfig: Partial<FuelCallHandlerConfig>
116
+ }
117
+
118
+ export type FuelProcessorConfig = {
119
+ address: string
120
+ name?: string
121
+ chainId: FuelNetwork
122
+ startBlock?: bigint
123
+ endBlock?: bigint
124
+ abi?: JsonAbi
125
+ }
@@ -0,0 +1,6 @@
1
+ export { FuelPlugin } from './fuel-plugin.js'
2
+ export * from './context.js'
3
+ export * from './fuel-processor.js'
4
+ export * from './network.js'
5
+ export * from './transaction.js'
6
+ export * from './base-processor.js'
@@ -0,0 +1,16 @@
1
+ import { FuelChainId } from '@sentio/chain'
2
+ import { FUEL_BETA_5_NETWORK_URL, FUEL_NETWORK_URL } from 'fuels'
3
+
4
+ export type FuelNetwork = FuelChainId
5
+ export const FuelNetwork = <const>{
6
+ MAIN_NET: FuelChainId.FUEL_MAINNET,
7
+ TEST_NET: FuelChainId.FUEL_TESTNET_BETA_V5
8
+ }
9
+
10
+ export function getRpcEndpoint(network: FuelNetwork): string {
11
+ switch (network) {
12
+ case FuelNetwork.TEST_NET:
13
+ return FUEL_BETA_5_NETWORK_URL
14
+ }
15
+ return FUEL_NETWORK_URL
16
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ AbiMap,
3
+ arrayify,
4
+ assembleTransactionSummary,
5
+ bn,
6
+ processGqlReceipt,
7
+ Provider,
8
+ TransactionCoder,
9
+ TransactionSummary
10
+ } from 'fuels'
11
+
12
+ export type FuelFetchConfig = {
13
+ includeFailed?: boolean
14
+ }
15
+
16
+ export const DEFAULT_FUEL_FETCH_CONFIG: FuelFetchConfig = {
17
+ includeFailed: false
18
+ }
19
+
20
+ export type FuelTransaction = TransactionSummary
21
+
22
+ export function decodeFuelTransaction(gqlTransaction: any, abiMap: AbiMap, provider: Provider): FuelTransaction {
23
+ const rawPayload = arrayify(gqlTransaction.rawPayload)
24
+
25
+ const [decodedTransaction] = new TransactionCoder().decode(rawPayload, 0)
26
+
27
+ const receipts = gqlTransaction.receipts?.map(processGqlReceipt) || []
28
+
29
+ const { gasPerByte, gasPriceFactor, maxInputs, gasCosts } = provider.getChain().consensusParameters
30
+
31
+ return assembleTransactionSummary({
32
+ id: gqlTransaction.id,
33
+ receipts,
34
+ transaction: decodedTransaction,
35
+ transactionBytes: rawPayload,
36
+ gqlTransactionStatus: gqlTransaction.status,
37
+ gasPerByte: bn(gasPerByte),
38
+ gasPriceFactor: bn(gasPriceFactor),
39
+ abiMap,
40
+ maxInputs,
41
+ gasCosts
42
+ })
43
+ }
@@ -0,0 +1,58 @@
1
+ import { TestProcessorServer } from './test-processor-server.js'
2
+ import { FuelChainId } from '@sentio/chain'
3
+ import { DataBinding, HandlerType } from '@sentio/protos'
4
+ import { FuelNetwork } from '../fuel/index.js'
5
+
6
+ export class FuelFacet {
7
+ server: TestProcessorServer
8
+
9
+ constructor(server: TestProcessorServer) {
10
+ this.server = server
11
+ }
12
+
13
+ testOnTransaction(transaction: any, network: FuelNetwork = FuelNetwork.TEST_NET) {
14
+ const bindings = this.buildBinding(transaction, network)
15
+ if (!bindings) {
16
+ throw Error('Invalid test transaction: ' + JSON.stringify(transaction))
17
+ }
18
+
19
+ return this.server.processBindings({
20
+ bindings
21
+ })
22
+ }
23
+
24
+ private buildBinding(transaction: any, network: FuelChainId): DataBinding[] {
25
+ const res: DataBinding[] = []
26
+ for (const config of this.server.contractConfigs) {
27
+ if (config.contract?.chainId !== network) {
28
+ continue
29
+ }
30
+ for (const callConfig of config.fuelCallConfigs) {
31
+ const binding = {
32
+ data: {
33
+ fuelCall: {
34
+ transaction,
35
+ timestamp: new Date()
36
+ }
37
+ },
38
+ handlerIds: [callConfig.handlerId],
39
+ handlerType: HandlerType.FUEL_CALL
40
+ }
41
+
42
+ const filter = callConfig.filters[0]?.function
43
+ if (filter) {
44
+ // filter out by tx receipt
45
+ for (const receipt of transaction.receipts || []) {
46
+ if (receipt.receiptType == 'CALL' && receipt.param1 == filter) {
47
+ res.push(binding)
48
+ }
49
+ }
50
+ } else {
51
+ res.push(binding)
52
+ }
53
+ }
54
+ }
55
+
56
+ return res
57
+ }
58
+ }
@@ -8,7 +8,7 @@ import {
8
8
  ProcessConfigRequest,
9
9
  ProcessConfigResponse,
10
10
  ProcessorServiceImplementation,
11
- StartRequest,
11
+ StartRequest
12
12
  } from '@sentio/protos'
13
13
  import { CallContext } from 'nice-grpc-common'
14
14
  import { Endpoints, ProcessorServiceImpl, State } from '@sentio/runtime'
@@ -18,6 +18,7 @@ import { AptosFacet } from './aptos-facet.js'
18
18
  import { SolanaFacet } from './solana-facet.js'
19
19
  import { EthFacet } from './eth-facet.js'
20
20
  import { SuiFacet } from './sui-facet.js'
21
+ import { FuelFacet } from './fuel-facet.js'
21
22
 
22
23
  export const TEST_CONTEXT: CallContext = <CallContext>{}
23
24
 
@@ -34,6 +35,7 @@ export class TestProcessorServer implements ProcessorServiceImplementation {
34
35
  eth: EthFacet
35
36
  solana: SolanaFacet
36
37
  sui: SuiFacet
38
+ fuel: FuelFacet
37
39
 
38
40
  constructor(loader: () => Promise<any>, httpEndpoints: Record<string, string> = {}) {
39
41
  cleanTest()
@@ -43,6 +45,7 @@ export class TestProcessorServer implements ProcessorServiceImplementation {
43
45
  this.solana = new SolanaFacet(this)
44
46
  this.eth = new EthFacet(this)
45
47
  this.sui = new SuiFacet(this)
48
+ this.fuel = new FuelFacet(this)
46
49
 
47
50
  for (const k in CHAIN_MAP) {
48
51
  const http = httpEndpoints[k] || ''