Skip to content

Commit

Permalink
[SKIP CI] Merge pull request #609 from subquery/feat/add-solana
Browse files Browse the repository at this point in the history
feat: integrate solana rpc
  • Loading branch information
cool-firer authored Feb 24, 2025
2 parents 57e6218 + ca80747 commit 0c74b1b
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const RpcSetting: FC<IProps> = (props) => {
polkadot: polkadotAndSubstrateRule,
substrate: polkadotAndSubstrateRule,
subql_dict: evmAndDataNodeRule,
solana: evmAndDataNodeRule,
};
}, [form, keys.data?.getRpcEndpointKeys]);

Expand Down
2 changes: 1 addition & 1 deletion apps/indexer-coordinator/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@subql/indexer-coordinator",
"version": "2.8.4",
"version": "2.9.0",
"description": "",
"author": "SubQuery",
"license": "Apache-2.0",
Expand Down
3 changes: 2 additions & 1 deletion apps/indexer-coordinator/src/project/project.rpc.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ export class ProjectRpcService implements OnModuleInit {
.withClientNameAndVersion(projectManifest.client?.name, projectManifest.client?.version)
.withBlockFitlerCapability()
.withFilteredBlocks()
.withHealth()
.validate(endpoint, endpointKey as RpcEndpointType);
return this.formatResponse(true);
} catch (e) {
Expand Down Expand Up @@ -403,7 +404,7 @@ export class ProjectRpcService implements OnModuleInit {
targetHeight = (await family.getTargetHeight(endpoint.value)) || lastHeight;
}
} catch (e) {
logger.debug(`getRpcMetadata error: ${e}`);
logger.debug(`getRpcMetadata error ${id}: ${e}`);
}
return {
startHeight,
Expand Down
160 changes: 159 additions & 1 deletion apps/indexer-coordinator/src/project/rpc.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export function getRpcFamilyObject(rpcFamily: string): IRpcFamily | undefined {
case 'subql_dict':
family = new RpcFamilySubqlDict();
break;
case 'solana':
family = new RpcFamilySolana();
break;
default:
break;
}
Expand Down Expand Up @@ -60,7 +63,7 @@ function jsonRpcRequest(endpoint: string, method: string, params: any[]): Promis
headers: {
'Content-Type': 'application/json',
},
timeout: 10000,
timeout: 30000,
}
);
}
Expand Down Expand Up @@ -161,6 +164,7 @@ export interface IRpcFamily {
withHeight(height?: number): IRpcFamily;
withBlockFitlerCapability(): IRpcFamily;
withFilteredBlocks(): IRpcFamily;
withHealth(): IRpcFamily;
validate(endpoint: string, endpointKey: string): Promise<void>;
getStartHeight(endpoint: string): Promise<number>;
getTargetHeight(endpoint: string): Promise<number>;
Expand Down Expand Up @@ -210,6 +214,9 @@ abstract class RpcFamily implements IRpcFamily {
withFilteredBlocks(): IRpcFamily {
throw new Error('Method not implemented.');
}
withHealth(): IRpcFamily {
throw new Error('Method not implemented.');
}
getStartHeight(endpoint: string): Promise<number> {
throw new Error('Method not implemented.');
}
Expand Down Expand Up @@ -447,6 +454,10 @@ export class RpcFamilyEvm extends RpcFamily {
return this;
}

withHealth(): IRpcFamily {
return this;
}

async getStartHeight(endpoint: string): Promise<number> {
const result = await getRpcRequestFunction(endpoint)(endpoint, 'eth_syncing', []);
if (result.data.error) {
Expand Down Expand Up @@ -694,6 +705,10 @@ export class RpcFamilySubstrate extends RpcFamily {
return this;
}

withHealth(): IRpcFamily {
return this;
}

async getStartHeight(endpoint: string): Promise<number> {
if (this.startHeight) {
return Promise.resolve(this.startHeight);
Expand Down Expand Up @@ -790,3 +805,146 @@ export class RpcFamilyPolkadot extends RpcFamilySubstrate {
return '';
}
}

export class RpcFamilySolana extends RpcFamily {
getEndpointKeys(): string[] {
return [
RpcEndpointType.solanaWs,
RpcEndpointType.solanaHttp,
RpcEndpointType.solanaMetricsHttp,
];
}

get ws() {
return RpcEndpointType.solanaWs;
}
get http() {
return RpcEndpointType.solanaHttp;
}
get metricsHttp() {
return RpcEndpointType.solanaMetricsHttp;
}

withChainId(chainId: string): IRpcFamily {
return this;
}

withGenesisHash(genesisHash: string): IRpcFamily {
this.actions.push(async () => {
const result = await getRpcRequestFunction(this.endpoint)(
this.endpoint,
'getGenesisHash',
[]
);
if (result.data.error) {
throw new Error(`Request getGenesisHash failed: ${result.data.error.message}`);
}
const genesisHashFromRpc = result.data.result;
if (genesisHashFromRpc !== genesisHash) {
throw new Error(`GenesisHash mismatch: ${genesisHashFromRpc} != ${genesisHash}`);
}
});
return this;
}

withNodeType(nodeType: string): IRpcFamily {
this.actions.push(async () => {
let slot = 1;
if (nodeType !== 'archive') {
let result = await getRpcRequestFunction(this.endpoint)(
this.endpoint,
'getLatestBlockhash',
[]
);
if (result.data.error) {
throw new Error(`Request withNodeType failed: ${result.data.error.message}`);
}
slot = BigNumber.from(result.data.result.context.slot).toNumber();
}
const result = await getRpcRequestFunction(this.endpoint)(this.endpoint, 'getBlock', [
slot,
{
maxSupportedTransactionVersion: 0,
transactionDetails: 'none',
rewards: false,
},
]);
if (result.data.error) {
throw new Error(`NodeType mismatch: ${nodeType} required`);
}
});
return this;
}

withClientNameAndVersion(clientName: string, clientVersion: string): IRpcFamily {
this.actions.push(async () => {
if (!clientName && !clientVersion) {
return;
}
const result = await getRpcRequestFunction(this.endpoint)(this.endpoint, 'getVersion', []);
if (result.data.error) {
throw new Error(`Request getVersion failed: ${result.data.error.message}`);
}
const resultSet = result.data.result;
const clientVersionFromRpc = resultSet['solana-core'];
if (
!!clientVersion &&
!semver.satisfies(semver.coerce(clientVersionFromRpc), clientVersion)
) {
throw new Error(`ClientVersion mismatch: ${clientVersionFromRpc} vs ${clientVersion}`);
}
});
return this;
}

withHeight(height?: number): IRpcFamily {
return this;
}

withBlockFitlerCapability(): IRpcFamily {
return this;
}

withFilteredBlocks(): IRpcFamily {
return this;
}

withHealth(): IRpcFamily {
this.actions.push(async () => {
const result = await getRpcRequestFunction(this.endpoint)(this.endpoint, 'getHealth', []);
if (result.data.error) {
throw new Error(`Not health: ${result.data.error}`);
}
});
return this;
}

async getStartHeight(endpoint: string): Promise<number> {
return 0;
}

async getTargetHeight(endpoint: string): Promise<number> {
return 0;
}

async getLastHeight(endpoint: string): Promise<number> {
const result = await getRpcRequestFunction(endpoint)(endpoint, 'getBlockHeight', []);
if (result.data.error) {
throw new Error(`Request getBlockHeight failed: ${result.data.error.message}`);
}
return BigNumber.from(result.data.result).toNumber();
}

async getLastTimestamp(endpoint: string): Promise<number> {
let result = await getRpcRequestFunction(endpoint)(endpoint, 'getLatestBlockhash', []);
if (result.data.error) {
throw new Error(`Request getLastTimestamp failed: ${result.data.error.message}`);
}
const slot = BigNumber.from(result.data.result.context.slot).toNumber();
result = await getRpcRequestFunction(endpoint)(endpoint, 'getBlockTime', [slot]);
if (result.data.error) {
throw new Error(`Request getLastTimestamp-getBlockTime failed: ${result.data.error.message}`);
}
return BigNumber.from(result.data.result).toNumber();
}
}
5 changes: 5 additions & 0 deletions apps/indexer-coordinator/src/project/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,17 @@ export enum RpcEndpointType {
subqlDictWs = 'subql_dictWs',
subqlDictHttp = 'subql_dictHttp',
subqlDictMetricsHttp = 'subql_dictMetricsHttp',

solanaHttp = 'solanaHttp',
solanaWs = 'solanaWs',
solanaMetricsHttp = 'solanaMetricsHttp',
}

export const RpcEndpointAccessType = {
[RpcEndpointType.evmMetricsHttp]: AccessType.INTERNAL,
[RpcEndpointType.polkadotMetricsHttp]: AccessType.INTERNAL,
[RpcEndpointType.subqlDictMetricsHttp]: AccessType.INTERNAL,
[RpcEndpointType.solanaMetricsHttp]: AccessType.INTERNAL,
};

@InputType('SubgraphPort')
Expand Down

0 comments on commit 0c74b1b

Please sign in to comment.