From 1785fa329c45e9f39a1217f2adb8e07af87c7b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kotol?= Date: Wed, 11 Oct 2023 16:01:41 +0200 Subject: [PATCH] ALL-3028 Add Solana Rpc support (#975) --- CHANGELOG.md | 4 + README.md | 8 +- package.json | 4 +- src/dto/Network.ts | 7 +- src/dto/rpc/SolanaRpcSuite.ts | 4 +- src/e2e/rpc/other/tatum.rpc.solana.spec.ts | 596 ++++++++++-------- src/service/rpc/index.ts | 2 +- src/service/rpc/other/AbstractSolanaRpc.ts | 467 ++++++++++++++ .../rpc/other/SolanaLoadBalancerRpc.ts | 42 ++ src/service/rpc/other/SolanaRpc.ts | 503 --------------- src/util/util.shared.ts | 26 +- 11 files changed, 865 insertions(+), 798 deletions(-) create mode 100644 src/service/rpc/other/AbstractSolanaRpc.ts create mode 100644 src/service/rpc/other/SolanaLoadBalancerRpc.ts delete mode 100644 src/service/rpc/other/SolanaRpc.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index fad4262b7f..5f5c56376f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [4.1.1] - 2023.10.11 +### Added +- Added RPC support for the SOLANA network. Users can now make RPC calls to these network using the `Network.SOLANA` network. + ## [4.1.0] - 2023.10.11 ### Added - Added `faucet` submodule with `fund` method for requesting testnet native tokens from available `Tatum` faucets. diff --git a/README.md b/README.md index 66a212466e..19b1f2d874 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,8 @@ This section provides a list of various blockchain network status pages, powered | [klaytn-baobab.status.tatum.io](https://klaytn-baobab.status.tatum.io) | | [klaytn-baobab-archive.status.tatum.io](https://klaytn-baobab-archive.status.tatum.io) | | [avalanche-c-mainnet-archive.status.tatum.io](https://avalanche-c-mainnet-archive.status.tatum.io) | +| [solana-mainnet.status.tatum.io](https://solana-mainnet.status.tatum.io) | +| [solana-devnet.status.tatum.io](https://solana-devnet.status.tatum.io) | ### Load Balancer @@ -549,8 +551,10 @@ Here are the list of nodes for each blockchain: | [rpc.tatum.io/klaytn-mainnet-archive/list.json](https://rpc.tatum.io/klaytn-mainnet-archive/list.json) | | [rpc.tatum.io/klaytn-baobab/list.json](https://rpc.tatum.io/klaytn-baobab/list.json) | | [rpc.tatum.io/klaytn-baobab-archive/list.json](https://rpc.tatum.io/klaytn-baobab-archive/list.json) | -| [rpc.tatum.io/bitcoin-cash-mainnet/list.json](https://rpc.tatum.io/bitcoin-cash-mainnet/list.json) | -| [rpc.tatum.io/avalanche-c-mainnet-archive/list.json](https://rpc.tatum.io/avalanche-c-mainnet-archive/list.json) | +| [rpc.tatum.io/bitcoin-cash-mainnet/list.json](https://rpc.tatum.io/bitcoin-cash-mainnet/list.json) | +| [rpc.tatum.io/avalanche-c-mainnet-archive/list.json](https://rpc.tatum.io/avalanche-c-mainnet-archive/list.json) | +| [rpc.tatum.io/solana-mainnet/list.json](https://rpc.tatum.io/solana-mainnet/list.json) | +| [rpc.tatum.io/solana-devnet/list.json](https://rpc.tatum.io/solana-devnet/list.json) | Following pattern defines the URL for fetching the list of nodes: diff --git a/package.json b/package.json index 77d7e8af04..5e4f9c64f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tatumio/tatum", - "version": "4.1.0", + "version": "4.1.1", "description": "Tatum JS SDK", "author": "Tatum", "repository": "https://github.com/tatumio/tatum-js", @@ -20,7 +20,7 @@ "types": "./dist/src/main.d.ts", "scripts": { "cli": "ts-node src/cli.ts", - "lint": "eslint src/ --ext .js,.jsx,.ts,.tsx && prettier --write src/", + "lint": "eslint --fix src/ --ext .js,.jsx,.ts,.tsx && prettier --write src/", "test": "jest --forceExit", "clean": "rm -rf dist build package", "ts-node": "ts-node", diff --git a/src/dto/Network.ts b/src/dto/Network.ts index 0d6d55670a..5c9725660c 100644 --- a/src/dto/Network.ts +++ b/src/dto/Network.ts @@ -224,6 +224,7 @@ export const TRON_LOAD_BALANCER_NETWORKS = [Network.TRON] export const EOS_LOAD_BALANCER_NETWORKS = [Network.EOS] export const XRP_LOAD_BALANCER_NETWORKS = [Network.XRP, Network.XRP_TESTNET] export const NATIVE_EVM_LOAD_BALANCER_NETWORKS = [Network.KLAYTN, Network.KLAYTN_BAOBAB] +export const SOLANA_NETWORKS = [Network.SOLANA, Network.SOLANA_DEVNET] export const LOAD_BALANCER_NETWORKS = [ ...UTXO_LOAD_BALANCER_NETWORKS, @@ -232,6 +233,7 @@ export const LOAD_BALANCER_NETWORKS = [ ...EOS_LOAD_BALANCER_NETWORKS, ...XRP_LOAD_BALANCER_NETWORKS, ...NATIVE_EVM_LOAD_BALANCER_NETWORKS, + ...SOLANA_NETWORKS, ] export const EVM_ARCHIVE_NON_ARCHIVE_LOAD_BALANCER_NETWORKS = [ @@ -244,7 +246,6 @@ export const EVM_ARCHIVE_NON_ARCHIVE_LOAD_BALANCER_NETWORKS = [ Network.CHILIZ, ] -export const SOLANA_NETWORKS = [Network.SOLANA, Network.SOLANA_DEVNET] export const TRON_NETWORKS = [Network.TRON, Network.TRON_SHASTA] export const EOS_NETWORKS = [Network.EOS, Network.EOS_TESTNET] @@ -260,7 +261,7 @@ export const isDataApiEnabledNetwork = (network: Network) => DATA_API_NETWORKS.i export const isDataApiUtxoEnabledNetwork = (network: Network) => DATA_API_UTXO_NETWORKS.includes(network) -export const isSolanaEnabledNetwork = (network: Network) => SOLANA_NETWORKS.includes(network) +export const isSolanaNetwork = (network: Network) => SOLANA_NETWORKS.includes(network) export const isTronNetwork = (network: Network) => TRON_NETWORKS.includes(network) @@ -284,6 +285,8 @@ export const isXrpLoadBalancerNetwork = (network: Network) => XRP_LOAD_BALANCER_ export const isNativeEvmLoadBalancerNetwork = (network: Network) => NATIVE_EVM_LOAD_BALANCER_NETWORKS.includes(network) +export const isSameGetBlockNetwork = (network: Network) => isUtxoBasedNetwork(network) || isEvmBasedNetwork(network) || isTronNetwork(network) || isSolanaNetwork(network) + export enum MappedNetwork { HORIZEN_EON = 'horizen-eon-mainnet', DOGECOIN_MAINNET = 'dogecoin-mainnet', diff --git a/src/dto/rpc/SolanaRpcSuite.ts b/src/dto/rpc/SolanaRpcSuite.ts index 681facaa3c..cc0c599402 100644 --- a/src/dto/rpc/SolanaRpcSuite.ts +++ b/src/dto/rpc/SolanaRpcSuite.ts @@ -341,7 +341,9 @@ export type SolanaAddressSignature = { export type SolanaInflationRate = { total: number; validator: number; foundation: number; epoch: number } -export interface SolanaRpcSuite extends AbstractRpcInterface { +export interface SolanaRpcSuite extends SolanaRpcInterface, AbstractRpcInterface {} + +export interface SolanaRpcInterface { /** * Get info about the account on the Solana blockchain. * @param pubkey - Pubkey of account to query, as base-58 encoded string diff --git a/src/e2e/rpc/other/tatum.rpc.solana.spec.ts b/src/e2e/rpc/other/tatum.rpc.solana.spec.ts index eaf5147485..30594533df 100644 --- a/src/e2e/rpc/other/tatum.rpc.solana.spec.ts +++ b/src/e2e/rpc/other/tatum.rpc.solana.spec.ts @@ -1,368 +1,406 @@ import { Commitment, Encoding } from '../../../dto' import { Network, Solana, TatumSDK } from '../../../service' +import { e2eUtil } from '../../e2e.util' const getClient = async (testnet?: boolean): Promise => await TatumSDK.init({ network: testnet ? Network.SOLANA_DEVNET : Network.SOLANA, retryCount: 1, retryDelay: 2000, + verbose: e2eUtil.isVerbose, }) const blockNumber = 203046000 -describe.skip('Solana mainnet RPC', () => { - describe('getAccountInfo', () => { - it('should return account info', async () => { - const tatum = await getClient() - const publicKey = '8Ew6iQXcTRHAUNNu3X9VBn1g1bJkXEZJ9gFD2AGKtdPB' - const { result } = await tatum.rpc.getAccountInfo(publicKey) - - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value?.lamports).toBeGreaterThan(0) +describe('Solana', () => { + describe('mainnet', () => { + describe('getAccountInfo', () => { + it('should return account info', async () => { + const tatum = await getClient() + const publicKey = '8Ew6iQXcTRHAUNNu3X9VBn1g1bJkXEZJ9gFD2AGKtdPB' + const { result } = await tatum.rpc.getAccountInfo(publicKey) + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value?.lamports).toBeGreaterThan(0) + }) }) - }) - describe('getBalance', () => { - it('should return the balance of a public key', async () => { - const tatum = await getClient() - const publicKey = '8Ew6iQXcTRHAUNNu3X9VBn1g1bJkXEZJ9gFD2AGKtdPB' + describe('getBalance', () => { + it('should return the balance of a public key', async () => { + const tatum = await getClient() + const publicKey = '8Ew6iQXcTRHAUNNu3X9VBn1g1bJkXEZJ9gFD2AGKtdPB' - const { result } = await tatum.rpc.getBalance(publicKey) + const { result } = await tatum.rpc.getBalance(publicKey) - const balance = result?.value - expect(typeof balance).toBe('number') - expect(balance).toBeGreaterThan(0) + await tatum.destroy() + const balance = result?.value + expect(typeof balance).toBe('number') + expect(balance).toBeGreaterThan(0) - expect(result?.context.slot).toBeGreaterThan(0) - }) - - it('should return error if an invalid public key is provided', async () => { - const tatum = await getClient() - const publicKey = 'invalid-public-key' + expect(result?.context.slot).toBeGreaterThan(0) + }) - const { error } = await tatum.rpc.getBalance(publicKey) + it('should return error if an invalid public key is provided', async () => { + const tatum = await getClient() + const publicKey = 'invalid-public-key' - expect(error?.message).toBe('Invalid param: Invalid') + const { error } = await tatum.rpc.getBalance(publicKey) + await tatum.destroy() + expect(error?.message).toBe('Invalid param: Invalid') + }) }) - }) - - describe('getBlockHeight', () => { - it('should return the current block height', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getBlockHeight() - expect(typeof result).toBe('number') - expect(result).toBeGreaterThan(0) + describe('getBlockHeight', () => { + it('should return the current block height', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlockHeight() + await tatum.destroy() + expect(typeof result).toBe('number') + expect(result).toBeGreaterThan(0) + }) }) - }) - describe('getBlock', () => { - it('should return a recent block', async () => { - const tatum = await getClient() - const { result: slot } = await tatum.rpc.getSlot() - const { result } = await tatum.rpc.getBlock(slot || 0, { - encoding: Encoding.JsonParsed, - maxSupportedTransactionVersion: 0, + describe('getBlock', () => { + it('should return a recent block', async () => { + const tatum = await getClient() + const { result: slot } = await tatum.rpc.getSlot() + const { result } = await tatum.rpc.getBlock(slot || 0, { + encoding: Encoding.JsonParsed, + maxSupportedTransactionVersion: 0, + }) + await tatum.destroy() + expect(result).toHaveProperty('blockhash') + expect(result?.blockhash).toBeTruthy() + expect(result?.previousBlockhash).toBeTruthy() + expect(result?.blockHeight).toBeGreaterThan(0) + expect(result?.parentSlot).toBeGreaterThan(0) + expect(result?.blockTime).toBeGreaterThan(0) + expect(Array.isArray(result?.transactions)).toBe(true) }) - - expect(result).toHaveProperty('blockhash') - expect(result?.blockhash).toBeTruthy() - expect(result?.previousBlockhash).toBeTruthy() - expect(result?.blockHeight).toBeGreaterThan(0) - expect(result?.parentSlot).toBeGreaterThan(0) - expect(result?.blockTime).toBeGreaterThan(0) - expect(Array.isArray(result?.transactions)).toBe(true) }) - }) - - describe('getBlockProduction', () => { - it('should return block production information', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getBlockProduction() - expect(result?.context.slot).toBeGreaterThan(0) - expect(result).toHaveProperty('value.byIdentity') - expect(result).toHaveProperty('value.range.firstSlot') + describe('getBlockProduction', () => { + it('should return block production information', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlockProduction() + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result).toHaveProperty('value.byIdentity') + expect(result).toHaveProperty('value.range.firstSlot') + }) }) - }) - - describe('getBlockCommitment', () => { - it('should return block commitment information', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getBlockCommitment(blockNumber) - expect(result).toHaveProperty('commitment') - expect(result?.totalStake).toBeGreaterThan(0) + describe('getBlockCommitment', () => { + it('should return block commitment information', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlockCommitment(blockNumber) + await tatum.destroy() + expect(result).toHaveProperty('commitment') + expect(result?.totalStake).toBeGreaterThan(0) + }) }) - }) - describe('getBlocks', () => { - it('should return an array of block numbers between two slots', async () => { - const tatum = await getClient() - const startSlot = 193167060 - const endSlot = 193167070 - const { result } = await tatum.rpc.getBlocks(endSlot, startSlot) + describe('getBlocks', () => { + it('should return an array of block numbers between two slots', async () => { + const tatum = await getClient() + const startSlot = 193167060 + const endSlot = 193167070 + const { result } = await tatum.rpc.getBlocks(endSlot, startSlot) + await tatum.destroy() + expect(Array.isArray(result)).toBe(true) + }) - expect(Array.isArray(result)).toBe(true) + // Sometimes this test fails, so we skip it for now + it.skip('should return an array of block numbers between two slots, passing only endSlot', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlocks(blockNumber) + await tatum.destroy() + expect(Array.isArray(result)).toBe(true) + }, 9000000) + + it('should return an array of confirmed block numbers between two slots', async () => { + const tatum = await getClient() + const startSlot = 193167060 + const endSlot = 193167070 + const { result } = await tatum.rpc.getBlocks(endSlot, startSlot, { + commitment: Commitment.Confirmed, + }) + await tatum.destroy() + expect(Array.isArray(result)).toBe(true) + }) }) - // Sometimes this test fails, so we skip it for now - it.skip('should return an array of block numbers between two slots, passing only endSlot', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getBlocks(blockNumber) - - expect(Array.isArray(result)).toBe(true) - }, 9000000) - - it('should return an array of confirmed block numbers between two slots', async () => { - const tatum = await getClient() - const startSlot = 193167060 - const endSlot = 193167070 - const { result } = await tatum.rpc.getBlocks(endSlot, startSlot, { - commitment: Commitment.Confirmed, + describe('getBlockTime', () => { + it('should return block time ', async () => { + const tatum = await getClient() + const { result: slot } = await tatum.rpc.getSlot() + const { result } = await tatum.rpc.getBlockTime(slot || 0) + await tatum.destroy() + expect(typeof result).toBe('number') + expect(result).toBeGreaterThan(0) }) - - expect(Array.isArray(result)).toBe(true) }) - }) - - describe('getBlockTime', () => { - it('should return block time ', async () => { - const tatum = await getClient() - const { result: slot } = await tatum.rpc.getSlot() - const { result } = await tatum.rpc.getBlockTime(slot || 0) - expect(typeof result).toBe('number') - expect(result).toBeGreaterThan(0) + describe('getClusterNodes', () => { + it('should return cluster nodes info ', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getClusterNodes() + await tatum.destroy() + expect(Array.isArray(result)).toBe(true) + }) }) - }) - - describe('getClusterNodes', () => { - it('should return cluster nodes info ', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getClusterNodes() - expect(Array.isArray(result)).toBe(true) + describe('getEpochInfo', () => { + it('should return epoch info ', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getEpochInfo() + await tatum.destroy() + expect(result?.epoch).toBeGreaterThan(0) + }) }) - }) - - describe('getEpochInfo', () => { - it('should return epoch info ', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getEpochInfo() - expect(result?.epoch).toBeGreaterThan(0) + describe('getEpochSchedule', () => { + it('should return epoch schedule ', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getEpochSchedule() + await tatum.destroy() + expect(result?.slotsPerEpoch).toBeGreaterThan(0) + }) }) - }) - - describe('getEpochSchedule', () => { - it('should return epoch schedule ', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getEpochSchedule() - expect(result?.slotsPerEpoch).toBeGreaterThan(0) + describe('getFirstAvailableBlock', () => { + it('should return first available block', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getFirstAvailableBlock() + await tatum.destroy() + expect(result).toBeGreaterThan(0) + }) }) - }) - - describe('getFirstAvailableBlock', () => { - it('should return first available block', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getFirstAvailableBlock() - expect(result).toBeGreaterThan(0) + describe('getGenesisHash', () => { + it('should return genesis hash', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getGenesisHash() + await tatum.destroy() + expect(result).toBeTruthy() + }) }) - }) - - describe('getGenesisHash', () => { - it('should return genesis hash', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getGenesisHash() - expect(result).toBeTruthy() + describe('getHealth', () => { + it('should return health status', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getHealth() + await tatum.destroy() + expect(result).toEqual('ok') + }) }) - }) - - describe('getHealth', () => { - it('should return health status', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getHealth() - expect(result).toEqual('ok') + describe('getHighestSnapshotSlot', () => { + it('should return highest snapshot slot', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getHighestSnapshotSlot() + await tatum.destroy() + expect(result?.full).toBeGreaterThan(0) + }) }) - }) - - describe('getHighestSnapshotSlot', () => { - it('should return highest snapshot slot', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getHighestSnapshotSlot() - expect(result?.full).toBeGreaterThan(0) + describe('getIdentity', () => { + it('should return identity', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getIdentity() + await tatum.destroy() + expect(result).toBeTruthy() + }) }) - }) - - describe('getIdentity', () => { - it('should return identity', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getIdentity() - expect(result).toBeTruthy() + describe('getInflationGovernor', () => { + it('should return inflation governor info', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getInflationGovernor() + await tatum.destroy() + expect(result?.terminal).toBeGreaterThan(0) + }) }) - }) - - describe('getInflationGovernor', () => { - it('should return inflation governor info', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getInflationGovernor() - expect(result?.terminal).toBeGreaterThan(0) + describe('getInflationRate', () => { + it('should return inflation rate', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getInflationRate() + await tatum.destroy() + expect(result?.total).toBeGreaterThan(0) + expect(result?.epoch).toBeGreaterThan(0) + }) }) - }) - - describe('getInflationRate', () => { - it('should return inflation rate', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getInflationRate() - expect(result?.total).toBeGreaterThan(0) - expect(result?.epoch).toBeGreaterThan(0) + describe('getInflationReward', () => { + it.skip('should return inflation reward', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getInflationReward(['GUP3BG93X9EoDor3bDarTqv8n653u1Bkr2NbQqRqBZwF']) + const item = result![0] + await tatum.destroy() + expect(item.epoch).toBeGreaterThan(0) + }) }) - }) - - describe('getInflationReward', () => { - it.skip('should return inflation reward', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getInflationReward(['GUP3BG93X9EoDor3bDarTqv8n653u1Bkr2NbQqRqBZwF']) - const item = result![0] - expect(item.epoch).toBeGreaterThan(0) + //takes long time to finish + describe('getLargestAccounts', () => { + it.skip('should return largest accounts', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getLargestAccounts() + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value.length).toBeGreaterThan(0) + }) }) - }) - - //takes long time to finish - describe('getLargestAccounts', () => { - it.skip('should return largest accounts', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getLargestAccounts() - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value.length).toBeGreaterThan(0) + describe('getLatestBlockhash', () => { + it('should return latest blockhash', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getLatestBlockhash() + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value.blockhash).toBeTruthy() + expect(result?.value.lastValidBlockHeight).toBeGreaterThan(0) + }) }) - }) - - describe('getLatestBlockhash', () => { - it('should return latest blockhash', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getLatestBlockhash() - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value.blockhash).toBeTruthy() - expect(result?.value.lastValidBlockHeight).toBeGreaterThan(0) + describe('getLeaderSchedule', () => { + it('should return leader schedule', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getLeaderSchedule() + await tatum.destroy() + //binance validator + expect(result?.DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy.length).toBeGreaterThan(0) + }) }) - }) - - describe('getLeaderSchedule', () => { - it('should return leader schedule', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getLeaderSchedule() - //binance validator - expect(result?.DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy.length).toBeGreaterThan(0) + describe('getMultipleAccounts', () => { + it('should return account info', async () => { + const tatum = await getClient() + //binance validator + const { result } = await tatum.rpc.getMultipleAccounts(['DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy']) + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value[0]?.lamports).toBeGreaterThan(0) + }) }) - }) - describe('getMultipleAccounts', () => { - it('should return account info', async () => { - const tatum = await getClient() - //binance validator - const { result } = await tatum.rpc.getMultipleAccounts(['DRpbCBMxVnDK7maPM5tGv6MvB3v1sRMC86PZ8okm21hy']) + describe('getSlot', () => { + it('should return slot number', async () => { + const tatum = await getClient() - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value[0]?.lamports).toBeGreaterThan(0) + const { result } = await tatum.rpc.getSlot() + await tatum.destroy() + expect(result).toBeGreaterThan(0) + }) }) - }) - describe('getSlot', () => { - it('should return slot number', async () => { - const tatum = await getClient() + describe('getSlotLeaders', () => { + it('should return slot leader info', async () => { + const tatum = await getClient() - const { result } = await tatum.rpc.getSlot() - - expect(result).toBeGreaterThan(0) + const { result } = await tatum.rpc.getSlotLeader() + await tatum.destroy() + expect(result).toBeTruthy() + }) }) - }) - describe('getSlotLeaders', () => { - it('should return slot leader info', async () => { - const tatum = await getClient() + describe('getTokenAccountBalance', () => { + it('should return token account balance', async () => { + const tatum = await getClient() - const { result } = await tatum.rpc.getSlotLeader() - - expect(result).toBeTruthy() + const { result } = await tatum.rpc.getTokenAccountBalance( + 'DhzDoryP2a4rMK2bcWwJxrE2uW6ir81ES8ZwJJPPpxDN', + ) + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value.amount).toBeTruthy() + }) }) - }) - describe('getTokenAccountBalance', () => { - it('should return token account balance', async () => { - const tatum = await getClient() + describe('getTokenAccountsByOwner', () => { + it('should return token accounts by owner', async () => { + const tatum = await getClient() - const { result } = await tatum.rpc.getTokenAccountBalance( - 'DhzDoryP2a4rMK2bcWwJxrE2uW6ir81ES8ZwJJPPpxDN', - ) - - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value.amount).toBeTruthy() + const { result } = await tatum.rpc.getTokenAccountsByOwner( + 'GgPpTKg78vmzgDtP1DNn72CHAYjRdKY7AV6zgszoHCSa', + { + mint: '1YDQ35V8g68FGvcT85haHwAXv1U7XMzuc4mZeEXfrjE', + }, + { encoding: Encoding.JsonParsed }, + ) + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result?.value.length).toBeGreaterThan(0) + }) }) - }) - - describe('getTokenAccountsByOwner', () => { - it('should return token accounts by owner', async () => { - const tatum = await getClient() - const { result } = await tatum.rpc.getTokenAccountsByOwner( - 'GgPpTKg78vmzgDtP1DNn72CHAYjRdKY7AV6zgszoHCSa', - { - mint: '1YDQ35V8g68FGvcT85haHwAXv1U7XMzuc4mZeEXfrjE', - }, - { encoding: Encoding.JsonParsed }, - ) - - expect(result?.context.slot).toBeGreaterThan(0) - expect(result?.value.length).toBeGreaterThan(0) + describe('getTransaction', () => { + it.skip('should return transaction data', async () => { + const tatum = await getClient() + + const { result: slot } = await tatum.rpc.getSlot() + const { result: blockResponse } = await tatum.rpc.getBlock(slot || 0, { + encoding: Encoding.JsonParsed, + maxSupportedTransactionVersion: 0, + }) + + const { result } = await tatum.rpc.getTransaction( + blockResponse?.transactions[0].transaction.signatures[0], + ) + await tatum.destroy() + expect(result?.slot).toBeGreaterThan(0) + expect(result?.transaction).toBeTruthy() + }) }) - }) - describe('getTransaction', () => { - it.skip('should return transaction data', async () => { - const tatum = await getClient() - - const { result: slot } = await tatum.rpc.getSlot() - const { result: blockResponse } = await tatum.rpc.getBlock(slot || 0, { - encoding: Encoding.JsonParsed, - maxSupportedTransactionVersion: 0, + //takes too long to finish + describe('getProgramAccount', () => { + it.skip('should return account data', async () => { + const tatum = await getClient(true) + + const { result } = await tatum.rpc.getProgramAccounts('FriELggez2Dy3phZeHHAdpcoEXkKQVkv6tx3zDtCVP8T', { + filters: [ + { + dataSize: 165, // number of bytes + }, + ], + }) + await tatum.destroy() + expect(result).toBeTruthy() }) - - const { result } = await tatum.rpc.getTransaction( - blockResponse?.transactions[0].transaction.signatures[0], - ) - - expect(result?.slot).toBeGreaterThan(0) - expect(result?.transaction).toBeTruthy() }) }) - //takes too long to finish - describe('getProgramAccount', () => { - it.skip('should return account data', async () => { - const tatum = await getClient(true) + describe('devnet', () => { + describe('getBlockHeight', () => { + it('should return the current block height', async () => { + const tatum = await getClient(true) + const { result } = await tatum.rpc.getBlockHeight() + await tatum.destroy() + expect(typeof result).toBe('number') + expect(result).toBeGreaterThan(0) + }) + }) - const { result } = await tatum.rpc.getProgramAccounts('FriELggez2Dy3phZeHHAdpcoEXkKQVkv6tx3zDtCVP8T', { - filters: [ - { - dataSize: 165, // number of bytes - }, - ], + describe('getBlockProduction', () => { + it('should return block production information', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlockProduction() + await tatum.destroy() + expect(result?.context.slot).toBeGreaterThan(0) + expect(result).toHaveProperty('value.byIdentity') + expect(result).toHaveProperty('value.range.firstSlot') }) + }) - expect(result).toBeTruthy() + describe('getBlockCommitment', () => { + it('should return block commitment information', async () => { + const tatum = await getClient() + const { result } = await tatum.rpc.getBlockCommitment(blockNumber) + await tatum.destroy() + expect(result).toHaveProperty('commitment') + expect(result?.totalStake).toBeGreaterThan(0) + }) }) }) }) diff --git a/src/service/rpc/index.ts b/src/service/rpc/index.ts index 70eebb96ea..91839f1efc 100644 --- a/src/service/rpc/index.ts +++ b/src/service/rpc/index.ts @@ -1,6 +1,6 @@ export * from './evm' export * from './evm/AbstractTronRpc' export * from './generic' +export * from './other/AbstractSolanaRpc' export * from './other/AbstractXrpRpc' -export * from './other/SolanaRpc' export * from './utxo' diff --git a/src/service/rpc/other/AbstractSolanaRpc.ts b/src/service/rpc/other/AbstractSolanaRpc.ts new file mode 100644 index 0000000000..13fea03733 --- /dev/null +++ b/src/service/rpc/other/AbstractSolanaRpc.ts @@ -0,0 +1,467 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { Service } from 'typedi' +import { + GetAccountInfoOptions, + GetBlockOptions, + GetBlockProductionOptions, + GetCommitmentMinContextSlotOptions, + GetCommitmentOptions, + GetInflationRewardOptions, + GetLargestAccountsOptions, + GetLeaderScheduleOptions, + GetMultipleAccountsOptions, + GetProgramAccountsOptions, + GetSignaturesForAddressOptions, + GetSignatureStatusesOptions, + GetStakeActivationOptions, + GetSupplyOptions, + GetTokenAccountsOptions, + GetTransactionOptions, + GetVoteAccountOptions, + JsonRpcResponse, + SendTransactionOptions, + SimulateTransactionOptions, + SolanaAccount, + SolanaAccountInfo, + SolanaAddressSignature, + SolanaBlock, + SolanaBlockProduction, + SolanaClusterNode, + SolanaEpochInfo, + SolanaEpochSchedule, + SolanaInflationGovernor, + SolanaInflationRate, + SolanaInflationReward, + SolanaLargestAccount, + SolanaLatestBlockhash, + SolanaLeaderSchedule, + SolanaMint, + SolanaPerformanceSample, + SolanaProgramId, + SolanaRpcInterface, + SolanaSignatureStatus, + SolanaSupply, + SolanaTokenAccount, + SolanaTokenAccountBalance, + SolanaTokenSupply, + SolanaTransaction, + SolanaTransactionSimulation, + SolanaTypeWithContext, + SolanaVersion, + SolanaVoteAccount, +} from '../../../dto' + +@Service() +export abstract class AbstractSolanaRpc implements SolanaRpcInterface { + protected abstract rpcCall(method: string, params?: unknown[]): Promise + + getAccountInfo( + pubkey: string, + options?: GetAccountInfoOptions, + ): Promise>> { + return this.rpcCall>>('getAccountInfo', [ + pubkey, + options, + ]) + } + + getBalance(address: string): Promise>> { + return this.rpcCall>>('getBalance', [address]) + } + + getBlockHeight(options?: GetCommitmentMinContextSlotOptions): Promise> { + return this.rpcCall>('getBlockHeight', [options]) + } + + getBlock(block: number, options?: GetBlockOptions): Promise> { + return this.rpcCall>('getBlock', [block, options]) + } + + getBlockProduction( + options?: GetBlockProductionOptions, + ): Promise>> { + return this.rpcCall>>('getBlockProduction', [ + options, + ]) + } + + getBlockCommitment( + block: number, + ): Promise; totalStake: number }>> { + return this.rpcCall; totalStake: number }>>( + 'getBlockCommitment', + [block], + ) + } + + getBlocks( + startSlot: number, + endSlot?: number, + options?: GetCommitmentOptions, + ): Promise>> { + const params: any = [startSlot] + + if (endSlot) { + params.push(endSlot) + } + + if (options && options.commitment) { + params.push(options) + } + + return this.rpcCall>>('getBlocks', params) + } + + getBlocksWithLimit( + startSlot: number, + limit?: number, + options?: GetCommitmentOptions, + ): Promise>> { + const params: any = [startSlot] + + if (limit) { + params.push(limit) + } + if (options && options.commitment) { + params.push(options) + } + + return this.rpcCall>>('getBlocksWithLimit', params) + } + + getBlockTime(block: number): Promise> { + return this.rpcCall>('getBlockTime', [block]) + } + + getClusterNodes(): Promise>> { + return this.rpcCall>>('getClusterNodes') + } + + getEpochInfo(options?: GetCommitmentMinContextSlotOptions): Promise> { + return this.rpcCall>('getEpochInfo', [options]) + } + + getEpochSchedule(): Promise> { + return this.rpcCall>('getEpochSchedule') + } + + getFeeForMessage( + message: any, + options?: GetCommitmentMinContextSlotOptions, + ): Promise> { + return this.rpcCall>('getFeeForMessage', [message, options]) + } + + getFirstAvailableBlock(): Promise> { + return this.rpcCall>('getFirstAvailableBlock') + } + + getGenesisHash(): Promise> { + return this.rpcCall>('getGenesisHash') + } + + getHealth(): Promise> { + return this.rpcCall>('getHealth') + } + + getHighestSnapshotSlot(): Promise> { + return this.rpcCall>('getHighestSnapshotSlot') + } + + getIdentity(): Promise> { + return this.rpcCall>('getIdentity') + } + + getInflationGovernor(options?: GetCommitmentOptions): Promise> { + return this.rpcCall>('getInflationGovernor', [options]) + } + + getInflationRate(): Promise> { + return this.rpcCall>('getInflationRate') + } + + getInflationReward( + addresses?: string[], + options?: GetInflationRewardOptions, + ): Promise>> { + const params: any = [] + + if (addresses) { + params.push(addresses) + } + + if (options) { + params.push(options) + } + + return this.rpcCall>>('getInflationReward', params) + } + + getLargestAccounts( + options?: GetLargestAccountsOptions, + ): Promise>> { + return this.rpcCall>>( + 'getLargestAccounts', + [options], + ) + } + + getLatestBlockhash( + options?: GetCommitmentMinContextSlotOptions, + ): Promise>> { + return this.rpcCall>>('getLatestBlockhash', [ + options, + ]) + } + + getLeaderSchedule( + slot?: number, + options?: GetLeaderScheduleOptions, + ): Promise> { + const params: any = [] + if (slot) { + params.push(slot) + } + + if (options) { + params.push(options) + } + + return this.rpcCall>('getLeaderSchedule', params) + } + + getMaxRetransmitSlot(): Promise> { + return this.rpcCall>('getMaxRetransmitSlot') + } + + getMaxShredInsertSlot(): Promise> { + return this.rpcCall>('getMaxShredInsertSlot') + } + + getMinimumBalanceForRentExemption( + dataSize?: number, + options?: GetCommitmentOptions, + ): Promise> { + return this.rpcCall>('getMinimumBalanceForRentExemption', [dataSize, options]) + } + + getMultipleAccounts( + pubKeys?: string[], + options?: GetMultipleAccountsOptions, + ): Promise>>> { + return this.rpcCall>>>( + 'getMultipleAccounts', + [pubKeys, options], + ) + } + + getProgramAccounts( + programId: string, + options?: GetProgramAccountsOptions, + ): Promise>> { + return this.rpcCall>>( + 'getProgramAccounts', + [programId, options], + ) + } + + getRecentPerformanceSamples(limit?: number): Promise>> { + return this.rpcCall>>('getRecentPerformanceSamples', [ + limit, + ]) + } + + getRecentPrioritizationFees( + addresses?: string[], + ): Promise>> { + return this.rpcCall>>( + 'getRecentPrioritizationFees', + [addresses], + ) + } + + getSignaturesForAddress( + address: string, + options?: GetSignaturesForAddressOptions, + ): Promise>> { + return this.rpcCall>>('getSignaturesForAddress', [ + address, + options, + ]) + } + + getSignatureStatuses( + signatures?: string[], + options?: GetSignatureStatusesOptions, + ): Promise>> { + return this.rpcCall>>( + 'getSignatureStatuses', + [signatures, options], + ) + } + + getSlot(options?: GetCommitmentMinContextSlotOptions): Promise> { + return this.rpcCall>('getSlot', [options]) + } + + getSlotLeader(options?: GetCommitmentMinContextSlotOptions): Promise> { + return this.rpcCall>('getSlotLeader', [options]) + } + + getSlotLeaders(startSlot?: number, limit?: number): Promise>> { + return this.rpcCall>>('getSlotLeaders', [startSlot, limit]) + } + + getStakeActivation( + pubkey: string, + options?: GetStakeActivationOptions, + ): Promise> { + return this.rpcCall>( + 'getStakeActivation', + [pubkey, options], + ) + } + + getStakeMinimumDelegation( + options?: GetCommitmentOptions, + ): Promise>> { + return this.rpcCall>>('getStakeMinimumDelegation', [ + options, + ]) + } + + getSupply(options?: GetSupplyOptions): Promise>> { + return this.rpcCall>>('getSupply', [options]) + } + + getTokenAccountBalance( + pubkey: string, + options?: GetCommitmentOptions, + ): Promise>> { + return this.rpcCall>>( + 'getTokenAccountBalance', + [pubkey, options], + ) + } + + getTokenAccountsByDelegate( + pubkey: string, + config?: SolanaMint | SolanaProgramId, + options?: GetTokenAccountsOptions, + ): Promise>> { + const params: any[] = [pubkey] + + if (config) { + params.push(config) + } + + if (options) { + params.push(options) + } + + return this.rpcCall>>( + 'getTokenAccountsByDelegate', + params, + ) + } + + getTokenAccountsByOwner( + pubkey: string, + config?: SolanaMint | SolanaProgramId, + options?: GetTokenAccountsOptions, + ): Promise>> { + const params: any[] = [pubkey] + + if (config) { + params.push(config) + } + if (options) { + params.push(options) + } + + return this.rpcCall>>( + 'getTokenAccountsByOwner', + params, + ) + } + + getTokenLargestAccounts( + pubkey: string, + options?: GetCommitmentOptions, + ): Promise>> { + return this.rpcCall>>('getTokenLargestAccounts', [ + pubkey, + options, + ]) + } + + getTokenSupply( + pubkey: string, + options?: GetCommitmentOptions, + ): Promise>> { + return this.rpcCall>>('getTokenSupply', [ + pubkey, + options, + ]) + } + + getTransaction( + signature: string, + options?: GetTransactionOptions, + ): Promise> { + return this.rpcCall>('getTransaction', [signature, options]) + } + + getTransactionCount(options?: GetCommitmentMinContextSlotOptions): Promise> { + return this.rpcCall>('getTransactionCount', [options]) + } + + getVersion(): Promise> { + return this.rpcCall>('getVersion') + } + + getVoteAccounts( + options?: GetVoteAccountOptions, + ): Promise; delinquent: Array }>> { + return this.rpcCall< + JsonRpcResponse<{ current: Array; delinquent: Array }> + >('getVoteAccounts', [options]) + } + + isBlockhashValid( + blockhash: string, + options?: GetCommitmentMinContextSlotOptions, + ): Promise>> { + return this.rpcCall>>('isBlockhashValid', [ + blockhash, + options, + ]) + } + + minimumLedgerSlot(): Promise> { + return this.rpcCall>('minimumLedgerSlot') + } + + requestAirdrop( + pubkey: string, + amount: number, + options?: GetCommitmentOptions, + ): Promise> { + return this.rpcCall>('requestAirdrop', [pubkey, amount, options]) + } + + sendTransaction(transaction: string, options?: SendTransactionOptions): Promise> { + return this.rpcCall>('sendTransaction', [transaction, options]) + } + + simulateTransaction( + transaction: string, + options?: SimulateTransactionOptions, + ): Promise>> { + return this.rpcCall>>( + 'simulateTransaction', + [transaction, options], + ) + } +} diff --git a/src/service/rpc/other/SolanaLoadBalancerRpc.ts b/src/service/rpc/other/SolanaLoadBalancerRpc.ts new file mode 100644 index 0000000000..000065a3a5 --- /dev/null +++ b/src/service/rpc/other/SolanaLoadBalancerRpc.ts @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Container, Service } from 'typedi' +import { Utils } from '../../../util' +import { LoadBalancer } from '../generic' +import { AbstractSolanaRpc } from './AbstractSolanaRpc' +import { JsonRpcCall, JsonRpcResponse, SolanaRpcSuite } from '../../../dto' + +@Service({ + factory: (data: { id: string }) => { + return new SolanaLoadBalancerRpc(data.id) + }, + transient: true, +}) +export class SolanaLoadBalancerRpc extends AbstractSolanaRpc implements SolanaRpcSuite { + protected readonly loadBalancer: LoadBalancer + + constructor(id: string) { + super() + this.loadBalancer = Container.of(id).get(LoadBalancer) + } + + protected async rpcCall(method: string, params?: unknown[]): Promise { + const preparedCall = Utils.prepareRpcCall(method, params) + return (await this.loadBalancer.rawRpcCall(preparedCall)) as T + } + + async rawRpcCall(body: JsonRpcCall): Promise> { + return this.loadBalancer.rawRpcCall(body) + } + + rawBatchRpcCall(body: JsonRpcCall[]): Promise[] | JsonRpcResponse> { + return this.loadBalancer.rawBatchRpcCall(body) + } + + public destroy() { + this.loadBalancer.destroy() + } + + public getRpcNodeUrl(): string { + return this.loadBalancer.getActiveNormalUrlWithFallback().url + } +} diff --git a/src/service/rpc/other/SolanaRpc.ts b/src/service/rpc/other/SolanaRpc.ts deleted file mode 100644 index 077687a15b..0000000000 --- a/src/service/rpc/other/SolanaRpc.ts +++ /dev/null @@ -1,503 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -import { Service } from 'typedi' -import { - GetAccountInfoOptions, - GetBlockOptions, - GetBlockProductionOptions, - GetCommitmentMinContextSlotOptions, - GetCommitmentOptions, - GetInflationRewardOptions, - GetLargestAccountsOptions, - GetLeaderScheduleOptions, - GetMultipleAccountsOptions, - GetProgramAccountsOptions, - GetSignaturesForAddressOptions, - GetSignatureStatusesOptions, - GetStakeActivationOptions, - GetSupplyOptions, - GetTokenAccountsOptions, - GetTransactionOptions, - GetVoteAccountOptions, - JsonRpcResponse, - SendTransactionOptions, - SimulateTransactionOptions, - SolanaAccount, - SolanaAccountInfo, - SolanaAddressSignature, - SolanaBlock, - SolanaBlockProduction, - SolanaClusterNode, - SolanaEpochInfo, - SolanaEpochSchedule, - SolanaInflationGovernor, - SolanaInflationRate, - SolanaInflationReward, - SolanaLargestAccount, - SolanaLatestBlockhash, - SolanaLeaderSchedule, - SolanaMint, - SolanaPerformanceSample, - SolanaProgramId, - SolanaRpcSuite, - SolanaSignatureStatus, - SolanaSupply, - SolanaTokenAccount, - SolanaTokenAccountBalance, - SolanaTokenSupply, - SolanaTransaction, - SolanaTransactionSimulation, - SolanaTypeWithContext, - SolanaVersion, - SolanaVoteAccount, -} from '../../../dto' -import { Utils } from '../../../util' -import { AbstractBatchRpc } from '../generic' - -@Service({ - factory: (data: { id: string }) => { - return new SolanaRpc(data.id) - }, - transient: true, -}) -export class SolanaRpc extends AbstractBatchRpc implements SolanaRpcSuite { - constructor(id: string) { - super(id) - } - - getAccountInfo( - pubkey: string, - options?: GetAccountInfoOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getAccountInfo', [pubkey, options]), - ) - } - - getBalance(address: string): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBalance', [address]), - ) - } - - getBlockHeight(options?: GetCommitmentMinContextSlotOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlockHeight', [options]), - ) - } - - getBlock(block: number, options?: GetBlockOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlock', [block, options]), - ) - } - - getBlockProduction( - options?: GetBlockProductionOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlockProduction', [options]), - ) - } - - getBlockCommitment( - block: number, - ): Promise; totalStake: number }>> { - return this.connector.rpcCall; totalStake: number }>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlockCommitment', [block]), - ) - } - getBlocks( - startSlot: number, - endSlot?: number, - options?: GetCommitmentOptions, - ): Promise>> { - let params: any = [startSlot] - if (endSlot) params = [startSlot, endSlot] - - if (options && options.commitment) params.push(options) - - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlocks', params), - ) - } - getBlocksWithLimit( - startSlot: number, - limit?: number, - options?: GetCommitmentOptions, - ): Promise>> { - let params: any = [startSlot] - if (limit) params = [startSlot, limit] - - if (options && options.commitment) params.push(options) - - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlocksWithLimit', params), - ) - } - - getBlockTime(block: number): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getBlockTime', [block]), - ) - } - - getClusterNodes(): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getClusterNodes'), - ) - } - - getEpochInfo(options?: GetCommitmentMinContextSlotOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getEpochInfo', [options]), - ) - } - - getEpochSchedule(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getEpochSchedule'), - ) - } - - getFeeForMessage( - message: any, - options?: GetCommitmentMinContextSlotOptions, - ): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getFeeForMessage', [message, options]), - ) - } - - getFirstAvailableBlock(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getFirstAvailableBlock'), - ) - } - getGenesisHash(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getGenesisHash'), - ) - } - getHealth(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getHealth'), - ) - } - getHighestSnapshotSlot(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getHighestSnapshotSlot'), - ) - } - - getIdentity(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getIdentity'), - ) - } - getInflationGovernor(options?: GetCommitmentOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getInflationGovernor', [options]), - ) - } - getInflationRate(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getInflationRate'), - ) - } - getInflationReward( - addresses?: string[], - options?: GetInflationRewardOptions, - ): Promise>> { - let params: any = [] - if (addresses) params = [addresses] - if (options) params = [addresses, options] - - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getInflationReward', params), - ) - } - getLargestAccounts( - options?: GetLargestAccountsOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getLargestAccounts', [options]), - ) - } - getLatestBlockhash( - options?: GetCommitmentMinContextSlotOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getLatestBlockhash', [options]), - ) - } - getLeaderSchedule( - slot?: number, - options?: GetLeaderScheduleOptions, - ): Promise> { - let params: any = [] - if (slot) params = [slot] - if (options) params = [slot, options] - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getLeaderSchedule', params), - ) - } - getMaxRetransmitSlot(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getMaxRetransmitSlot'), - ) - } - getMaxShredInsertSlot(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getMaxShredInsertSlot'), - ) - } - getMinimumBalanceForRentExemption( - dataSize?: number, - options?: GetCommitmentOptions, - ): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getMinimumBalanceForRentExemption', [dataSize, options]), - ) - } - getMultipleAccounts( - pubKeys?: string[], - options?: GetMultipleAccountsOptions, - ): Promise>>> { - return this.connector.rpcCall>>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getMultipleAccounts', [pubKeys, options]), - ) - } - getProgramAccounts( - programId: string, - options?: GetProgramAccountsOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getProgramAccounts', [programId, options]), - ) - } - getRecentPerformanceSamples(limit?: number): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getRecentPerformanceSamples', [limit]), - ) - } - getRecentPrioritizationFees( - addresses?: string[], - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getRecentPrioritizationFees', [addresses]), - ) - } - - getSignaturesForAddress( - address: string, - options?: GetSignaturesForAddressOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSignaturesForAddress', [address, options]), - ) - } - getSignatureStatuses( - signatures?: string[], - options?: GetSignatureStatusesOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSignatureStatuses', [signatures, options]), - ) - } - getSlot(options?: GetCommitmentMinContextSlotOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSlot', [options]), - ) - } - getSlotLeader(options?: GetCommitmentMinContextSlotOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSlotLeader', [options]), - ) - } - getSlotLeaders(startSlot?: number, limit?: number): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSlotLeaders', [startSlot, limit]), - ) - } - getStakeActivation( - pubkey: string, - options?: GetStakeActivationOptions, - ): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getStakeActivation', [pubkey, options]), - ) - } - getStakeMinimumDelegation( - options?: GetCommitmentOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getStakeMinimumDelegation', [options]), - ) - } - getSupply(options?: GetSupplyOptions): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getSupply', [options]), - ) - } - getTokenAccountBalance( - pubkey: string, - options?: GetCommitmentOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTokenAccountBalance', [pubkey, options]), - ) - } - getTokenAccountsByDelegate( - pubkey: string, - config?: SolanaMint | SolanaProgramId, - options?: GetTokenAccountsOptions, - ): Promise>> { - const params: any[] = [pubkey] - if (config) params.push(config) - if (options) params.push(options) - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTokenAccountsByDelegate', params), - ) - } - getTokenAccountsByOwner( - pubkey: string, - config?: SolanaMint | SolanaProgramId, - options?: GetTokenAccountsOptions, - ): Promise>> { - const params: any[] = [pubkey] - if (config) params.push(config) - if (options) params.push(options) - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTokenAccountsByOwner', params), - ) - } - getTokenLargestAccounts( - pubkey: string, - options?: GetCommitmentOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTokenLargestAccounts', [pubkey, options]), - ) - } - getTokenSupply( - pubkey: string, - options?: GetCommitmentOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTokenSupply', [pubkey, options]), - ) - } - getTransaction( - signature: string, - options?: GetTransactionOptions, - ): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTransaction', [signature, options]), - ) - } - getTransactionCount(options?: GetCommitmentMinContextSlotOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getTransactionCount', [options]), - ) - } - getVersion(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('getVersion'), - ) - } - getVoteAccounts( - options?: GetVoteAccountOptions, - ): Promise; delinquent: Array }>> { - return this.connector.rpcCall< - JsonRpcResponse<{ current: Array; delinquent: Array }> - >(this.getRpcNodeUrl(), Utils.prepareRpcCall('getVoteAccounts', [options])) - } - isBlockhashValid( - blockhash: string, - options?: GetCommitmentMinContextSlotOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('isBlockhashValid', [blockhash, options]), - ) - } - minimumLedgerSlot(): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('minimumLedgerSlot'), - ) - } - requestAirdrop( - pubkey: string, - amount: number, - options?: GetCommitmentOptions, - ): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('requestAirdrop', [pubkey, amount, options]), - ) - } - sendTransaction(transaction: string, options?: SendTransactionOptions): Promise> { - return this.connector.rpcCall>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('sendTransaction', [transaction, options]), - ) - } - simulateTransaction( - transaction: string, - options?: SimulateTransactionOptions, - ): Promise>> { - return this.connector.rpcCall>>( - this.getRpcNodeUrl(), - Utils.prepareRpcCall('simulateTransaction', [transaction, options]), - ) - } -} diff --git a/src/util/util.shared.ts b/src/util/util.shared.ts index 3afb04eea7..1f2dde5710 100644 --- a/src/util/util.shared.ts +++ b/src/util/util.shared.ts @@ -9,8 +9,8 @@ import { isEvmArchiveNonArchiveLoadBalancerNetwork, isEvmBasedNetwork, isEvmLoadBalancerNetwork, - isNativeEvmLoadBalancerNetwork, - isSolanaEnabledNetwork, + isNativeEvmLoadBalancerNetwork, isSameGetBlockNetwork, + isSolanaNetwork, isTronLoadBalancerNetwork, isTronNetwork, isUtxoBasedNetwork, @@ -56,7 +56,6 @@ import { Palm, Polygon, Solana, - SolanaRpc, TatumConfig, Tezos, Tron, @@ -76,6 +75,7 @@ import { EosRpc } from '../service/rpc/other/EosRpc' import { XrpLoadBalancerRpc } from '../service/rpc/other/XrpLoadBalancerRpc' import { Constant } from './constant' import { CONFIG } from './di.tokens' +import { SolanaLoadBalancerRpc } from '../service/rpc/other/SolanaLoadBalancerRpc' export const Utils = { getRpc: (id: string, config: TatumConfig): T => { @@ -108,8 +108,8 @@ export const Utils = { return Container.of(id).get(XrpLoadBalancerRpc) as T } - if (isSolanaEnabledNetwork(network)) { - return Container.of(id).get(SolanaRpc) as T + if (isSolanaNetwork(network)) { + return Container.of(id).get(SolanaLoadBalancerRpc) as T } if (isTronLoadBalancerNetwork(network)) { @@ -151,6 +151,7 @@ export const Utils = { id: 1, } } + if (isEvmBasedNetwork(network) || isTronNetwork(network)) { return { jsonrpc: '2.0', @@ -160,6 +161,15 @@ export const Utils = { } } + if (isSolanaNetwork(network)) { + return { + jsonrpc: '2.0', + method: 'getBlockHeight', + params: [], + id: 1, + } + } + if (isEosNetwork(network)) { return null } @@ -171,14 +181,14 @@ export const Utils = { return `${url}${Constant.EOS_PREFIX}get_info` } - if (isUtxoBasedNetwork(network) || isEvmBasedNetwork(network) || isTronNetwork(network)) { + if (isSameGetBlockNetwork(network)) { return url } throw new Error(`Network ${network} is not supported.`) }, parseStatusPayload: (network: Network, response: JsonRpcResponse | any) => { - if (isUtxoBasedNetwork(network) || isEvmBasedNetwork(network) || isTronNetwork(network)) { + if (isSameGetBlockNetwork(network)) { return new BigNumber((response.result as number) || -1).toNumber() } @@ -193,7 +203,7 @@ export const Utils = { return response.head_block_num !== undefined } - if (isUtxoBasedNetwork(network) || isEvmBasedNetwork(network) || isTronNetwork(network)) { + if (isSameGetBlockNetwork(network)) { return response.result !== undefined }