Claim price impact
This commit is contained in:
@@ -167,12 +167,7 @@ const claimFundingFeesSchema = z.object({
|
|||||||
|
|
||||||
// Schema for claim price impact request
|
// Schema for claim price impact request
|
||||||
const claimPriceImpactSchema = z.object({
|
const claimPriceImpactSchema = z.object({
|
||||||
account: z.string().nonempty(),
|
account: z.string().nonempty()
|
||||||
claimablePositionPriceImpactFees: z.array(z.object({
|
|
||||||
marketAddress: z.string().nonempty(),
|
|
||||||
tokenAddress: z.string().nonempty(),
|
|
||||||
timeKey: z.number().positive()
|
|
||||||
}))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Schema for get price impact rebates request
|
// 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
|
* Implementation function to claim price impact rebates
|
||||||
* @param sdk The GMX SDK client
|
* @param sdk The GMX SDK client
|
||||||
* @param claimablePositionPriceImpactFees Array of claimable position price impact fees
|
|
||||||
* @returns Transaction hash
|
* @returns Transaction hash
|
||||||
*/
|
*/
|
||||||
export const claimGmxPriceImpactImpl = async (
|
export const claimGmxPriceImpactImpl = async (
|
||||||
sdk: GmxSdk,
|
sdk: GmxSdk
|
||||||
claimablePositionPriceImpactFees: ClaimablePositionPriceImpactFee[]
|
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
try {
|
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");
|
throw new Error("No price impact fees available to claim");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1356,7 +1335,7 @@ export const claimGmxPriceImpactImpl = async (
|
|||||||
const tokens: string[] = [];
|
const tokens: string[] = [];
|
||||||
const timeKeys: number[] = [];
|
const timeKeys: number[] = [];
|
||||||
|
|
||||||
claimablePositionPriceImpactFees.forEach((fee) => {
|
rebatesInfo.claimablePositionPriceImpactFees.forEach((fee) => {
|
||||||
markets.push(fee.marketAddress);
|
markets.push(fee.marketAddress);
|
||||||
tokens.push(fee.tokenAddress);
|
tokens.push(fee.tokenAddress);
|
||||||
timeKeys.push(fee.timeKey);
|
timeKeys.push(fee.timeKey);
|
||||||
@@ -1387,24 +1366,22 @@ export const claimGmxPriceImpactImpl = async (
|
|||||||
* @param this The FastifyRequest instance
|
* @param this The FastifyRequest instance
|
||||||
* @param reply The FastifyReply instance
|
* @param reply The FastifyReply instance
|
||||||
* @param account The wallet address of the user
|
* @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
|
* @returns The response object with success status and transaction hash
|
||||||
*/
|
*/
|
||||||
export async function claimGmxPriceImpact(
|
export async function claimGmxPriceImpact(
|
||||||
this: FastifyRequest,
|
this: FastifyRequest,
|
||||||
reply: FastifyReply,
|
reply: FastifyReply,
|
||||||
account: string,
|
account: string
|
||||||
claimablePositionPriceImpactFees: ClaimablePositionPriceImpactFee[]
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
// Validate the request parameters
|
// Validate the request parameters
|
||||||
claimPriceImpactSchema.parse({ account, claimablePositionPriceImpactFees });
|
claimPriceImpactSchema.parse({ account });
|
||||||
|
|
||||||
// Get client for the address
|
// Get client for the address
|
||||||
const sdk = await this.getClientForAddress(account);
|
const sdk = await this.getClientForAddress(account);
|
||||||
|
|
||||||
// Call the implementation function
|
// Call the implementation function
|
||||||
const hash = await claimGmxPriceImpactImpl(sdk, claimablePositionPriceImpactFees);
|
const hash = await claimGmxPriceImpactImpl(sdk);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -5,153 +5,115 @@ import {claimGmxPriceImpactImpl, getClientForAddress} from '../../src/plugins/cu
|
|||||||
test('GMX Claim Price Impact', async (t) => {
|
test('GMX Claim Price Impact', async (t) => {
|
||||||
const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f';
|
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 {
|
try {
|
||||||
const sdk = await getClientForAddress(testAccount);
|
const sdk = await getClientForAddress(testAccount);
|
||||||
|
const result = await claimGmxPriceImpactImpl(sdk);
|
||||||
// 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);
|
|
||||||
|
|
||||||
console.log('Claim price impact result:', result);
|
console.log('Claim price impact result:', result);
|
||||||
|
|
||||||
|
// Should return a success indicator
|
||||||
assert.ok(typeof result === 'string', 'Result should be a string');
|
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');
|
assert.equal(result, 'price_impact_claimed', 'Should return success indicator');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Expected error in test environment:', error.message);
|
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');
|
assert.ok(error instanceof Error, 'Should throw an Error instance');
|
||||||
|
|
||||||
// Check for expected error messages
|
// Should be a meaningful error message
|
||||||
const errorMessage = error.message;
|
assert.ok(
|
||||||
const expectedErrors = [
|
error.message.includes('No price impact fees available to claim') ||
|
||||||
'Failed to claim price impact rebates',
|
error.message.includes('Failed to claim price impact rebates') ||
|
||||||
'No price impact fees available to claim'
|
error.message.includes('Error claiming price impact rebates'),
|
||||||
];
|
'Error message should be meaningful'
|
||||||
|
|
||||||
const hasExpectedError = expectedErrors.some(expectedError =>
|
|
||||||
errorMessage.includes(expectedError)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
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 () => {
|
await t.test('should handle SDK client creation for claim', 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 () => {
|
|
||||||
try {
|
try {
|
||||||
const sdk = await getClientForAddress(testAccount);
|
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(typeof sdk === 'object', 'SDK should be an object');
|
||||||
assert.ok(sdk.account === testAccount, 'SDK account should match test account');
|
assert.ok(sdk.account === testAccount, 'SDK account should match test account');
|
||||||
assert.ok(typeof sdk.chainId === 'number', 'Chain ID should be a number');
|
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) {
|
} catch (error) {
|
||||||
console.warn('Expected error in test environment:', error.message);
|
console.warn('Expected error in test environment:', error.message);
|
||||||
assert.ok(error instanceof Error, 'Should throw an Error instance');
|
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 {
|
try {
|
||||||
const sdk = await getClientForAddress(testAccount);
|
const sdk = await getClientForAddress(testAccount);
|
||||||
|
|
||||||
// Test with multiple different fees
|
// The method should now automatically fetch claimable fees using GraphQL
|
||||||
const multipleFees = [
|
// This test verifies that the method integrates with getGmxPriceImpactRebatesImpl
|
||||||
{
|
const result = await claimGmxPriceImpactImpl(sdk);
|
||||||
marketAddress: '0x47c031236e19d024b42f8AE6780E44A573170703',
|
|
||||||
tokenAddress: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1',
|
|
||||||
timeKey: 1640995200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
marketAddress: '0x70d95587d40A2caf56bd97485aB3Eec10Bee6336',
|
|
||||||
tokenAddress: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
|
|
||||||
timeKey: 1640995300
|
|
||||||
},
|
|
||||||
{
|
|
||||||
marketAddress: '0x09400D9DB990D5ed3f35D7be61DfAEB900Af03C9',
|
|
||||||
tokenAddress: '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f',
|
|
||||||
timeKey: 1640995400
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = await claimGmxPriceImpactImpl(sdk, multipleFees);
|
console.log('Auto-fetch and claim result:', result);
|
||||||
|
|
||||||
console.log('Multiple fees claim result:', result);
|
// If successful, should return the success indicator
|
||||||
assert.ok(typeof result === 'string', 'Should return transaction hash or success indicator');
|
assert.equal(result, 'price_impact_claimed', 'Should return success indicator');
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Expected errors during the claiming process
|
console.warn('Expected error in test environment:', error.message);
|
||||||
const errorMessage = error.message;
|
assert.ok(error instanceof Error, 'Should throw an Error instance');
|
||||||
const validClaimErrors = [
|
|
||||||
'Failed to claim price impact rebates',
|
|
||||||
'No price impact fees available to claim'
|
|
||||||
];
|
|
||||||
|
|
||||||
const isValidError = validClaimErrors.some(validError =>
|
// Should handle the case where no claimable fees are found
|
||||||
errorMessage.includes(validError)
|
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');
|
||||||
if (isValidError) {
|
|
||||||
console.log('Multiple fees claim handled expected error:', errorMessage);
|
|
||||||
assert.ok(true, 'Expected error in claim process');
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('Unexpected error in multiple fees claim:', errorMessage);
|
assert.ok(
|
||||||
assert.ok(error instanceof Error, 'Should still be an Error instance');
|
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'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user