From aac28adebe4bc6adeadee1840f1c32578226899e Mon Sep 17 00:00:00 2001 From: cryptooda Date: Thu, 5 Jun 2025 22:13:24 +0700 Subject: [PATCH] Claim price impact --- .../src/plugins/custom/gmx.ts | 43 +---- .../plugins/claim-gmx-price-impact.test.ts | 182 +++++++----------- 2 files changed, 82 insertions(+), 143 deletions(-) diff --git a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts index b1a9113..aec8511 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts @@ -167,12 +167,7 @@ const claimFundingFeesSchema = z.object({ // Schema for claim price impact request const claimPriceImpactSchema = z.object({ - account: z.string().nonempty(), - claimablePositionPriceImpactFees: z.array(z.object({ - marketAddress: z.string().nonempty(), - tokenAddress: z.string().nonempty(), - timeKey: z.number().positive() - })) + account: z.string().nonempty() }); // Schema for get price impact rebates request @@ -1316,35 +1311,19 @@ export async function claimGmxFundingFees( } } -/** - * Interface for claimable position price impact fee - */ -interface ClaimablePositionPriceImpactFee { - marketAddress: string; - tokenAddress: string; - timeKey: number; -} - -/** - * Interface for price impact rebate parameters - */ -interface ClaimPriceImpactRebateParams { - account: string; - claimablePositionPriceImpactFees: ClaimablePositionPriceImpactFee[]; -} - /** * Implementation function to claim price impact rebates * @param sdk The GMX SDK client - * @param claimablePositionPriceImpactFees Array of claimable position price impact fees * @returns Transaction hash */ export const claimGmxPriceImpactImpl = async ( - sdk: GmxSdk, - claimablePositionPriceImpactFees: ClaimablePositionPriceImpactFee[] + sdk: GmxSdk ): Promise => { try { - if (!claimablePositionPriceImpactFees || claimablePositionPriceImpactFees.length === 0) { + // First, get the claimable position price impact fees using our GraphQL method + const rebatesInfo = await getGmxPriceImpactRebatesImpl(sdk); + + if (!rebatesInfo.claimablePositionPriceImpactFees || rebatesInfo.claimablePositionPriceImpactFees.length === 0) { throw new Error("No price impact fees available to claim"); } @@ -1356,7 +1335,7 @@ export const claimGmxPriceImpactImpl = async ( const tokens: string[] = []; const timeKeys: number[] = []; - claimablePositionPriceImpactFees.forEach((fee) => { + rebatesInfo.claimablePositionPriceImpactFees.forEach((fee) => { markets.push(fee.marketAddress); tokens.push(fee.tokenAddress); timeKeys.push(fee.timeKey); @@ -1387,24 +1366,22 @@ export const claimGmxPriceImpactImpl = async ( * @param this The FastifyRequest instance * @param reply The FastifyReply instance * @param account The wallet address of the user - * @param claimablePositionPriceImpactFees Array of claimable position price impact fees * @returns The response object with success status and transaction hash */ export async function claimGmxPriceImpact( this: FastifyRequest, reply: FastifyReply, - account: string, - claimablePositionPriceImpactFees: ClaimablePositionPriceImpactFee[] + account: string ) { try { // Validate the request parameters - claimPriceImpactSchema.parse({ account, claimablePositionPriceImpactFees }); + claimPriceImpactSchema.parse({ account }); // Get client for the address const sdk = await this.getClientForAddress(account); // Call the implementation function - const hash = await claimGmxPriceImpactImpl(sdk, claimablePositionPriceImpactFees); + const hash = await claimGmxPriceImpactImpl(sdk); return { success: true, diff --git a/src/Managing.Web3Proxy/test/plugins/claim-gmx-price-impact.test.ts b/src/Managing.Web3Proxy/test/plugins/claim-gmx-price-impact.test.ts index c4d827e..89917cf 100644 --- a/src/Managing.Web3Proxy/test/plugins/claim-gmx-price-impact.test.ts +++ b/src/Managing.Web3Proxy/test/plugins/claim-gmx-price-impact.test.ts @@ -5,153 +5,115 @@ import {claimGmxPriceImpactImpl, getClientForAddress} from '../../src/plugins/cu test('GMX Claim Price Impact', async (t) => { const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f'; - await t.test('should claim price impact for valid account with fees', async () => { + await t.test('should claim price impact rebates for valid account with claimable fees', async () => { try { const sdk = await getClientForAddress(testAccount); - - // Mock claimable position price impact fees - const mockClaimableFees = [ - { - marketAddress: '0x47c031236e19d024b42f8AE6780E44A573170703', - tokenAddress: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - timeKey: 1640995200 - }, - { - marketAddress: '0x70d95587d40A2caf56bd97485aB3Eec10Bee6336', - tokenAddress: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - timeKey: 1640995300 - } - ]; - - const result = await claimGmxPriceImpactImpl(sdk, mockClaimableFees); + const result = await claimGmxPriceImpactImpl(sdk); console.log('Claim price impact result:', result); + + // Should return a success indicator assert.ok(typeof result === 'string', 'Result should be a string'); - assert.ok(result.length > 0, 'Result should not be empty'); assert.equal(result, 'price_impact_claimed', 'Should return success indicator'); + } catch (error) { console.warn('Expected error in test environment:', error.message); - // Expected behavior - may fail in test environment assert.ok(error instanceof Error, 'Should throw an Error instance'); - // Check for expected error messages - const errorMessage = error.message; - const expectedErrors = [ - 'Failed to claim price impact rebates', - 'No price impact fees available to claim' - ]; - - const hasExpectedError = expectedErrors.some(expectedError => - errorMessage.includes(expectedError) + // Should be a meaningful error message + assert.ok( + error.message.includes('No price impact fees available to claim') || + error.message.includes('Failed to claim price impact rebates') || + error.message.includes('Error claiming price impact rebates'), + 'Error message should be meaningful' ); - - if (!hasExpectedError) { - // Log unexpected errors for debugging - console.warn('Unexpected error in claimGmxPriceImpactImpl:', errorMessage); - } - - // Still assert it's an error for test completeness - assert.ok(true, 'Expected error occurred'); } }); - await t.test('should handle empty claimable fees array', async () => { - try { - const sdk = await getClientForAddress(testAccount); - const emptyFees = []; - - await claimGmxPriceImpactImpl(sdk, emptyFees); - - // Should not reach here - assert.fail('Should have thrown an error for empty fees array'); - } catch (error) { - // Expected behavior for empty array - assert.ok(error instanceof Error, 'Should throw an Error instance'); - assert.ok(error.message.includes('No price impact fees available to claim'), 'Should have specific error message'); - console.log('Correctly handled empty fees array'); - } - }); - - await t.test('should handle null claimable fees', async () => { - try { - const sdk = await getClientForAddress(testAccount); - const nullFees = null as any; - - await claimGmxPriceImpactImpl(sdk, nullFees); - - // Should not reach here - assert.fail('Should have thrown an error for null fees'); - } catch (error) { - // Expected behavior for null input - assert.ok(error instanceof Error, 'Should throw an Error instance'); - assert.ok(error.message.includes('No price impact fees available to claim'), 'Should have specific error message'); - console.log('Correctly handled null fees'); - } - }); - - await t.test('should handle SDK client creation for price impact claiming', async () => { + await t.test('should handle SDK client creation for claim', async () => { try { const sdk = await getClientForAddress(testAccount); - // Validate SDK properties before attempting to claim + // Validate SDK properties assert.ok(typeof sdk === 'object', 'SDK should be an object'); assert.ok(sdk.account === testAccount, 'SDK account should match test account'); assert.ok(typeof sdk.chainId === 'number', 'Chain ID should be a number'); - console.log('SDK validation passed for price impact claiming'); + console.log('SDK validation passed for claim'); } catch (error) { console.warn('Expected error in test environment:', error.message); assert.ok(error instanceof Error, 'Should throw an Error instance'); } }); - await t.test('should validate claim process with multiple fees', async () => { + await t.test('should automatically fetch claimable fees before claiming', async () => { try { const sdk = await getClientForAddress(testAccount); - // Test with multiple different fees - const multipleFees = [ - { - marketAddress: '0x47c031236e19d024b42f8AE6780E44A573170703', - tokenAddress: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - timeKey: 1640995200 - }, - { - marketAddress: '0x70d95587d40A2caf56bd97485aB3Eec10Bee6336', - tokenAddress: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', - timeKey: 1640995300 - }, - { - marketAddress: '0x09400D9DB990D5ed3f35D7be61DfAEB900Af03C9', - tokenAddress: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', - timeKey: 1640995400 - } - ]; + // The method should now automatically fetch claimable fees using GraphQL + // This test verifies that the method integrates with getGmxPriceImpactRebatesImpl + const result = await claimGmxPriceImpactImpl(sdk); - const result = await claimGmxPriceImpactImpl(sdk, multipleFees); + console.log('Auto-fetch and claim result:', result); - console.log('Multiple fees claim result:', result); - assert.ok(typeof result === 'string', 'Should return transaction hash or success indicator'); + // If successful, should return the success indicator + assert.equal(result, 'price_impact_claimed', 'Should return success indicator'); } catch (error) { - // Expected errors during the claiming process - const errorMessage = error.message; - const validClaimErrors = [ - 'Failed to claim price impact rebates', - 'No price impact fees available to claim' - ]; + console.warn('Expected error in test environment:', error.message); + assert.ok(error instanceof Error, 'Should throw an Error instance'); - const isValidError = validClaimErrors.some(validError => - errorMessage.includes(validError) - ); - - if (isValidError) { - console.log('Multiple fees claim handled expected error:', errorMessage); - assert.ok(true, 'Expected error in claim process'); + // Should handle the case where no claimable fees are found + if (error.message.includes('No price impact fees available to claim')) { + console.log('No claimable fees found - this is expected for test account'); + assert.ok(true, 'Correctly handled no claimable fees case'); } else { - console.warn('Unexpected error in multiple fees claim:', errorMessage); - assert.ok(error instanceof Error, 'Should still be an Error instance'); + assert.ok( + error.message.includes('Failed to claim price impact rebates') || + error.message.includes('Error claiming price impact rebates'), + 'Should be a meaningful claim error' + ); } } }); + + await t.test('should handle chain ID validation for claim', async () => { + try { + const sdk = await getClientForAddress(testAccount); + + // Should only work for Arbitrum (chainId 42161) + assert.equal(sdk.chainId, 42161, 'Should be using Arbitrum chain ID'); + + console.log('Chain ID validation passed for claim'); + + } catch (error) { + console.warn('Expected error in test environment:', error.message); + assert.ok(error instanceof Error, 'Should throw an Error instance'); + } + }); + + await t.test('should validate contract addresses for claim', async () => { + try { + const sdk = await getClientForAddress(testAccount); + + // The method should validate that proper contract addresses are available + // This tests the ExchangeRouter contract address retrieval + await claimGmxPriceImpactImpl(sdk); + + console.log('Contract validation passed'); + + } catch (error) { + console.warn('Expected error in test environment:', error.message); + assert.ok(error instanceof Error, 'Should throw an Error instance'); + + // Validate error messages are meaningful + assert.ok( + error.message.includes('No price impact fees available to claim') || + error.message.includes('Failed to claim price impact rebates') || + error.message.includes('Unsupported chain ID') || + error.message.includes('Error claiming price impact rebates'), + 'Should provide meaningful error messages' + ); + } + }); }); \ No newline at end of file