diff --git a/README.md b/README.md index ba3bd88..c3db640 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,13 @@ All compact operations require a valid session ID in the `x-session-id` header. ``` 3. **Get Specific Compact** + ```http GET /compact/:chainId/:claimHash ``` + Example response: + ```json { "chainId": "10", @@ -156,6 +159,24 @@ All compact operations require a valid session ID in the `x-session-id` header. } ``` +4. **Get Resource Lock Balance** + ```http + GET /balance/:chainId/:lockId + ``` + Returns balance information for a specific resource lock. Example response: + ```json + { + "allocatableBalance": "1000000000000000000", + "allocatedBalance": "500000000000000000", + "balanceAvailableToAllocate": "500000000000000000", + "withdrawalStatus": 0 + } + ``` + The `balanceAvailableToAllocate` will be: + - `"0"` if `withdrawalStatus` is non-zero + - `"0"` if `allocatedBalance` >= `allocatableBalance` + - `allocatableBalance - allocatedBalance` otherwise + ## Development ### Prerequisites diff --git a/src/__tests__/routes.test.ts b/src/__tests__/routes.test.ts index df9d8f0..d3d35c0 100644 --- a/src/__tests__/routes.test.ts +++ b/src/__tests__/routes.test.ts @@ -384,15 +384,45 @@ describe('API Routes', () => { }); it('should return 404 for non-existent lock', async () => { - const response = await server.inject({ - method: 'GET', - url: '/balance/1/0x0000000000000000000000000000000000000000000000000000000000000000', - headers: { - 'x-session-id': sessionId, + // Store original function + const originalRequest = graphqlClient.request; + + // Mock GraphQL response with no resource lock + graphqlClient.request = async (): Promise< + AllocatorResponse & AccountDeltasResponse & AccountResponse + > => ({ + allocator: { + supportedChains: { + items: [{ allocatorId: '1' }], + }, + }, + accountDeltas: { + items: [], + }, + account: { + resourceLocks: { + items: [], // Empty array indicates no resource lock found + }, + claims: { + items: [], + }, }, }); - expect(response.statusCode).toBe(404); + try { + const response = await server.inject({ + method: 'GET', + url: '/balance/1/0x0000000000000000000000000000000000000000000000000000000000000000', + headers: { + 'x-session-id': sessionId, + }, + }); + + expect(response.statusCode).toBe(404); + } finally { + // Restore original function + graphqlClient.request = originalRequest; + } }); it('should return zero balanceAvailableToAllocate when withdrawal enabled', async () => { diff --git a/src/routes.ts b/src/routes.ts index be6f820..1a2c958 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -258,12 +258,6 @@ export async function setupRoutes(server: FastifyInstance): Promise { const { chainId, lockId } = request.params; const sponsor = request.session.address; - // Extract allocatorId from the lockId - const lockIdBigInt = BigInt(lockId); - const allocatorId = - (lockIdBigInt >> BigInt(160)) & - ((BigInt(1) << BigInt(92)) - BigInt(1)); - // Get details from GraphQL const response = await getCompactDetails({ allocator: process.env.ALLOCATOR_ADDRESS!, @@ -279,6 +273,12 @@ export async function setupRoutes(server: FastifyInstance): Promise { return { error: 'Resource lock not found' }; } + // Extract allocatorId from the lockId + const lockIdBigInt = BigInt(lockId); + const allocatorId = + (lockIdBigInt >> BigInt(160)) & + ((BigInt(1) << BigInt(92)) - BigInt(1)); + // Verify allocatorId matches const graphqlAllocatorId = response.allocator.supportedChains.items[0]?.allocatorId;