@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.
- package/lib/eth/binds.d.ts +2 -2
- package/lib/eth/binds.d.ts.map +1 -1
- package/lib/eth/binds.js +4 -4
- package/lib/eth/binds.js.map +1 -1
- package/lib/eth/codegen/codegen.js +2 -0
- package/lib/eth/codegen/codegen.js.map +1 -1
- package/lib/eth/codegen/ethers-sentio.d.ts.map +1 -1
- package/lib/eth/codegen/ethers-sentio.js +2 -0
- package/lib/eth/codegen/ethers-sentio.js.map +1 -1
- package/lib/eth/eth-plugin.js +2 -2
- package/lib/eth/eth-plugin.js.map +1 -1
- package/lib/fuel/base-processor.d.ts +13 -0
- package/lib/fuel/base-processor.d.ts.map +1 -0
- package/lib/fuel/base-processor.js +26 -0
- package/lib/fuel/base-processor.js.map +1 -0
- package/lib/fuel/codegen/codegen.d.ts +2 -0
- package/lib/fuel/codegen/codegen.d.ts.map +1 -0
- package/lib/fuel/codegen/codegen.js +132 -0
- package/lib/fuel/codegen/codegen.js.map +1 -0
- package/lib/fuel/codegen/index.d.ts +2 -0
- package/lib/fuel/codegen/index.d.ts.map +1 -0
- package/lib/fuel/codegen/index.js +2 -0
- package/lib/fuel/codegen/index.js.map +1 -0
- package/lib/fuel/codegen/run.d.ts +2 -0
- package/lib/fuel/codegen/run.d.ts.map +1 -0
- package/lib/fuel/codegen/run.js +11 -0
- package/lib/fuel/codegen/run.js.map +1 -0
- package/lib/fuel/codegen/utils.d.ts +2 -0
- package/lib/fuel/codegen/utils.d.ts.map +1 -0
- package/lib/fuel/codegen/utils.js +4 -0
- package/lib/fuel/codegen/utils.js.map +1 -0
- package/lib/fuel/context.d.ts +14 -0
- package/lib/fuel/context.d.ts.map +1 -0
- package/lib/fuel/context.js +27 -0
- package/lib/fuel/context.js.map +1 -0
- package/lib/fuel/fuel-plugin.d.ts +17 -0
- package/lib/fuel/fuel-plugin.d.ts.map +1 -0
- package/lib/fuel/fuel-plugin.js +96 -0
- package/lib/fuel/fuel-plugin.js.map +1 -0
- package/lib/fuel/fuel-processor.d.ts +32 -0
- package/lib/fuel/fuel-processor.d.ts.map +1 -0
- package/lib/fuel/fuel-processor.js +94 -0
- package/lib/fuel/fuel-processor.js.map +1 -0
- package/lib/fuel/index.d.ts +7 -0
- package/lib/fuel/index.d.ts.map +1 -0
- package/lib/fuel/index.js +7 -0
- package/lib/fuel/index.js.map +1 -0
- package/lib/fuel/network.d.ts +8 -0
- package/lib/fuel/network.d.ts.map +1 -0
- package/lib/fuel/network.js +14 -0
- package/lib/fuel/network.js.map +1 -0
- package/lib/fuel/transaction.d.ts +8 -0
- package/lib/fuel/transaction.d.ts.map +1 -0
- package/lib/fuel/transaction.js +23 -0
- package/lib/fuel/transaction.js.map +1 -0
- package/lib/testing/fuel-facet.d.ts +9 -0
- package/lib/testing/fuel-facet.d.ts.map +1 -0
- package/lib/testing/fuel-facet.js +51 -0
- package/lib/testing/fuel-facet.js.map +1 -0
- package/lib/testing/test-processor-server.d.ts +2 -0
- package/lib/testing/test-processor-server.d.ts.map +1 -1
- package/lib/testing/test-processor-server.js +3 -0
- package/lib/testing/test-processor-server.js.map +1 -1
- package/package.json +12 -6
- package/src/eth/binds.ts +4 -4
- package/src/eth/codegen/codegen.ts +2 -0
- package/src/eth/codegen/ethers-sentio.ts +4 -1
- package/src/eth/eth-plugin.ts +2 -2
- package/src/fuel/base-processor.ts +38 -0
- package/src/fuel/codegen/codegen.ts +149 -0
- package/src/fuel/codegen/index.ts +1 -0
- package/src/fuel/codegen/run.ts +10 -0
- package/src/fuel/codegen/utils.ts +6 -0
- package/src/fuel/context.ts +34 -0
- package/src/fuel/fuel-plugin.ts +128 -0
- package/src/fuel/fuel-processor.ts +125 -0
- package/src/fuel/index.ts +6 -0
- package/src/fuel/network.ts +16 -0
- package/src/fuel/transaction.ts +43 -0
- package/src/testing/fuel-facet.ts +58 -0
- 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,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,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] || ''
|