Start claiming rebate
This commit is contained in:
@@ -102,6 +102,7 @@ declare module 'fastify' {
|
||||
closeGmxPosition: typeof closeGmxPosition;
|
||||
getGmxTrade: typeof getGmxTrade;
|
||||
getGmxPositions: typeof getGmxPositions;
|
||||
getGmxRebateStats: typeof getGmxRebateStats;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,6 +861,7 @@ export default fp(async (fastify) => {
|
||||
fastify.decorateRequest('closeGmxPosition', closeGmxPosition)
|
||||
fastify.decorateRequest('getGmxTrade', getGmxTrade)
|
||||
fastify.decorateRequest('getGmxPositions', getGmxPositions)
|
||||
fastify.decorateRequest('getGmxRebateStats', getGmxRebateStats)
|
||||
|
||||
// Pre-populate and refresh the markets cache on startup
|
||||
fastify.addHook('onReady', async () => {
|
||||
@@ -872,4 +874,159 @@ export default fp(async (fastify) => {
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export const getGmxRebateStatsImpl = async (
|
||||
sdk: GmxSdk
|
||||
): Promise<{
|
||||
totalRebateUsd: number;
|
||||
discountUsd: number;
|
||||
volume: number;
|
||||
tier: number;
|
||||
rebateFactor: number;
|
||||
discountFactor: number;
|
||||
} | null> => {
|
||||
try {
|
||||
// Get the referral storage contract address
|
||||
const { getContract } = await import('../../generated/gmxsdk/configs/contracts.js');
|
||||
const { decodeReferralCode } = await import('../../generated/gmxsdk/utils/referrals.js');
|
||||
const { basisPointsToFloat } = await import('../../generated/gmxsdk/utils/numbers.js');
|
||||
const { zeroHash } = await import('viem');
|
||||
|
||||
const referralStorageAddress = getContract(sdk.chainId, "ReferralStorage");
|
||||
|
||||
// Get user referral code
|
||||
const userRefCodeRes = await sdk.executeMulticall({
|
||||
referralStorage: {
|
||||
contractAddress: referralStorageAddress,
|
||||
abiId: "ReferralStorage",
|
||||
calls: {
|
||||
traderReferralCodes: {
|
||||
methodName: "traderReferralCodes",
|
||||
params: [sdk.account],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
console.log("traderReferralCodes", userRefCodeRes.data.referralStorage.traderReferralCodes.returnValues)
|
||||
|
||||
const userReferralCode = userRefCodeRes.data.referralStorage.traderReferralCodes.returnValues[0];
|
||||
const userReferralCodeString = decodeReferralCode(userReferralCode);
|
||||
|
||||
console.log("userReferralCodeAfterDecode", userReferralCodeString)
|
||||
|
||||
// If no referral code, return default values
|
||||
if (!userReferralCode || userReferralCode === zeroHash) {
|
||||
return {
|
||||
totalRebateUsd: 0,
|
||||
discountUsd: 0,
|
||||
volume: 0,
|
||||
tier: 0,
|
||||
rebateFactor: 0,
|
||||
discountFactor: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Get code owner and affiliate tier
|
||||
const [codeOwnerRes, affiliateTierRes] = await Promise.all([
|
||||
sdk.executeMulticall({
|
||||
referralStorage: {
|
||||
contractAddress: referralStorageAddress,
|
||||
abiId: "ReferralStorage",
|
||||
calls: {
|
||||
codeOwner: {
|
||||
methodName: "codeOwners",
|
||||
params: [userReferralCodeString],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
sdk.executeMulticall({
|
||||
referralStorage: {
|
||||
contractAddress: referralStorageAddress,
|
||||
abiId: "ReferralStorage",
|
||||
calls: {
|
||||
referrerTiers: {
|
||||
methodName: "referrerTiers",
|
||||
params: [sdk.account],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
]);
|
||||
|
||||
const codeOwner = codeOwnerRes.data.referralStorage.codeOwner.returnValues[0];
|
||||
const tierId = affiliateTierRes.data.referralStorage.referrerTiers.returnValues[0];
|
||||
|
||||
// Get tier information
|
||||
const tiersRes = await sdk.executeMulticall({
|
||||
referralStorage: {
|
||||
contractAddress: referralStorageAddress,
|
||||
abiId: "ReferralStorage",
|
||||
calls: {
|
||||
tiers: {
|
||||
methodName: "tiers",
|
||||
params: [tierId],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [totalRebate, discountShare] = tiersRes.data.referralStorage.tiers.returnValues ?? [0n, 0n];
|
||||
|
||||
// Get custom discount share if available
|
||||
let customDiscountShare = 0n;
|
||||
if (codeOwner) {
|
||||
const customDiscountRes = await sdk.executeMulticall({
|
||||
referralStorage: {
|
||||
contractAddress: referralStorageAddress,
|
||||
abiId: "ReferralStorage",
|
||||
calls: {
|
||||
referrerDiscountShares: {
|
||||
methodName: "referrerDiscountShares",
|
||||
params: [codeOwner],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
customDiscountShare = customDiscountRes.data.referralStorage.referrerDiscountShares.returnValues[0] || 0n;
|
||||
}
|
||||
|
||||
const finalDiscountShare = customDiscountShare > 0n ? customDiscountShare : discountShare;
|
||||
|
||||
// Convert bigint values to numbers for JSON serialization
|
||||
const totalRebateFactor = basisPointsToFloat(totalRebate);
|
||||
const discountFactor = basisPointsToFloat(finalDiscountShare);
|
||||
|
||||
return {
|
||||
totalRebateUsd: Number(totalRebate) / 1e4, // Convert from basis points to decimal
|
||||
discountUsd: Number(finalDiscountShare) / 1e4, // Convert from basis points to decimal
|
||||
volume: 0, // Volume data would need to be fetched from different endpoint
|
||||
tier: Number(tierId),
|
||||
rebateFactor: Number(totalRebateFactor),
|
||||
discountFactor: Number(discountFactor)
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error getting GMX rebate stats:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export async function getGmxRebateStats(
|
||||
this: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
account: string
|
||||
) {
|
||||
try {
|
||||
const sdk = await this.getClientForAddress(account);
|
||||
const rebateStats = await getGmxRebateStatsImpl(sdk);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
rebateStats
|
||||
};
|
||||
} catch (error) {
|
||||
return handleError(this, reply, error, 'getGmxRebateStats');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +149,37 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
|
||||
account
|
||||
)
|
||||
})
|
||||
|
||||
// Define route to get rebate stats
|
||||
fastify.get('/rebate-stats', {
|
||||
schema: {
|
||||
querystring: Type.Object({
|
||||
account: Type.String()
|
||||
}),
|
||||
response: {
|
||||
200: Type.Object({
|
||||
success: Type.Boolean(),
|
||||
rebateStats: Type.Optional(Type.Object({
|
||||
totalRebateUsd: Type.Number(),
|
||||
discountUsd: Type.Number(),
|
||||
volume: Type.Number(),
|
||||
tier: Type.Number(),
|
||||
rebateFactor: Type.Number(),
|
||||
discountFactor: Type.Number()
|
||||
})),
|
||||
error: Type.Optional(Type.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
}, async (request, reply) => {
|
||||
const { account } = request.query
|
||||
|
||||
// Call the getGmxRebateStats method from the GMX plugin
|
||||
return request.getGmxRebateStats(
|
||||
reply,
|
||||
account
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export default plugin
|
||||
62
src/Managing.Web3Proxy/test/plugins/get-gmx-rebate-stats
Normal file
62
src/Managing.Web3Proxy/test/plugins/get-gmx-rebate-stats
Normal file
@@ -0,0 +1,62 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import { getClientForAddress, getGmxRebateStatsImpl } from '../../src/plugins/custom/gmx'
|
||||
|
||||
test('GMX get rebate stats', async (t) => {
|
||||
await t.test('should get rebate stats for account', async () => {
|
||||
const testAccount = '0x932167388dD9aad41149b3cA23eBD489E2E2DD78'
|
||||
const sdk = await getClientForAddress(testAccount)
|
||||
|
||||
const result = await getGmxRebateStatsImpl(sdk)
|
||||
|
||||
console.log('Rebate stats result:', result)
|
||||
assert.ok(result, 'Rebate stats result should be defined')
|
||||
|
||||
// Check that the result has the expected structure
|
||||
assert.ok(typeof result.totalRebateUsd === 'number', 'totalRebateUsd should be a number')
|
||||
assert.ok(typeof result.discountUsd === 'number', 'discountUsd should be a number')
|
||||
assert.ok(typeof result.volume === 'number', 'volume should be a number')
|
||||
assert.ok(typeof result.tier === 'number', 'tier should be a number')
|
||||
assert.ok(typeof result.rebateFactor === 'number', 'rebateFactor should be a number')
|
||||
assert.ok(typeof result.discountFactor === 'number', 'discountFactor should be a number')
|
||||
|
||||
// All values should be non-negative
|
||||
assert.ok(result.totalRebateUsd >= 0, 'totalRebateUsd should be non-negative')
|
||||
assert.ok(result.discountUsd >= 0, 'discountUsd should be non-negative')
|
||||
assert.ok(result.volume >= 0, 'volume should be non-negative')
|
||||
assert.ok(result.tier >= 0, 'tier should be non-negative')
|
||||
assert.ok(result.rebateFactor >= 0, 'rebateFactor should be non-negative')
|
||||
assert.ok(result.discountFactor >= 0, 'discountFactor should be non-negative')
|
||||
})
|
||||
|
||||
await t.test('should handle account with no referral info', async () => {
|
||||
// Test with a different account that might not have referral data
|
||||
const testAccount = '0x0000000000000000000000000000000000000000'
|
||||
const sdk = await getClientForAddress(testAccount)
|
||||
|
||||
const result = await getGmxRebateStatsImpl(sdk)
|
||||
|
||||
console.log('Rebate stats result for empty account:', result)
|
||||
assert.ok(result, 'Rebate stats result should be defined even for empty account')
|
||||
|
||||
// Should return default values for account with no referral info
|
||||
assert.strictEqual(result.totalRebateUsd, 0, 'totalRebateUsd should be 0 for empty account')
|
||||
assert.strictEqual(result.discountUsd, 0, 'discountUsd should be 0 for empty account')
|
||||
assert.strictEqual(result.volume, 0, 'volume should be 0 for empty account')
|
||||
assert.strictEqual(result.tier, 0, 'tier should be 0 for empty account')
|
||||
assert.strictEqual(result.rebateFactor, 0, 'rebateFactor should be 0 for empty account')
|
||||
assert.strictEqual(result.discountFactor, 0, 'discountFactor should be 0 for empty account')
|
||||
})
|
||||
|
||||
await t.test('should handle errors gracefully', async () => {
|
||||
// Test with an invalid account address to trigger error handling
|
||||
try {
|
||||
const sdk = await getClientForAddress('invalid-address')
|
||||
await getGmxRebateStatsImpl(sdk)
|
||||
assert.fail('Should have thrown an error for invalid address')
|
||||
} catch (error) {
|
||||
assert.ok(error instanceof Error, 'Should throw an Error instance')
|
||||
console.log('Expected error for invalid address:', error.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,62 @@
|
||||
import {test} from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import {getClientForAddress, getGmxRebateStatsImpl} from '../../src/plugins/custom/gmx.ts'
|
||||
|
||||
test('GMX get rebate stats', async (t) => {
|
||||
await t.test('should get rebate stats for account', async () => {
|
||||
const testAccount = '0xbBA4eaA534cbD0EcAed5E2fD6036Aec2E7eE309f'
|
||||
const sdk = await getClientForAddress(testAccount)
|
||||
|
||||
const result = await getGmxRebateStatsImpl(sdk)
|
||||
|
||||
console.log('Rebate stats result:', result)
|
||||
assert.ok(result, 'Rebate stats result should be defined')
|
||||
|
||||
// Check that the result has the expected structure
|
||||
assert.ok(typeof result.totalRebateUsd === 'number', 'totalRebateUsd should be a number')
|
||||
assert.ok(typeof result.discountUsd === 'number', 'discountUsd should be a number')
|
||||
assert.ok(typeof result.volume === 'number', 'volume should be a number')
|
||||
assert.ok(typeof result.tier === 'number', 'tier should be a number')
|
||||
assert.ok(typeof result.rebateFactor === 'number', 'rebateFactor should be a number')
|
||||
assert.ok(typeof result.discountFactor === 'number', 'discountFactor should be a number')
|
||||
|
||||
// All values should be non-negative
|
||||
assert.ok(result.totalRebateUsd >= 0, 'totalRebateUsd should be non-negative')
|
||||
assert.ok(result.discountUsd >= 0, 'discountUsd should be non-negative')
|
||||
assert.ok(result.volume >= 0, 'volume should be non-negative')
|
||||
assert.ok(result.tier >= 0, 'tier should be non-negative')
|
||||
assert.ok(result.rebateFactor >= 0, 'rebateFactor should be non-negative')
|
||||
assert.ok(result.discountFactor >= 0, 'discountFactor should be non-negative')
|
||||
})
|
||||
|
||||
await t.test('should handle account with no referral info', async () => {
|
||||
// Test with a different account that might not have referral data
|
||||
const testAccount = '0x0000000000000000000000000000000000000000'
|
||||
const sdk = await getClientForAddress(testAccount)
|
||||
|
||||
const result = await getGmxRebateStatsImpl(sdk)
|
||||
|
||||
console.log('Rebate stats result for empty account:', result)
|
||||
assert.ok(result, 'Rebate stats result should be defined even for empty account')
|
||||
|
||||
// Should return default values for account with no referral info
|
||||
assert.strictEqual(result.totalRebateUsd, 0, 'totalRebateUsd should be 0 for empty account')
|
||||
assert.strictEqual(result.discountUsd, 0, 'discountUsd should be 0 for empty account')
|
||||
assert.strictEqual(result.volume, 0, 'volume should be 0 for empty account')
|
||||
assert.strictEqual(result.tier, 0, 'tier should be 0 for empty account')
|
||||
assert.strictEqual(result.rebateFactor, 0, 'rebateFactor should be 0 for empty account')
|
||||
assert.strictEqual(result.discountFactor, 0, 'discountFactor should be 0 for empty account')
|
||||
})
|
||||
|
||||
await t.test('should handle errors gracefully', async () => {
|
||||
// Test with an invalid account address to trigger error handling
|
||||
try {
|
||||
const sdk = await getClientForAddress('invalid-address')
|
||||
await getGmxRebateStatsImpl(sdk)
|
||||
assert.fail('Should have thrown an error for invalid address')
|
||||
} catch (error) {
|
||||
assert.ok(error instanceof Error, 'Should throw an Error instance')
|
||||
console.log('Expected error for invalid address:', error.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user