Skip to main content
Glama

Grove's MCP Server for Pocket Network

solana-service.test.ts14 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { SolanaService } from '../solana-service.js'; import { BlockchainRPCService } from '../blockchain-service.js'; import type { BlockchainService, RPCMethod } from '../../types.js'; describe('SolanaService', () => { let solanaService: SolanaService; let mockBlockchainService: BlockchainRPCService; beforeEach(() => { const mockServicesData = { methodAliases: {}, services: [ { id: 'solana-mainnet', name: 'Solana Mainnet', blockchain: 'solana', network: 'mainnet', rpcUrl: 'https://solana.rpc.grove.city/v1/test-app-id', protocol: 'json-rpc', category: 'layer1', supportedMethods: [] as RPCMethod[], }, ] as BlockchainService[], }; mockBlockchainService = new BlockchainRPCService(mockServicesData); solanaService = new SolanaService(mockBlockchainService); vi.restoreAllMocks(); }); describe('getBalance', () => { it('should get SOL balance successfully', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: { value: 1000000000, // 1 SOL }, }); const result = await solanaService.getBalance( 'So11111111111111111111111111111111111111112', 'mainnet' ); expect(result.success).toBe(true); expect(result.data?.lamports).toBe(1000000000); expect(result.data?.sol).toBe(1); }); it('should handle zero balance', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: { value: 0, }, }); const result = await solanaService.getBalance( 'So11111111111111111111111111111111111111112' ); expect(result.success).toBe(true); expect(result.data?.lamports).toBe(0); expect(result.data?.sol).toBe(0); }); it('should return error for non-existent network', async () => { const result = await solanaService.getBalance( 'So11111111111111111111111111111111111111112', 'testnet' as any ); expect(result.success).toBe(false); expect(result.error).toContain('Solana service not found'); }); }); describe('getTokenBalance', () => { it('should get specific SPL token balance', async () => { const mockResponse = { success: true, data: { value: [ { account: { data: { parsed: { info: { tokenAmount: { amount: '1000000', uiAmount: 1.0, decimals: 6, }, }, }, }, }, }, ], }, }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue(mockResponse); const result = await solanaService.getTokenBalance( 'So11111111111111111111111111111111111111112', 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC mint 'mainnet' ); expect(result.success).toBe(true); expect(result.data?.balance).toBe('1000000'); expect(result.data?.uiAmount).toBe(1.0); expect(result.data?.decimals).toBe(6); }); it('should return zero balance when no token account exists', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: { value: [], }, }); const result = await solanaService.getTokenBalance( 'So11111111111111111111111111111111111111112', 'TokenMintAddress', 'mainnet' ); expect(result.success).toBe(true); expect(result.data?.balance).toBe('0'); expect(result.data?.decimals).toBe(0); }); it('should get all SPL token balances', async () => { const mockResponse = { success: true, data: { value: [ { account: { data: { parsed: { info: { mint: 'TokenMint1', tokenAmount: { amount: '1000000', uiAmount: 1.0, decimals: 6, }, }, }, }, }, }, { account: { data: { parsed: { info: { mint: 'TokenMint2', tokenAmount: { amount: '2000000', uiAmount: 2.0, decimals: 6, }, }, }, }, }, }, ], }, }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue(mockResponse); const result = await solanaService.getTokenBalance( 'So11111111111111111111111111111111111111112', undefined, 'mainnet' ); expect(result.success).toBe(true); expect(result.data?.tokens).toHaveLength(2); expect(result.data?.count).toBe(2); }); }); describe('getTransaction', () => { it('should get transaction details', async () => { const mockTx = { slot: 123456, transaction: { message: {}, signatures: ['sig1'], }, }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockTx, }); const result = await solanaService.getTransaction( '5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW', 'mainnet' ); expect(result.success).toBe(true); expect(result.data).toEqual(mockTx); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getTransaction', expect.any(Array) ); }); }); describe('getBlockHeight', () => { it('should get current block height', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: 123456789, }); const result = await solanaService.getBlockHeight('mainnet'); expect(result.success).toBe(true); expect(result.data).toBe(123456789); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getBlockHeight', [] ); }); }); describe('getTokenMetadata', () => { it('should get SPL token metadata', async () => { const mockMetadata = { value: { data: { parsed: { info: { decimals: 6, supply: '1000000000', mintAuthority: 'AuthorityAddress', freezeAuthority: null, isInitialized: true, }, }, }, }, }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockMetadata, }); const result = await solanaService.getTokenMetadata( 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', 'mainnet' ); expect(result.success).toBe(true); expect(result.data?.decimals).toBe(6); expect(result.data?.supply).toBe('1000000000'); expect(result.data?.isInitialized).toBe(true); }); it('should handle invalid token mint', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: { value: null, }, }); const result = await solanaService.getTokenMetadata('InvalidMint', 'mainnet'); expect(result.success).toBe(false); expect(result.error).toContain('Token mint not found'); }); }); describe('getAccountInfo', () => { it('should get account information', async () => { const mockAccount = { lamports: 1000000, owner: 'OwnerProgramId', data: 'base64data', }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockAccount, }); const result = await solanaService.getAccountInfo( 'So11111111111111111111111111111111111111112', 'mainnet' ); expect(result.success).toBe(true); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getAccountInfo', expect.any(Array) ); }); }); describe('getBlock', () => { it('should get block with transactions', async () => { const mockBlock = { blockhash: 'hash123', previousBlockhash: 'hash122', transactions: [], }; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockBlock, }); const result = await solanaService.getBlock(123456, true, 'mainnet'); expect(result.success).toBe(true); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getBlock', expect.arrayContaining([ 123456, expect.objectContaining({ transactionDetails: 'full', }), ]) ); }); it('should get block without full transactions', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: {}, }); await solanaService.getBlock(123456, false, 'mainnet'); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getBlock', expect.arrayContaining([ 123456, expect.objectContaining({ transactionDetails: 'signatures', }), ]) ); }); }); describe('getRecentPrioritizationFees', () => { it('should get recent prioritization fees', async () => { const mockFees = [ { slot: 123, prioritizationFee: 5000 }, { slot: 124, prioritizationFee: 6000 }, ]; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockFees, }); const result = await solanaService.getRecentPrioritizationFees( undefined, 'mainnet' ); expect(result.success).toBe(true); expect(result.data).toEqual(mockFees); }); it('should get fees for specific addresses', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: [], }); await solanaService.getRecentPrioritizationFees( ['Address1', 'Address2'], 'mainnet' ); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getRecentPrioritizationFees', [['Address1', 'Address2']] ); }); }); describe('getFeeForMessage', () => { it('should get fee for a message', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: { value: 5000 }, }); const result = await solanaService.getFeeForMessage( 'base64EncodedMessage', 'mainnet' ); expect(result.success).toBe(true); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getFeeForMessage', ['base64EncodedMessage'] ); }); }); describe('getSignaturesForAddress', () => { it('should get transaction signatures for address', async () => { const mockSignatures = [ { signature: 'sig1', slot: 123 }, { signature: 'sig2', slot: 124 }, ]; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockSignatures, }); const result = await solanaService.getSignaturesForAddress( 'So11111111111111111111111111111111111111112', 10, 'mainnet' ); expect(result.success).toBe(true); expect(result.data).toEqual(mockSignatures); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getSignaturesForAddress', expect.arrayContaining([ 'So11111111111111111111111111111111111111112', expect.objectContaining({ limit: 10 }), ]) ); }); }); describe('getProgramAccounts', () => { it('should get program accounts without filters', async () => { const mockAccounts = [ { pubkey: 'Account1', account: {} }, { pubkey: 'Account2', account: {} }, ]; vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: mockAccounts, }); const result = await solanaService.getProgramAccounts( 'ProgramId', undefined, 'mainnet' ); expect(result.success).toBe(true); expect(result.data).toEqual(mockAccounts); }); it('should get program accounts with filters', async () => { vi.spyOn(mockBlockchainService, 'callRPCMethod').mockResolvedValue({ success: true, data: [], }); const filters = [{ memcmp: { offset: 0, bytes: 'base58data' } }]; await solanaService.getProgramAccounts('ProgramId', filters, 'mainnet'); expect(mockBlockchainService.callRPCMethod).toHaveBeenCalledWith( 'solana-mainnet', 'getProgramAccounts', expect.arrayContaining([ 'ProgramId', expect.objectContaining({ filters, }), ]) ); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/buildwithgrove/mcp-pocket'

If you have feedback or need assistance with the MCP directory API, please join our Discord server