@sentio/sdk 2.35.0 → 2.35.1-rc.1

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.
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] || ''