@subql/common-solana 1.0.1-0 → 1.0.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/CHANGELOG.md +9 -1
- package/package.json +15 -3
- package/src/codegen/codegen.spec.ts +0 -145
- package/src/codegen/codegen.ts +0 -96
- package/src/codegen/idl.ts +0 -89
- package/src/codegen/index.ts +0 -5
- package/src/index.ts +0 -16
- package/src/project/index.ts +0 -11
- package/src/project/load.ts +0 -10
- package/src/project/models.ts +0 -213
- package/src/project/project.spec.ts +0 -123
- package/src/project/types.ts +0 -19
- package/src/project/utils.ts +0 -50
- package/src/project/versioned/ProjectManifestVersioned.ts +0 -71
- package/src/project/versioned/index.ts +0 -5
- package/src/project/versioned/v1_0_0/index.ts +0 -4
- package/src/project/versioned/v1_0_0/model.ts +0 -200
- package/templates/idl.ts.ejs +0 -10
- package/test/abiTest/project.yaml +0 -51
- package/test/project_1.0.0.yaml +0 -91
- package/tsconfig.json +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [1.0.1] - 2025-07-01
|
|
10
|
+
### Changed
|
|
11
|
+
- Update `@subql/common` (#12)
|
|
12
|
+
|
|
13
|
+
## [1.0.0] - 2025-05-29
|
|
8
14
|
### Changed
|
|
9
15
|
- Initial release
|
|
10
16
|
|
|
17
|
+
[Unreleased]: https://github.com/subquery/subql-solana/compare/common-solana/1.0.1...HEAD
|
|
18
|
+
[1.0.1]: https://github.com/subquery/subql-solana/compare/common-solana/1.0.0...common-solana/1.0.1
|
|
11
19
|
[1.0.0]: https://github.com/subquery/subql-solana/releases/tag/common-solana/1.0.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@subql/common-solana",
|
|
3
|
-
"version": "1.0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "rm -rf dist && tsc -b",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@codama/nodes-from-anchor": "^1.1.11",
|
|
18
18
|
"@codama/renderers-js": "^1.2.10",
|
|
19
19
|
"@subql/common": "^5.7.0",
|
|
20
|
-
"@subql/types-solana": "1.0.1
|
|
20
|
+
"@subql/types-solana": "1.0.1",
|
|
21
21
|
"codama": "^1.2.11",
|
|
22
22
|
"js-yaml": "^4.1.0",
|
|
23
23
|
"pino": "^6.13.3",
|
|
@@ -36,5 +36,17 @@
|
|
|
36
36
|
"ejs": "3.1.10",
|
|
37
37
|
"lodash": "4.17.21"
|
|
38
38
|
},
|
|
39
|
-
"
|
|
39
|
+
"files": [
|
|
40
|
+
"/dist",
|
|
41
|
+
"!/dist/**/*.spec.js",
|
|
42
|
+
"!/dist/**/*.spec.d.ts",
|
|
43
|
+
"!/dist/**/*.spec.js.map",
|
|
44
|
+
"!/dist/**/*.test.js",
|
|
45
|
+
"!/dist/**/*.test.d.ts",
|
|
46
|
+
"!/dist/**/*.test.js.map",
|
|
47
|
+
"package.json",
|
|
48
|
+
"README.md",
|
|
49
|
+
"CHANGELOG.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
]
|
|
40
52
|
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import {SolanaDatasourceKind, SolanaHandlerKind, SubqlRuntimeDatasource} from '@subql/types-solana';
|
|
6
|
-
import rimraf from 'rimraf';
|
|
7
|
-
import {generateIDLInterfaces, validateDiscriminators} from './codegen';
|
|
8
|
-
|
|
9
|
-
const outDir = path.join(__dirname, 'test-output');
|
|
10
|
-
|
|
11
|
-
describe('IDL Codegen', () => {
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
rimraf.sync(outDir);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('can generate an interface from an IDL', async () => {
|
|
17
|
-
const ds: SubqlRuntimeDatasource = {
|
|
18
|
-
kind: SolanaDatasourceKind.Runtime,
|
|
19
|
-
startBlock: 0,
|
|
20
|
-
assets: new Map([
|
|
21
|
-
['jupiter', {file: '../../../../node/test/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.idl.json'}],
|
|
22
|
-
]),
|
|
23
|
-
mapping: {
|
|
24
|
-
file: '',
|
|
25
|
-
handlers: [],
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const renderTemplate = jest.fn().mockImplementation((templatePath, outputPath, templateData) => {
|
|
30
|
-
/* DO nothing*/
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
await expect(generateIDLInterfaces([ds], outDir, renderTemplate)).resolves.not.toThrow();
|
|
34
|
-
|
|
35
|
-
expect(renderTemplate).toHaveBeenCalledWith(
|
|
36
|
-
path.resolve(__dirname, '../../templates/idl.ts.ejs'),
|
|
37
|
-
path.join(outDir, '/src/types/handler-inputs/jupiter.ts'),
|
|
38
|
-
{
|
|
39
|
-
props: {
|
|
40
|
-
name: 'jupiter',
|
|
41
|
-
instructions: [
|
|
42
|
-
'Claim',
|
|
43
|
-
'ClaimToken',
|
|
44
|
-
'CloseToken',
|
|
45
|
-
'CreateOpenOrders',
|
|
46
|
-
'CreateProgramOpenOrders',
|
|
47
|
-
'CreateTokenLedger',
|
|
48
|
-
'CreateTokenAccount',
|
|
49
|
-
'ExactOutRoute',
|
|
50
|
-
'Route',
|
|
51
|
-
'RouteWithTokenLedger',
|
|
52
|
-
'SetTokenLedger',
|
|
53
|
-
'SharedAccountsExactOutRoute',
|
|
54
|
-
'SharedAccountsRoute',
|
|
55
|
-
'SharedAccountsRouteWithTokenLedger',
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
}
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('validating discriminators agains IDLs', () => {
|
|
63
|
-
it('can validate that a discriminator is part of an IDL', async () => {
|
|
64
|
-
const ds: SubqlRuntimeDatasource = {
|
|
65
|
-
kind: SolanaDatasourceKind.Runtime,
|
|
66
|
-
startBlock: 0,
|
|
67
|
-
assets: new Map([
|
|
68
|
-
[
|
|
69
|
-
'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
70
|
-
{file: '../../../../node/test/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.idl.json'},
|
|
71
|
-
],
|
|
72
|
-
]),
|
|
73
|
-
mapping: {
|
|
74
|
-
file: '',
|
|
75
|
-
handlers: [
|
|
76
|
-
{
|
|
77
|
-
handler: 'handleClaimToken',
|
|
78
|
-
kind: SolanaHandlerKind.Instruction,
|
|
79
|
-
filter: {
|
|
80
|
-
programId: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
81
|
-
discriminator: 'ClaimToken',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
await expect(validateDiscriminators([ds], outDir)).resolves.not.toThrow();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('throws when an idl does not include the discriminator', async () => {
|
|
92
|
-
const ds: SubqlRuntimeDatasource = {
|
|
93
|
-
kind: SolanaDatasourceKind.Runtime,
|
|
94
|
-
startBlock: 0,
|
|
95
|
-
assets: new Map([
|
|
96
|
-
[
|
|
97
|
-
'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
98
|
-
{file: '../../../../node/test/JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.idl.json'},
|
|
99
|
-
],
|
|
100
|
-
]),
|
|
101
|
-
mapping: {
|
|
102
|
-
file: '',
|
|
103
|
-
handlers: [
|
|
104
|
-
{
|
|
105
|
-
handler: 'handleClaimToken',
|
|
106
|
-
kind: SolanaHandlerKind.Instruction,
|
|
107
|
-
filter: {
|
|
108
|
-
programId: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
109
|
-
discriminator: 'InvalidDiscriminator',
|
|
110
|
-
},
|
|
111
|
-
},
|
|
112
|
-
],
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
await expect(validateDiscriminators([ds], outDir)).rejects.toThrow(
|
|
117
|
-
'Datasource has a handler with a discriminator InvalidDiscriminator but no matching instruction found in IDL'
|
|
118
|
-
);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('throws when there is no IDL', async () => {
|
|
122
|
-
const ds: SubqlRuntimeDatasource = {
|
|
123
|
-
kind: SolanaDatasourceKind.Runtime,
|
|
124
|
-
startBlock: 0,
|
|
125
|
-
mapping: {
|
|
126
|
-
file: '',
|
|
127
|
-
handlers: [
|
|
128
|
-
{
|
|
129
|
-
handler: 'handleClaimToken',
|
|
130
|
-
kind: SolanaHandlerKind.Instruction,
|
|
131
|
-
filter: {
|
|
132
|
-
programId: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',
|
|
133
|
-
discriminator: 'ClaimToken',
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
],
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
await expect(validateDiscriminators([ds], outDir)).rejects.toThrow(
|
|
141
|
-
'Datasource has a handler with a discriminator but no IDL file reference'
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
package/src/codegen/codegen.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import assert from 'node:assert';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import {renderVisitor} from '@codama/renderers-js';
|
|
7
|
-
import {SolanaDatasourceKind, SolanaHandlerKind, SubqlDatasource, SubqlRuntimeDatasource} from '@subql/types-solana';
|
|
8
|
-
import {pascalCase} from 'codama';
|
|
9
|
-
import {Data} from 'ejs';
|
|
10
|
-
import {parseIdlFromFile, findInstructionDiscriminatorByName} from './idl';
|
|
11
|
-
|
|
12
|
-
const INSTRUCTION_TEMPLATE_TS = path.resolve(__dirname, '../../templates/idl.ts.ejs');
|
|
13
|
-
const CODAMA_PATH = '/src/types/program-interfaces';
|
|
14
|
-
const IDL_PATH = '/src/types/handler-inputs';
|
|
15
|
-
|
|
16
|
-
export async function validateDiscriminators(
|
|
17
|
-
dataSources: SubqlRuntimeDatasource[],
|
|
18
|
-
projectPath: string
|
|
19
|
-
): Promise<void> {
|
|
20
|
-
const issues: string[] = [];
|
|
21
|
-
|
|
22
|
-
for (const ds of dataSources) {
|
|
23
|
-
for (const handler of ds.mapping.handlers) {
|
|
24
|
-
if (!handler.filter) {
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
switch (handler.kind) {
|
|
28
|
-
case SolanaHandlerKind.Instruction: {
|
|
29
|
-
if (handler.filter.discriminator && handler.filter.programId) {
|
|
30
|
-
const fileRef = ds.assets?.get(handler.filter.programId);
|
|
31
|
-
if (!fileRef) {
|
|
32
|
-
issues.push(`Datasource has a handler with a discriminator but no IDL file reference`);
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const idlPath = path.join(projectPath, fileRef.file);
|
|
37
|
-
const idl = await parseIdlFromFile(idlPath);
|
|
38
|
-
|
|
39
|
-
if (!findInstructionDiscriminatorByName(idl.getRoot(), handler.filter.discriminator)) {
|
|
40
|
-
issues.push(
|
|
41
|
-
`Datasource has a handler with a discriminator ${handler.filter.discriminator} but no matching instruction found in IDL`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
// TODO check discriminator in IDL
|
|
45
|
-
}
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
default:
|
|
49
|
-
/* Do nothing, there is nothing extra to validate*/
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
assert(issues.length === 0, issues.join('\n'));
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function generateIDLInterfaces(
|
|
58
|
-
dataSources: SubqlDatasource[],
|
|
59
|
-
projectPath: string,
|
|
60
|
-
renderTemplate: (templatePath: string, outputPath: string, templateData: Data) => Promise<void>
|
|
61
|
-
): Promise<void> {
|
|
62
|
-
// @subql/cli package calls this function with datasources as an array of objects
|
|
63
|
-
dataSources = dataSources.map((d) => ({
|
|
64
|
-
...d,
|
|
65
|
-
assets: d?.assets ? (d.assets instanceof Map ? d.assets : new Map(Object.entries(d.assets))) : undefined,
|
|
66
|
-
})) as SubqlRuntimeDatasource[];
|
|
67
|
-
|
|
68
|
-
const allAssets = new Map(
|
|
69
|
-
dataSources.filter((ds) => ds.assets !== undefined).flatMap((ds) => Array.from(ds.assets!.entries()))
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
await validateDiscriminators(
|
|
73
|
-
dataSources.filter((ds) => ds.kind === SolanaDatasourceKind.Runtime) as SubqlRuntimeDatasource[],
|
|
74
|
-
projectPath
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
for (const [name, fileRef] of allAssets) {
|
|
78
|
-
const idlPath = path.join(projectPath, fileRef.file);
|
|
79
|
-
const idl = await parseIdlFromFile(idlPath);
|
|
80
|
-
|
|
81
|
-
const output = path.join(projectPath, CODAMA_PATH, name);
|
|
82
|
-
|
|
83
|
-
await idl.accept(renderVisitor(output, {}));
|
|
84
|
-
|
|
85
|
-
const instructionNames = idl.getRoot().program.instructions.map((inst) => pascalCase(inst.name));
|
|
86
|
-
|
|
87
|
-
await renderTemplate(INSTRUCTION_TEMPLATE_TS, path.join(projectPath, IDL_PATH, `${name}.ts`), {
|
|
88
|
-
props: {
|
|
89
|
-
name,
|
|
90
|
-
instructions: instructionNames,
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
console.log(`* IDL ${name} generated`);
|
|
95
|
-
}
|
|
96
|
-
}
|
package/src/codegen/idl.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import {AnchorIdl, IdlV01, rootNodeFromAnchor} from '@codama/nodes-from-anchor';
|
|
6
|
-
import {getBase16Encoder, getBase58Encoder, getBase64Encoder, getUtf8Encoder} from '@solana/codecs-strings';
|
|
7
|
-
import {BytesValueNode, InstructionNode, RootNode, Codama, createFromRoot, camelCase} from 'codama';
|
|
8
|
-
|
|
9
|
-
export type Idl = AnchorIdl | RootNode;
|
|
10
|
-
|
|
11
|
-
export function isAnchorIdl(idl: Idl): idl is AnchorIdl {
|
|
12
|
-
return !isRootNode(idl);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isAnchorIdlV01(idl: Idl): idl is IdlV01 {
|
|
16
|
-
return isAnchorIdl(idl) && (idl.metadata as {spec?: string}).spec === '0.1.0';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isRootNode(idl: Idl): idl is RootNode {
|
|
20
|
-
return !!(idl as RootNode).program?.publicKey;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function parseIdl(idl: Idl): Codama {
|
|
24
|
-
let codama = createFromRoot(rootNodeFromAnchor(idl as AnchorIdl));
|
|
25
|
-
// Check if the idl was an anchor idl
|
|
26
|
-
if (codama.getRoot().program.publicKey === '') {
|
|
27
|
-
codama = createFromRoot(idl as RootNode);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return codama;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export async function parseIdlFromFile(path: string): Promise<Codama> {
|
|
34
|
-
const idlStr = await fs.promises.readFile(path, 'utf-8');
|
|
35
|
-
const idlJSON = JSON.parse(idlStr);
|
|
36
|
-
|
|
37
|
-
return parseIdl(idlJSON);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function getBytesFromBytesValueNode(node: BytesValueNode): Uint8Array {
|
|
41
|
-
switch (node.encoding) {
|
|
42
|
-
case 'utf8':
|
|
43
|
-
return getUtf8Encoder().encode(node.data) as Uint8Array;
|
|
44
|
-
case 'base16':
|
|
45
|
-
return getBase16Encoder().encode(node.data) as Uint8Array;
|
|
46
|
-
case 'base58':
|
|
47
|
-
return getBase58Encoder().encode(node.data) as Uint8Array;
|
|
48
|
-
case 'base64':
|
|
49
|
-
default:
|
|
50
|
-
return getBase64Encoder().encode(node.data) as Uint8Array;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function getInstructionDiscriminatorBytes(node: InstructionNode): Buffer {
|
|
55
|
-
const discArg = node.arguments.find((arg) => arg.name === 'discriminator');
|
|
56
|
-
if (!discArg) {
|
|
57
|
-
throw new Error(`Instruction ${node.name} does not have a discriminator`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// TODO what about other types of discriminators or ones that are larger than 1 byte?
|
|
61
|
-
switch (discArg.defaultValue?.kind) {
|
|
62
|
-
case 'numberValueNode':
|
|
63
|
-
return Buffer.from([discArg.defaultValue.number]);
|
|
64
|
-
case 'bytesValueNode':
|
|
65
|
-
return Buffer.from(getBytesFromBytesValueNode(discArg.defaultValue));
|
|
66
|
-
case undefined:
|
|
67
|
-
break;
|
|
68
|
-
default:
|
|
69
|
-
throw new Error(`Unable to handle unknown discriminator type ${discArg.defaultValue?.kind}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
throw new Error(`Unable to find discriminator for instruction ${node.name}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function findInstructionDiscriminatorByName(rootNode: RootNode, name: string): Buffer | undefined {
|
|
76
|
-
const inst = rootNode.program.instructions.find((inst) => inst.name === name || inst.name === camelCase(name));
|
|
77
|
-
if (!inst) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
return getInstructionDiscriminatorBytes(inst);
|
|
83
|
-
} catch (e) {
|
|
84
|
-
// logger.debug(
|
|
85
|
-
// `Failed to get discriminator for instruction ${inst.name}: ${e}`,
|
|
86
|
-
// );
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
}
|
package/src/codegen/index.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
export * from './codegen';
|
|
5
|
-
export * from './project';
|
|
6
|
-
|
|
7
|
-
import {SolanaNetworkModule} from '@subql/types-solana';
|
|
8
|
-
import * as c from './codegen';
|
|
9
|
-
import * as p from './project';
|
|
10
|
-
|
|
11
|
-
// This provides a compiled time check to ensure that the correct exports are provided
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
13
|
-
const _ = {
|
|
14
|
-
...p,
|
|
15
|
-
...c,
|
|
16
|
-
} satisfies SolanaNetworkModule;
|
package/src/project/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
export * from './load';
|
|
5
|
-
export * from './models';
|
|
6
|
-
export * from './types';
|
|
7
|
-
export * from './utils';
|
|
8
|
-
export * from './versioned';
|
|
9
|
-
|
|
10
|
-
import { parseSolanaProjectManifest } from './load';
|
|
11
|
-
export { parseSolanaProjectManifest as parseProjectManifest };
|
package/src/project/load.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import { SolanaProjectManifestVersioned, VersionedProjectManifest } from './versioned';
|
|
5
|
-
|
|
6
|
-
export function parseSolanaProjectManifest(raw: unknown): SolanaProjectManifestVersioned {
|
|
7
|
-
const projectManifest = new SolanaProjectManifestVersioned(raw as VersionedProjectManifest);
|
|
8
|
-
projectManifest.validate();
|
|
9
|
-
return projectManifest;
|
|
10
|
-
}
|
package/src/project/models.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import {BaseDataSource, BlockFilterImpl, forbidNonWhitelisted} from '@subql/common';
|
|
5
|
-
import {FileReference} from '@subql/types-core';
|
|
6
|
-
import {
|
|
7
|
-
SolanaHandlerKind,
|
|
8
|
-
SolanaDatasourceKind,
|
|
9
|
-
SolanaLogFilter,
|
|
10
|
-
SubqlCustomHandler,
|
|
11
|
-
SubqlMapping,
|
|
12
|
-
SubqlHandler,
|
|
13
|
-
SubqlRuntimeHandler,
|
|
14
|
-
SubqlRuntimeDatasource,
|
|
15
|
-
SubqlCustomDatasource,
|
|
16
|
-
SubqlBlockHandler,
|
|
17
|
-
SubqlTransactionHandler,
|
|
18
|
-
SolanaTransactionFilter,
|
|
19
|
-
SolanaBlockFilter,
|
|
20
|
-
SolanaInstructionFilter,
|
|
21
|
-
SubqlInstructionHandler,
|
|
22
|
-
SubqlLogHandler,
|
|
23
|
-
InstructionAccountFilter,
|
|
24
|
-
} from '@subql/types-solana';
|
|
25
|
-
import {plainToClass, Transform, Type} from 'class-transformer';
|
|
26
|
-
import {
|
|
27
|
-
IsArray,
|
|
28
|
-
IsEnum,
|
|
29
|
-
IsOptional,
|
|
30
|
-
IsString,
|
|
31
|
-
IsObject,
|
|
32
|
-
ValidateNested,
|
|
33
|
-
ValidateIf,
|
|
34
|
-
IsDefined,
|
|
35
|
-
IsBoolean,
|
|
36
|
-
} from 'class-validator';
|
|
37
|
-
|
|
38
|
-
export class BlockFilter extends BlockFilterImpl implements SolanaBlockFilter {}
|
|
39
|
-
|
|
40
|
-
export class TransactionFilter implements SolanaTransactionFilter {
|
|
41
|
-
@IsOptional()
|
|
42
|
-
@IsString()
|
|
43
|
-
signerAccountKey?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class InstructionFilter implements SolanaInstructionFilter {
|
|
47
|
-
@ValidateIf((o) => o.discriminator !== undefined)
|
|
48
|
-
@IsDefined({message: 'programId is required if discriminator is set'})
|
|
49
|
-
@IsString()
|
|
50
|
-
programId?: string;
|
|
51
|
-
|
|
52
|
-
@IsOptional()
|
|
53
|
-
@IsString()
|
|
54
|
-
discriminator?: string;
|
|
55
|
-
|
|
56
|
-
@IsOptional()
|
|
57
|
-
@IsArray()
|
|
58
|
-
accounts?: [
|
|
59
|
-
InstructionAccountFilter,
|
|
60
|
-
InstructionAccountFilter?,
|
|
61
|
-
InstructionAccountFilter?,
|
|
62
|
-
InstructionAccountFilter?,
|
|
63
|
-
InstructionAccountFilter?,
|
|
64
|
-
InstructionAccountFilter?,
|
|
65
|
-
InstructionAccountFilter?,
|
|
66
|
-
InstructionAccountFilter?,
|
|
67
|
-
InstructionAccountFilter?,
|
|
68
|
-
InstructionAccountFilter?,
|
|
69
|
-
InstructionAccountFilter?,
|
|
70
|
-
InstructionAccountFilter?,
|
|
71
|
-
InstructionAccountFilter?,
|
|
72
|
-
InstructionAccountFilter?,
|
|
73
|
-
InstructionAccountFilter?,
|
|
74
|
-
InstructionAccountFilter?
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
@IsOptional()
|
|
78
|
-
@IsBoolean()
|
|
79
|
-
includeFalied?: boolean;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export class LogFilter implements SolanaLogFilter {
|
|
83
|
-
@IsOptional()
|
|
84
|
-
@IsString()
|
|
85
|
-
programId?: string;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export class BlockHandler implements SubqlBlockHandler {
|
|
89
|
-
@IsObject()
|
|
90
|
-
@IsOptional()
|
|
91
|
-
@Type(() => BlockFilter)
|
|
92
|
-
filter?: BlockFilter;
|
|
93
|
-
@IsEnum(SolanaHandlerKind, {groups: [SolanaHandlerKind.Block]})
|
|
94
|
-
kind!: SolanaHandlerKind.Block;
|
|
95
|
-
@IsString()
|
|
96
|
-
handler!: string;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export class TransactionHandler implements SubqlTransactionHandler {
|
|
100
|
-
@IsOptional()
|
|
101
|
-
@ValidateNested()
|
|
102
|
-
@Type(() => TransactionFilter)
|
|
103
|
-
filter?: SolanaTransactionFilter;
|
|
104
|
-
@IsEnum(SolanaHandlerKind, {groups: [SolanaHandlerKind.Transaction]})
|
|
105
|
-
kind!: SolanaHandlerKind.Transaction;
|
|
106
|
-
@IsString()
|
|
107
|
-
handler!: string;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export class InstructionHandler implements SubqlInstructionHandler {
|
|
111
|
-
@IsOptional()
|
|
112
|
-
@ValidateNested()
|
|
113
|
-
@Type(() => InstructionFilter)
|
|
114
|
-
filter?: SolanaInstructionFilter;
|
|
115
|
-
@IsEnum(SolanaHandlerKind, {groups: [SolanaHandlerKind.Instruction]})
|
|
116
|
-
kind!: SolanaHandlerKind.Instruction;
|
|
117
|
-
@IsString()
|
|
118
|
-
handler!: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export class LogHandler implements SubqlLogHandler {
|
|
122
|
-
@IsOptional()
|
|
123
|
-
@ValidateNested()
|
|
124
|
-
@Type(() => LogFilter)
|
|
125
|
-
filter?: SolanaLogFilter;
|
|
126
|
-
@IsEnum(SolanaHandlerKind, {groups: [SolanaHandlerKind.Log]})
|
|
127
|
-
kind!: SolanaHandlerKind.Log;
|
|
128
|
-
@IsString()
|
|
129
|
-
handler!: string;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export class CustomHandler implements SubqlCustomHandler {
|
|
133
|
-
@IsString()
|
|
134
|
-
kind!: string;
|
|
135
|
-
@IsString()
|
|
136
|
-
handler!: string;
|
|
137
|
-
@IsObject()
|
|
138
|
-
@IsOptional()
|
|
139
|
-
filter?: Record<string, unknown>;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export class SolanaMapping implements SubqlMapping {
|
|
143
|
-
@Transform((params) => {
|
|
144
|
-
const handlers: SubqlHandler[] = params.value;
|
|
145
|
-
return handlers.map((handler) => {
|
|
146
|
-
switch (handler.kind) {
|
|
147
|
-
case SolanaHandlerKind.Log:
|
|
148
|
-
return plainToClass(LogHandler, handler);
|
|
149
|
-
case SolanaHandlerKind.Instruction:
|
|
150
|
-
return plainToClass(InstructionHandler, handler);
|
|
151
|
-
case SolanaHandlerKind.Transaction:
|
|
152
|
-
return plainToClass(TransactionHandler, handler);
|
|
153
|
-
case SolanaHandlerKind.Block:
|
|
154
|
-
return plainToClass(BlockHandler, handler);
|
|
155
|
-
default:
|
|
156
|
-
throw new Error(`handler ${(handler as any).kind} not supported`);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
})
|
|
160
|
-
@IsArray()
|
|
161
|
-
@ValidateNested()
|
|
162
|
-
handlers!: SubqlHandler[];
|
|
163
|
-
@IsString()
|
|
164
|
-
file!: string;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export class CustomMapping implements SubqlMapping<SubqlCustomHandler> {
|
|
168
|
-
@IsArray()
|
|
169
|
-
@Type(() => CustomHandler)
|
|
170
|
-
@ValidateNested()
|
|
171
|
-
handlers!: CustomHandler[];
|
|
172
|
-
@IsString()
|
|
173
|
-
file!: string;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export class RuntimeDataSourceBase<M extends SubqlMapping<SubqlRuntimeHandler>>
|
|
177
|
-
extends BaseDataSource
|
|
178
|
-
implements SubqlRuntimeDatasource<M>
|
|
179
|
-
{
|
|
180
|
-
@IsEnum(SolanaDatasourceKind, {
|
|
181
|
-
groups: [SolanaDatasourceKind.Runtime],
|
|
182
|
-
})
|
|
183
|
-
kind!: SolanaDatasourceKind.Runtime;
|
|
184
|
-
@Type(() => SolanaMapping)
|
|
185
|
-
@ValidateNested()
|
|
186
|
-
mapping!: M;
|
|
187
|
-
@Type(() => FileReferenceImpl)
|
|
188
|
-
@ValidateNested({each: true})
|
|
189
|
-
@IsOptional()
|
|
190
|
-
assets?: Map<string, FileReference>;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
export class FileReferenceImpl implements FileReference {
|
|
194
|
-
@IsString()
|
|
195
|
-
file!: string;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
export class CustomDataSourceBase<K extends string, M extends SubqlMapping = SubqlMapping<SubqlCustomHandler>>
|
|
199
|
-
extends BaseDataSource
|
|
200
|
-
implements SubqlCustomDatasource<K, M>
|
|
201
|
-
{
|
|
202
|
-
@IsString()
|
|
203
|
-
kind!: K;
|
|
204
|
-
@Type(() => CustomMapping)
|
|
205
|
-
@ValidateNested()
|
|
206
|
-
mapping!: M;
|
|
207
|
-
@Type(() => FileReferenceImpl)
|
|
208
|
-
@ValidateNested({each: true})
|
|
209
|
-
assets!: Map<string, FileReference>;
|
|
210
|
-
@Type(() => FileReferenceImpl)
|
|
211
|
-
@IsObject()
|
|
212
|
-
processor!: FileReference;
|
|
213
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import {loadFromJsonOrYaml, RunnerQueryBaseModel} from '@subql/common';
|
|
7
|
-
import {validateSync} from 'class-validator';
|
|
8
|
-
import {DeploymentV1_0_0, SolanaRunnerNodeImpl, SolanaRunnerSpecsImpl} from '../project/versioned/v1_0_0';
|
|
9
|
-
import {SolanaProjectManifestVersioned, VersionedProjectManifest} from './versioned';
|
|
10
|
-
|
|
11
|
-
const projectsDir = path.join(__dirname, '../../test');
|
|
12
|
-
|
|
13
|
-
function loadSolanaProjectManifest(file: string): SolanaProjectManifestVersioned {
|
|
14
|
-
let manifestPath = file;
|
|
15
|
-
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
|
|
16
|
-
const yamlFilePath = path.join(file, 'project.yaml');
|
|
17
|
-
const jsonFilePath = path.join(file, 'project.json');
|
|
18
|
-
if (fs.existsSync(yamlFilePath)) {
|
|
19
|
-
manifestPath = yamlFilePath;
|
|
20
|
-
} else if (fs.existsSync(jsonFilePath)) {
|
|
21
|
-
manifestPath = jsonFilePath;
|
|
22
|
-
} else {
|
|
23
|
-
throw new Error(`Could not find project manifest under dir ${file}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const doc = loadFromJsonOrYaml(manifestPath);
|
|
28
|
-
const projectManifest = new SolanaProjectManifestVersioned(doc as VersionedProjectManifest);
|
|
29
|
-
projectManifest.validate();
|
|
30
|
-
return projectManifest;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe('test solana project.yaml', () => {
|
|
34
|
-
it('could get solana project template name from its deployment', () => {
|
|
35
|
-
const manifest = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
|
|
36
|
-
const deployment = manifest.toDeployment();
|
|
37
|
-
expect(deployment).toContain('name: Pool');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('could get options in template from its deployment', () => {
|
|
41
|
-
const manifest = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
|
|
42
|
-
const deployment = manifest.toDeployment();
|
|
43
|
-
expect(deployment).toContain('abi: Pool');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('project.yaml', () => {
|
|
48
|
-
it('can validate project.yaml', () => {
|
|
49
|
-
// TODO this should catch a specific error, the file doesn't currently exist
|
|
50
|
-
throw new Error('Not implemented');
|
|
51
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_falsy.yaml'))).toThrow();
|
|
52
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_falsy_array.yaml'))).toThrow();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('can fail validation if version not supported', () => {
|
|
56
|
-
// TODO this should catch a specific error, the file doesn't currently exist
|
|
57
|
-
throw new Error('Not implemented');
|
|
58
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_invalid_version.yaml'))).toThrow('');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('can validate a v1.0.0 project.yaml with templates', () => {
|
|
62
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'))).not.toThrow();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('get v1.0.0 deployment mapping filter', () => {
|
|
66
|
-
const manifestVersioned = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
|
|
67
|
-
|
|
68
|
-
const deployment = manifestVersioned.asV1_0_0.deployment;
|
|
69
|
-
const filter = deployment.dataSources[0].mapping.handlers[0].filter;
|
|
70
|
-
const deploymentString = manifestVersioned.toDeployment();
|
|
71
|
-
expect(filter).not.toBeNull();
|
|
72
|
-
expect(deploymentString).toContain('Transfer (address from, address to, uint256 tokenId)');
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('can convert genesis hash in v1.0.0 to chainId in deployment', () => {
|
|
76
|
-
const deployment = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml')).asV1_0_0.deployment;
|
|
77
|
-
expect(deployment.network.chainId).not.toBeNull();
|
|
78
|
-
console.log(deployment.network.chainId);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it.skip('can get chainId for deployment', () => {
|
|
82
|
-
const deployment = loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0_chainId.yaml')).asV1_0_0
|
|
83
|
-
.deployment;
|
|
84
|
-
expect(deployment.network.chainId).toBe('moonbeamChainId');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('can validate deployment runner versions', () => {
|
|
88
|
-
const deployment = new DeploymentV1_0_0();
|
|
89
|
-
const nodeImp = new SolanaRunnerNodeImpl();
|
|
90
|
-
const queryImp = new RunnerQueryBaseModel();
|
|
91
|
-
deployment.specVersion = '1.0.0';
|
|
92
|
-
deployment.runner = new SolanaRunnerSpecsImpl();
|
|
93
|
-
|
|
94
|
-
nodeImp.name = '@subql/node-solana';
|
|
95
|
-
nodeImp.version = '*';
|
|
96
|
-
deployment.runner.node = nodeImp;
|
|
97
|
-
|
|
98
|
-
queryImp.name = '@subql/query';
|
|
99
|
-
queryImp.version = '0.213.1';
|
|
100
|
-
|
|
101
|
-
deployment.runner.query = queryImp;
|
|
102
|
-
|
|
103
|
-
const errors = validateSync(deployment.runner, {whitelist: true, forbidNonWhitelisted: true});
|
|
104
|
-
expect(errors.length).toBe(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('can validate a v1.0.0 project.yaml with unsupported runner node', () => {
|
|
108
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0_bad_runner.yaml'))).toThrow();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
//TODO, pre-release should be excluded
|
|
112
|
-
it.skip('can throw error with unsupported runner version', () => {
|
|
113
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0_bad_runner_version.yaml'))).toThrow();
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('can validate a v1.0.0 project.yaml runner and datasource mismatches', () => {
|
|
117
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_1.0.0_runner_ds_mismatch.yaml'))).toThrow();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('can fail validation if custom ds missing processor', () => {
|
|
121
|
-
expect(() => loadSolanaProjectManifest(path.join(projectsDir, 'project_0.2.0_invalid_custom_ds.yaml'))).toThrow();
|
|
122
|
-
});
|
|
123
|
-
});
|
package/src/project/types.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import { IProjectManifest } from '@subql/types-core';
|
|
5
|
-
import { SubqlDatasource } from '@subql/types-solana';
|
|
6
|
-
|
|
7
|
-
// All of these used to be redefined in this file, re-exporting for simplicity
|
|
8
|
-
export {
|
|
9
|
-
SubqlRuntimeHandler,
|
|
10
|
-
SubqlCustomHandler,
|
|
11
|
-
SubqlHandler,
|
|
12
|
-
SubqlDatasource as SubqlSolanaDataSource,
|
|
13
|
-
SubqlCustomDatasource as SubqlSolanaCustomDataSource,
|
|
14
|
-
SubqlDatasourceProcessor,
|
|
15
|
-
SubqlHandlerFilter,
|
|
16
|
-
} from '@subql/types-solana';
|
|
17
|
-
|
|
18
|
-
export type ISolanaProjectManifest = IProjectManifest<SubqlDatasource>;
|
|
19
|
-
|
package/src/project/utils.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
SecondLayerHandlerProcessor,
|
|
6
|
-
SubqlCustomDatasource,
|
|
7
|
-
SubqlDatasource,
|
|
8
|
-
SolanaDatasourceKind,
|
|
9
|
-
SolanaHandlerKind,
|
|
10
|
-
SubqlRuntimeDatasource,
|
|
11
|
-
SecondLayerHandlerProcessorArray,
|
|
12
|
-
SubqlCustomHandler,
|
|
13
|
-
SubqlMapping,
|
|
14
|
-
} from '@subql/types-solana';
|
|
15
|
-
|
|
16
|
-
type DefaultFilter = Record<string, unknown>;
|
|
17
|
-
|
|
18
|
-
export function isBlockHandlerProcessor<E>(
|
|
19
|
-
hp: SecondLayerHandlerProcessorArray<SolanaHandlerKind, DefaultFilter, unknown>
|
|
20
|
-
): hp is SecondLayerHandlerProcessor<SolanaHandlerKind.Block, DefaultFilter, E> {
|
|
21
|
-
return hp.baseHandlerKind === SolanaHandlerKind.Block;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function isTransactionHandlerProcessor<E>(
|
|
25
|
-
hp: SecondLayerHandlerProcessorArray<SolanaHandlerKind, DefaultFilter, unknown>
|
|
26
|
-
): hp is SecondLayerHandlerProcessor<SolanaHandlerKind.Transaction, DefaultFilter, E> {
|
|
27
|
-
return hp.baseHandlerKind === SolanaHandlerKind.Transaction;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isInstructionHandlerProcessor<E>(
|
|
31
|
-
hp: SecondLayerHandlerProcessorArray<SolanaHandlerKind, DefaultFilter, unknown>
|
|
32
|
-
): hp is SecondLayerHandlerProcessor<SolanaHandlerKind.Instruction, DefaultFilter, E> {
|
|
33
|
-
return hp.baseHandlerKind === SolanaHandlerKind.Instruction;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function isLogHandlerProcessor<E>(
|
|
37
|
-
hp: SecondLayerHandlerProcessorArray<SolanaHandlerKind, DefaultFilter, unknown>
|
|
38
|
-
): hp is SecondLayerHandlerProcessor<SolanaHandlerKind.Log, DefaultFilter, E> {
|
|
39
|
-
return hp.baseHandlerKind === SolanaHandlerKind.Log;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function isCustomDs<F extends SubqlMapping<SubqlCustomHandler>>(
|
|
43
|
-
ds: SubqlDatasource
|
|
44
|
-
): ds is SubqlCustomDatasource<string, F> {
|
|
45
|
-
return ds.kind !== SolanaDatasourceKind.Runtime && !!(ds as SubqlCustomDatasource<string>).processor;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function isRuntimeDs(ds: SubqlDatasource): ds is SubqlRuntimeDatasource {
|
|
49
|
-
return ds.kind === SolanaDatasourceKind.Runtime;
|
|
50
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import { plainToClass } from 'class-transformer';
|
|
5
|
-
import { ISolanaProjectManifest, SubqlSolanaDataSource } from '../types';
|
|
6
|
-
import { ProjectManifestV1_0_0Impl } from './v1_0_0';
|
|
7
|
-
export type VersionedProjectManifest = { specVersion: string };
|
|
8
|
-
|
|
9
|
-
const SOLANA_SUPPORTED_VERSIONS = {
|
|
10
|
-
'1.0.0': ProjectManifestV1_0_0Impl,
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
type Versions = keyof typeof SOLANA_SUPPORTED_VERSIONS;
|
|
14
|
-
|
|
15
|
-
type ProjectManifestImpls = InstanceType<(typeof SOLANA_SUPPORTED_VERSIONS)[Versions]>;
|
|
16
|
-
|
|
17
|
-
export function manifestIsV1_0_0(manifest: ISolanaProjectManifest): manifest is ProjectManifestV1_0_0Impl {
|
|
18
|
-
return manifest.specVersion === '1.0.0';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class SolanaProjectManifestVersioned implements ISolanaProjectManifest {
|
|
22
|
-
private _impl: ProjectManifestImpls;
|
|
23
|
-
|
|
24
|
-
constructor(projectManifest: VersionedProjectManifest) {
|
|
25
|
-
const klass = SOLANA_SUPPORTED_VERSIONS[projectManifest.specVersion as Versions];
|
|
26
|
-
if (!klass) {
|
|
27
|
-
throw new Error('specVersion not supported for project manifest file');
|
|
28
|
-
}
|
|
29
|
-
this._impl = plainToClass<ProjectManifestImpls, VersionedProjectManifest>(klass, projectManifest);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get asImpl(): ProjectManifestImpls {
|
|
33
|
-
return this._impl;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
get isV1_0_0(): boolean {
|
|
37
|
-
return this.specVersion === '1.0.0';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
get asV1_0_0(): ProjectManifestV1_0_0Impl {
|
|
41
|
-
return this._impl as ProjectManifestV1_0_0Impl;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
toDeployment(): string {
|
|
45
|
-
return this._impl.deployment.toYaml();
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
validate(): void {
|
|
49
|
-
return this._impl.validate();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get dataSources(): SubqlSolanaDataSource[] {
|
|
53
|
-
return this._impl.dataSources;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get schema(): string {
|
|
57
|
-
return this._impl.schema.file;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get specVersion(): string {
|
|
61
|
-
return this._impl.specVersion;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get description(): string | undefined {
|
|
65
|
-
return this._impl.description;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get repository(): string | undefined {
|
|
69
|
-
return this._impl.repository;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
2
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
BaseDeploymentV1_0_0,
|
|
6
|
-
CommonEndpointConfig,
|
|
7
|
-
CommonProjectNetworkV1_0_0,
|
|
8
|
-
FileType,
|
|
9
|
-
IsNetworkEndpoint,
|
|
10
|
-
ParentProjectModel,
|
|
11
|
-
ProjectManifestBaseImpl,
|
|
12
|
-
RunnerNodeImpl,
|
|
13
|
-
RunnerQueryBaseModel,
|
|
14
|
-
} from '@subql/common';
|
|
15
|
-
import { BaseMapping, NodeSpec, RunnerSpecs, QuerySpec, ParentProject } from '@subql/types-core';
|
|
16
|
-
import {
|
|
17
|
-
CustomDatasourceTemplate,
|
|
18
|
-
SolanaProjectManifestV1_0_0,
|
|
19
|
-
ISolanaEndpointConfig,
|
|
20
|
-
RuntimeDatasourceTemplate,
|
|
21
|
-
SubqlCustomDatasource,
|
|
22
|
-
SubqlMapping,
|
|
23
|
-
SubqlRuntimeDatasource,
|
|
24
|
-
} from '@subql/types-solana';
|
|
25
|
-
import { plainToInstance, Transform, TransformFnParams, Type } from 'class-transformer';
|
|
26
|
-
import {
|
|
27
|
-
Equals,
|
|
28
|
-
IsArray,
|
|
29
|
-
IsIn,
|
|
30
|
-
IsNotEmpty,
|
|
31
|
-
IsObject,
|
|
32
|
-
IsOptional,
|
|
33
|
-
IsPositive,
|
|
34
|
-
IsString,
|
|
35
|
-
ValidateNested,
|
|
36
|
-
validateSync,
|
|
37
|
-
} from 'class-validator';
|
|
38
|
-
import { CustomDataSourceBase, RuntimeDataSourceBase } from '../../models';
|
|
39
|
-
import { SubqlSolanaDataSource, SubqlRuntimeHandler } from '../../types';
|
|
40
|
-
|
|
41
|
-
const SOLANA_NODE_NAME = `@subql/node-solana`;
|
|
42
|
-
|
|
43
|
-
export class SolanaRunnerNodeImpl extends RunnerNodeImpl {
|
|
44
|
-
@IsIn([SOLANA_NODE_NAME], {
|
|
45
|
-
message: `Runner Solana node name is incorrect, supposed to be '${SOLANA_NODE_NAME}'`,
|
|
46
|
-
})
|
|
47
|
-
name: string = SOLANA_NODE_NAME;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function validateObject(object: any, errorMessage = 'failed to validate object.'): void {
|
|
51
|
-
const errors = validateSync(object, { whitelist: true, forbidNonWhitelisted: true });
|
|
52
|
-
if (errors?.length) {
|
|
53
|
-
const errorMsgs = errors.map((e) => e.toString()).join('\n');
|
|
54
|
-
throw new Error(`${errorMessage}\n${errorMsgs}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export class SolanaRuntimeDataSourceImpl
|
|
59
|
-
extends RuntimeDataSourceBase<SubqlMapping<SubqlRuntimeHandler>>
|
|
60
|
-
implements SubqlRuntimeDatasource {
|
|
61
|
-
validate(): void {
|
|
62
|
-
return validateObject(this, 'failed to validate runtime datasource.');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export class SolanaCustomDataSourceImpl<K extends string = string, M extends BaseMapping<any> = BaseMapping<any>>
|
|
67
|
-
extends CustomDataSourceBase<K, M>
|
|
68
|
-
implements SubqlCustomDatasource<K, M> {
|
|
69
|
-
validate(): void {
|
|
70
|
-
return validateObject(this, 'failed to validate custom datasource.');
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class RuntimeDatasourceTemplateImpl extends SolanaRuntimeDataSourceImpl implements RuntimeDatasourceTemplate {
|
|
75
|
-
@IsString()
|
|
76
|
-
name!: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export class CustomDatasourceTemplateImpl extends SolanaCustomDataSourceImpl implements CustomDatasourceTemplate {
|
|
80
|
-
@IsString()
|
|
81
|
-
name!: string;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export class SolanaRunnerSpecsImpl implements RunnerSpecs {
|
|
85
|
-
@IsObject()
|
|
86
|
-
@ValidateNested()
|
|
87
|
-
@Type(() => SolanaRunnerNodeImpl)
|
|
88
|
-
node!: NodeSpec;
|
|
89
|
-
@IsObject()
|
|
90
|
-
@ValidateNested()
|
|
91
|
-
@Type(() => RunnerQueryBaseModel)
|
|
92
|
-
query!: QuerySpec;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export class ProjectNetworkDeploymentV1_0_0 {
|
|
96
|
-
@IsNotEmpty()
|
|
97
|
-
@Transform(({ value }: TransformFnParams) => value.trim())
|
|
98
|
-
@IsString()
|
|
99
|
-
chainId!: string;
|
|
100
|
-
@IsOptional()
|
|
101
|
-
@IsArray()
|
|
102
|
-
bypassBlocks?: (number | `${number}-${number}`)[];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export class SolanaEndpointConfig extends CommonEndpointConfig implements ISolanaEndpointConfig {
|
|
106
|
-
@IsOptional()
|
|
107
|
-
@IsPositive()
|
|
108
|
-
batchSize?: number;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export class ProjectNetworkV1_0_0 extends CommonProjectNetworkV1_0_0<void> {
|
|
112
|
-
@IsOptional()
|
|
113
|
-
@IsNetworkEndpoint(SolanaEndpointConfig)
|
|
114
|
-
endpoint: string | string[] | Record<string, CommonEndpointConfig> = {};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export class DeploymentV1_0_0 extends BaseDeploymentV1_0_0 {
|
|
118
|
-
@Transform((params) => {
|
|
119
|
-
if (params.value.genesisHash && !params.value.chainId) {
|
|
120
|
-
params.value.chainId = params.value.genesisHash;
|
|
121
|
-
}
|
|
122
|
-
return plainToInstance(ProjectNetworkDeploymentV1_0_0, params.value);
|
|
123
|
-
})
|
|
124
|
-
@ValidateNested()
|
|
125
|
-
@Type(() => ProjectNetworkDeploymentV1_0_0)
|
|
126
|
-
network!: ProjectNetworkDeploymentV1_0_0;
|
|
127
|
-
@IsObject()
|
|
128
|
-
@ValidateNested()
|
|
129
|
-
@Type(() => SolanaRunnerSpecsImpl)
|
|
130
|
-
runner!: RunnerSpecs;
|
|
131
|
-
@IsArray()
|
|
132
|
-
@ValidateNested()
|
|
133
|
-
@Type(() => SolanaCustomDataSourceImpl, {
|
|
134
|
-
discriminator: {
|
|
135
|
-
property: 'kind',
|
|
136
|
-
subTypes: [{ value: SolanaRuntimeDataSourceImpl, name: 'solana/Runtime' }],
|
|
137
|
-
},
|
|
138
|
-
keepDiscriminatorProperty: true,
|
|
139
|
-
})
|
|
140
|
-
dataSources!: (SubqlRuntimeDatasource | SubqlCustomDatasource)[];
|
|
141
|
-
@IsOptional()
|
|
142
|
-
@IsArray()
|
|
143
|
-
@ValidateNested()
|
|
144
|
-
@Type(() => CustomDatasourceTemplateImpl, {
|
|
145
|
-
discriminator: {
|
|
146
|
-
property: 'kind',
|
|
147
|
-
subTypes: [{ value: RuntimeDatasourceTemplateImpl, name: 'solana/Runtime' }],
|
|
148
|
-
},
|
|
149
|
-
keepDiscriminatorProperty: true,
|
|
150
|
-
})
|
|
151
|
-
templates?: (RuntimeDatasourceTemplate | CustomDatasourceTemplate)[];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export class ProjectManifestV1_0_0Impl
|
|
155
|
-
extends ProjectManifestBaseImpl<DeploymentV1_0_0>
|
|
156
|
-
implements SolanaProjectManifestV1_0_0 {
|
|
157
|
-
constructor() {
|
|
158
|
-
super(DeploymentV1_0_0);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
@Equals('1.0.0')
|
|
162
|
-
specVersion = '1.0.0';
|
|
163
|
-
@Type(() => SolanaCustomDataSourceImpl, {
|
|
164
|
-
discriminator: {
|
|
165
|
-
property: 'kind',
|
|
166
|
-
subTypes: [{ value: SolanaRuntimeDataSourceImpl, name: 'solana/Runtime' }],
|
|
167
|
-
},
|
|
168
|
-
keepDiscriminatorProperty: true,
|
|
169
|
-
})
|
|
170
|
-
dataSources!: SubqlSolanaDataSource[];
|
|
171
|
-
@Type(() => ProjectNetworkV1_0_0)
|
|
172
|
-
network!: ProjectNetworkV1_0_0;
|
|
173
|
-
@IsString()
|
|
174
|
-
name!: string;
|
|
175
|
-
@IsString()
|
|
176
|
-
version!: string;
|
|
177
|
-
@ValidateNested()
|
|
178
|
-
@Type(() => FileType)
|
|
179
|
-
schema!: FileType;
|
|
180
|
-
@IsOptional()
|
|
181
|
-
@IsArray()
|
|
182
|
-
@ValidateNested()
|
|
183
|
-
@Type(() => CustomDatasourceTemplateImpl, {
|
|
184
|
-
discriminator: {
|
|
185
|
-
property: 'kind',
|
|
186
|
-
subTypes: [{ value: RuntimeDatasourceTemplateImpl, name: 'solana/Runtime' }],
|
|
187
|
-
},
|
|
188
|
-
keepDiscriminatorProperty: true,
|
|
189
|
-
})
|
|
190
|
-
templates?: (RuntimeDatasourceTemplate | CustomDatasourceTemplate)[];
|
|
191
|
-
@IsObject()
|
|
192
|
-
@ValidateNested()
|
|
193
|
-
@Type(() => SolanaRunnerSpecsImpl)
|
|
194
|
-
runner!: RunnerSpecs;
|
|
195
|
-
|
|
196
|
-
@IsOptional()
|
|
197
|
-
@IsObject()
|
|
198
|
-
@Type(() => ParentProjectModel)
|
|
199
|
-
parent?: ParentProject;
|
|
200
|
-
}
|
package/templates/idl.ts.ejs
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
-
|
|
3
|
-
// Auto-generated , DO NOT EDIT
|
|
4
|
-
import { SolanaInstruction, SolanaLogMessage } from '@subql/types-solana';
|
|
5
|
-
|
|
6
|
-
import {<% props.instructions.forEach(function(inst){ %><%= inst %>InstructionDataArgs, <% }); %>} from '../program-interfaces/<%=props.name%>/instructions';
|
|
7
|
-
|
|
8
|
-
<% props.instructions.forEach(function(inst){ %>
|
|
9
|
-
export type <%=inst %>Instruction = SolanaInstruction<<%=inst%>InstructionDataArgs>;
|
|
10
|
-
<% }); %>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
specVersion: '1.0.0'
|
|
2
|
-
name: 'example'
|
|
3
|
-
|
|
4
|
-
version: '0.0.1'
|
|
5
|
-
runner:
|
|
6
|
-
node:
|
|
7
|
-
name: '@subql/node-ethereum'
|
|
8
|
-
version: '*'
|
|
9
|
-
query:
|
|
10
|
-
name: '@subql/query'
|
|
11
|
-
version: '*'
|
|
12
|
-
description: ''
|
|
13
|
-
repository: ''
|
|
14
|
-
schema:
|
|
15
|
-
file: './schema.graphql'
|
|
16
|
-
network:
|
|
17
|
-
chainId: '1'
|
|
18
|
-
endpoint: 'aaa'
|
|
19
|
-
dictionary: ''
|
|
20
|
-
|
|
21
|
-
customDs:
|
|
22
|
-
assets:
|
|
23
|
-
abi:
|
|
24
|
-
file: ./erc20.json
|
|
25
|
-
artifact:
|
|
26
|
-
file: ./abis/Erc20.sol/erc20.json
|
|
27
|
-
dataSources:
|
|
28
|
-
- kind: ethereum/Runtime
|
|
29
|
-
options:
|
|
30
|
-
abi: Erc20
|
|
31
|
-
assets:
|
|
32
|
-
Erc20:
|
|
33
|
-
file: './abis/erc20.json'
|
|
34
|
-
startBlock: 1
|
|
35
|
-
mapping:
|
|
36
|
-
file: ''
|
|
37
|
-
handlers:
|
|
38
|
-
- handler: handleTransaction
|
|
39
|
-
kind: ethereum/BlockHandler
|
|
40
|
-
- kind: ethereum/Runtime
|
|
41
|
-
options:
|
|
42
|
-
abi: 'artifactErc20'
|
|
43
|
-
assets:
|
|
44
|
-
'artifactErc20':
|
|
45
|
-
file: './abis/Erc20.sol/Erc20.json'
|
|
46
|
-
startBlock: 1
|
|
47
|
-
mapping:
|
|
48
|
-
file: ''
|
|
49
|
-
handlers:
|
|
50
|
-
- handler: handleTransaction
|
|
51
|
-
kind: ethereum/BlockHandler
|
package/test/project_1.0.0.yaml
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
specVersion: '1.0.0'
|
|
2
|
-
|
|
3
|
-
name: 'test'
|
|
4
|
-
version: '0.0.1'
|
|
5
|
-
runner:
|
|
6
|
-
node:
|
|
7
|
-
name: '@subql/node-solana'
|
|
8
|
-
version: '*'
|
|
9
|
-
query:
|
|
10
|
-
name: '@subql/query'
|
|
11
|
-
version: '*'
|
|
12
|
-
description: 'This project can be use as a starting point for developing your new Solana SubQuery project'
|
|
13
|
-
repository: 'https://github.com/subquery/solana-subql-starter'
|
|
14
|
-
|
|
15
|
-
schema:
|
|
16
|
-
file: './schema.graphql'
|
|
17
|
-
|
|
18
|
-
network:
|
|
19
|
-
# endpoint: "fill it in"
|
|
20
|
-
chainId: '1'
|
|
21
|
-
dictionary: 'https://eth-dict.api.onfinality.io/'
|
|
22
|
-
endpoint: 'wss://eth.api.onfinality.io/ws?apikey='
|
|
23
|
-
|
|
24
|
-
dataSources:
|
|
25
|
-
- kind: solana/Runtime
|
|
26
|
-
startBlock: 12369621
|
|
27
|
-
mapping:
|
|
28
|
-
file: './dist/index.js'
|
|
29
|
-
handlers:
|
|
30
|
-
- handler: handlePoolCreated
|
|
31
|
-
kind: solana/LogHandler
|
|
32
|
-
filter:
|
|
33
|
-
topics:
|
|
34
|
-
- PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool)
|
|
35
|
-
# solana/contract
|
|
36
|
-
- kind: solana/Runtime
|
|
37
|
-
startBlock: 12369651
|
|
38
|
-
mapping:
|
|
39
|
-
file: './dist/index.js'
|
|
40
|
-
handlers:
|
|
41
|
-
- handler: handleIncreaseLiquidity
|
|
42
|
-
kind: solana/LogHandler
|
|
43
|
-
filter:
|
|
44
|
-
topics:
|
|
45
|
-
- IncreaseLiquidity (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)
|
|
46
|
-
- handler: handleDecreaseLiquidity
|
|
47
|
-
kind: solana/LogHandler
|
|
48
|
-
filter:
|
|
49
|
-
topics:
|
|
50
|
-
- DecreaseLiquidity (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)
|
|
51
|
-
- handler: handleCollect
|
|
52
|
-
kind: solana/LogHandler
|
|
53
|
-
filter:
|
|
54
|
-
topics:
|
|
55
|
-
- Collect (uint256 tokenId, address recipient, uint256 amount0, uint256 amount1)
|
|
56
|
-
- handler: handleTransfer
|
|
57
|
-
kind: solana/LogHandler
|
|
58
|
-
filter:
|
|
59
|
-
topics:
|
|
60
|
-
- Transfer (address from, address to, uint256 tokenId)
|
|
61
|
-
templates:
|
|
62
|
-
- name: Pool
|
|
63
|
-
kind: solana/Runtime
|
|
64
|
-
mapping:
|
|
65
|
-
file: './dist/index.js'
|
|
66
|
-
handlers:
|
|
67
|
-
- handler: handleInitialize
|
|
68
|
-
kind: solana/LogHandler
|
|
69
|
-
filter:
|
|
70
|
-
topics:
|
|
71
|
-
- Initialize (uint160,int24)
|
|
72
|
-
- handler: handleSwap
|
|
73
|
-
kind: solana/LogHandler
|
|
74
|
-
filter:
|
|
75
|
-
topics:
|
|
76
|
-
- Swap (address sender, address recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)
|
|
77
|
-
- handler: handleMint
|
|
78
|
-
kind: solana/LogHandler
|
|
79
|
-
filter:
|
|
80
|
-
topics:
|
|
81
|
-
- Mint(address sender, address owner, int24 tickLower, int24 tickUpper, uint128 amount, uint256 amount0, uint256 amount1)
|
|
82
|
-
- handler: handleBurn
|
|
83
|
-
kind: solana/LogHandler
|
|
84
|
-
filter:
|
|
85
|
-
topics:
|
|
86
|
-
- Burn(indexed address,indexed int24,indexed int24,uint128,uint256,uint256)
|
|
87
|
-
- handler: handleFlash
|
|
88
|
-
kind: solana/LogHandler
|
|
89
|
-
filter:
|
|
90
|
-
topics:
|
|
91
|
-
- Flash(indexed address,indexed address,uint256,uint256,uint256,uint256)
|
package/tsconfig.json
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"rootDir": "src",
|
|
5
|
-
"tsBuildInfoFile": "dist/.tsbuildinfo",
|
|
6
|
-
"outDir": "dist",
|
|
7
|
-
"noImplicitAny": true,
|
|
8
|
-
"strict": true
|
|
9
|
-
},
|
|
10
|
-
"references": [{"path": "../types"}],
|
|
11
|
-
"include": ["src/**/*"]
|
|
12
|
-
}
|