Skip to main content
Glama

MPC Tally API Server

tally.service.delegators.test.ts7.85 kB
import { TallyService } from '../tally.service'; import dotenv from 'dotenv'; dotenv.config(); const apiKey = process.env.TALLY_API_KEY; if (!apiKey) { throw new Error('TALLY_API_KEY environment variable is required'); } // Helper function to add delay between API calls const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); describe('TallyService - getDelegators', () => { const service = new TallyService({ apiKey }); // Test constants const UNISWAP_ORG_ID = '2206072050458560434'; const UNISWAP_SLUG = 'uniswap'; const VITALIK_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; // Add delay between each test beforeEach(async () => { await delay(1000); // 1 second delay between tests }); it.only('should fetch delegators using organization ID', async () => { const result = await service.getDelegators({ address: VITALIK_ADDRESS, organizationSlug: 'uniswap', limit: 5, sortBy: 'votes', isDescending: true }); // Check response structure expect(result).toHaveProperty('delegators'); expect(result).toHaveProperty('pageInfo'); expect(Array.isArray(result.delegators)).toBe(true); // Check pageInfo structure expect(result.pageInfo).toHaveProperty('firstCursor'); expect(result.pageInfo).toHaveProperty('lastCursor'); // If there are delegators, check their structure if (result.delegators.length > 0) { const delegation = result.delegators[0]; expect(delegation).toHaveProperty('chainId'); expect(delegation).toHaveProperty('delegator'); expect(delegation).toHaveProperty('blockNumber'); expect(delegation).toHaveProperty('blockTimestamp'); expect(delegation).toHaveProperty('votes'); // Check delegator structure expect(delegation.delegator).toHaveProperty('address'); // Check token structure if present if (delegation.token) { expect(delegation.token).toHaveProperty('id'); expect(delegation.token).toHaveProperty('name'); expect(delegation.token).toHaveProperty('symbol'); expect(delegation.token).toHaveProperty('decimals'); } } }); it('should fetch delegators using organization slug', async () => { const result = await service.getDelegators({ address: VITALIK_ADDRESS, organizationSlug: UNISWAP_SLUG, limit: 5, sortBy: 'votes', isDescending: true }); expect(result).toHaveProperty('delegators'); expect(result).toHaveProperty('pageInfo'); expect(Array.isArray(result.delegators)).toBe(true); await delay(1000); // Add delay before second API call // Results should be the same whether using ID or slug const resultWithId = await service.getDelegators({ address: VITALIK_ADDRESS, organizationId: UNISWAP_ORG_ID, limit: 5, sortBy: 'votes', isDescending: true }); // Compare the results after sorting by blockNumber to ensure consistent comparison const sortByBlockNumber = (a: any, b: any) => a.blockNumber - b.blockNumber; const sortedSlugResults = [...result.delegators].sort(sortByBlockNumber); const sortedIdResults = [...resultWithId.delegators].sort(sortByBlockNumber); // Compare the first delegator if exists if (sortedSlugResults.length > 0 && sortedIdResults.length > 0) { expect(sortedSlugResults[0].blockNumber).toBe(sortedIdResults[0].blockNumber); expect(sortedSlugResults[0].votes).toBe(sortedIdResults[0].votes); } }); it('should handle pagination correctly', async () => { // First page with smaller limit to ensure multiple pages const firstPage = await service.getDelegators({ address: VITALIK_ADDRESS, organizationId: UNISWAP_ORG_ID, // Using ID instead of slug for consistency limit: 1, // Request just 1 item to ensure we have more pages sortBy: 'votes', isDescending: true }); // Verify first page structure expect(firstPage).toHaveProperty('delegators'); expect(firstPage).toHaveProperty('pageInfo'); expect(Array.isArray(firstPage.delegators)).toBe(true); expect(firstPage.delegators.length).toBe(1); // Should have exactly 1 item expect(firstPage.pageInfo).toHaveProperty('firstCursor'); expect(firstPage.pageInfo).toHaveProperty('lastCursor'); expect(firstPage.pageInfo.lastCursor).toBeTruthy(); // Ensure we have a cursor for next page // Store first page data for comparison const firstPageDelegator = firstPage.delegators[0]; await delay(1000); // Add delay before fetching second page // Only proceed if we have a valid cursor if (firstPage.pageInfo.lastCursor) { // Fetch second page using lastCursor from first page const secondPage = await service.getDelegators({ address: VITALIK_ADDRESS, organizationId: UNISWAP_ORG_ID, limit: 1, afterCursor: firstPage.pageInfo.lastCursor, sortBy: 'votes', isDescending: true }); // Verify second page structure expect(secondPage).toHaveProperty('delegators'); expect(secondPage).toHaveProperty('pageInfo'); expect(Array.isArray(secondPage.delegators)).toBe(true); // If we got results in second page, verify they're different if (secondPage.delegators.length > 0) { const secondPageDelegator = secondPage.delegators[0]; // Ensure we got a different delegator expect(secondPageDelegator.delegator.address).not.toBe(firstPageDelegator.delegator.address); // Since we sorted by votes descending, second page votes should be less than or equal expect(BigInt(secondPageDelegator.votes) <= BigInt(firstPageDelegator.votes)).toBe(true); } } }); it('should handle sorting by blockNumber', async () => { const result = await service.getDelegators({ address: VITALIK_ADDRESS, organizationSlug: UNISWAP_SLUG, limit: 5, sortBy: 'votes', isDescending: true }); expect(result).toHaveProperty('delegators'); expect(Array.isArray(result.delegators)).toBe(true); // Verify the results are sorted if (result.delegators.length > 1) { const votes = result.delegators.map(d => BigInt(d.votes)); const isSorted = votes.every((v, i) => i === 0 || v <= votes[i - 1]); expect(isSorted).toBe(true); } }); it('should handle errors for invalid address', async () => { await expect(service.getDelegators({ address: 'invalid-address', organizationSlug: UNISWAP_SLUG })).rejects.toThrow(); }); it('should handle errors for invalid organization slug', async () => { await expect(service.getDelegators({ address: VITALIK_ADDRESS, organizationSlug: 'invalid-org-slug' })).rejects.toThrow(); }); it('should handle errors when neither organizationId/Slug nor governorId is provided', async () => { await expect(service.getDelegators({ address: VITALIK_ADDRESS })).rejects.toThrow('Either organizationId/organizationSlug or governorId must be provided'); }); it('should format delegators list correctly', () => { const mockDelegators = [{ chainId: 'eip155:1', delegator: { address: '0x123', name: 'Test Delegator', ens: 'test.eth' }, blockNumber: 12345, blockTimestamp: '2023-01-01T00:00:00Z', votes: '1000000000000000000', token: { id: 'token-id', name: 'Test Token', symbol: 'TEST', decimals: 18 } }]; const formatted = TallyService.formatDelegatorsList(mockDelegators); expect(typeof formatted).toBe('string'); expect(formatted).toContain('Test Delegator'); expect(formatted).toContain('0x123'); expect(formatted).toContain('Test Token'); }); });

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/crazyrabbitLTC/mpc-tally-api-server'

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