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