Skip to content

Commit 867bfed

Browse files
authored
feat: pnl api image cards (#2060)
1 parent c09768a commit 867bfed

File tree

4 files changed

+226
-234
lines changed

4 files changed

+226
-234
lines changed

public/configs/v1/env.json

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,8 @@
462462
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
463463
"faucet": "https://faucet.v4dev.dydx.exchange",
464464
"affiliates": "https://dydx.stg.fuul.xyz",
465-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
465+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
466+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
466467
},
467468
"stakingValidators": [],
468469
"featureFlags": {
@@ -504,7 +505,8 @@
504505
"neutronValidator": "https://neutron-testnet-rpc.polkachu.com/",
505506
"geoV2": "https://geo-whitelist-web-mainnet-preview.infrastructure-34d.workers.dev/",
506507
"geo": "https://api.dydx.exchange/v4/geo",
507-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
508+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
509+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
508510
},
509511
"stakingValidators": [],
510512
"featureFlags": {
@@ -549,7 +551,8 @@
549551
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
550552
"faucet": "http://dev3-faucet-lb-public-1644791410.us-east-2.elb.amazonaws.com",
551553
"affiliates": "https://dydx.stg.fuul.xyz",
552-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
554+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
555+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
553556
},
554557
"stakingValidators": [],
555558
"featureFlags": {
@@ -594,7 +597,8 @@
594597
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
595598
"faucet": "https://faucet.v4dev4.dydx.exchange",
596599
"affiliates": "https://dydx.stg.fuul.xyz",
597-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
600+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
601+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
598602
},
599603
"stakingValidators": [],
600604
"featureFlags": {
@@ -636,7 +640,8 @@
636640
"neutronValidator": "https://neutron-testnet-rpc.polkachu.com/",
637641
"geoV2": "https://geo-whitelist-web-mainnet-preview.infrastructure-34d.workers.dev/",
638642
"geo": "https://api.dydx.exchange/v4/geo",
639-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
643+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
644+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
640645
},
641646
"stakingValidators": [],
642647
"featureFlags": {
@@ -680,7 +685,8 @@
680685
"neutronValidator": "https://neutron-testnet-rpc.polkachu.com/",
681686
"geo": "https://api.dydx.exchange/v4/geo",
682687
"geoV2": "https://geo-whitelist-web-mainnet-preview.infrastructure-34d.workers.dev/",
683-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
688+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
689+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
684690
},
685691
"stakingValidators": [],
686692
"featureFlags": {
@@ -724,7 +730,8 @@
724730
"neutronValidator": "https://neutron-testnet-rpc.polkachu.com/",
725731
"geoV2": "https://geo-whitelist-web-mainnet-preview.infrastructure-34d.workers.dev/",
726732
"geo": "https://api.dydx.exchange/v4/geo",
727-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
733+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
734+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
728735
},
729736
"apps": {
730737
"ios": {
@@ -780,7 +787,8 @@
780787
"neutronValidator": "https://neutron-testnet-rpc.polkachu.com/",
781788
"geoV2": "https://geo-whitelist-web-mainnet-preview.infrastructure-34d.workers.dev/",
782789
"geo": "https://api.dydx.exchange/v4/geo",
783-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
790+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
791+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
784792
},
785793
"stakingValidators": [],
786794
"featureFlags": {
@@ -826,7 +834,8 @@
826834
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
827835
"faucet": "https://faucet.v4testnet.dydx.exchange",
828836
"affiliates": "https://dydx.stg.fuul.xyz",
829-
"spotApi": "https://dydx-solana-api-staging-e2fb353831a4.herokuapp.com"
837+
"spotApi": "https://dydx-solana-api-staging-e2fb353831a4.herokuapp.com",
838+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
830839
},
831840
"stakingValidators": [
832841
"dydxvaloper1vvc9vl6z9pu0vt2y79d0ln8zp6qmpmrhxx99h4",
@@ -874,7 +883,8 @@
874883
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
875884
"faucet": "https://faucet.v4testnet.dydx.exchange",
876885
"affiliates": "https://dydx.stg.fuul.xyz",
877-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
886+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
887+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
878888
},
879889
"stakingValidators": [],
880890
"featureFlags": {
@@ -919,7 +929,8 @@
919929
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
920930
"faucet": "https://faucet.v4testnet.dydx.exchange",
921931
"affiliates": "https://dydx.stg.fuul.xyz",
922-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
932+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
933+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
923934
},
924935
"stakingValidators": [],
925936
"featureFlags": {
@@ -964,7 +975,8 @@
964975
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
965976
"faucet": "https://faucet.v4testnet.dydx.exchange",
966977
"affiliates": "https://dydx.stg.fuul.xyz",
967-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
978+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
979+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
968980
},
969981
"stakingValidators": [],
970982
"featureFlags": {
@@ -1009,7 +1021,8 @@
10091021
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
10101022
"faucet": "https://faucet.v4testnet.dydx.exchange",
10111023
"affiliates": "https://dydx.stg.fuul.xyz",
1012-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
1024+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
1025+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
10131026
},
10141027
"stakingValidators": [],
10151028
"featureFlags": {
@@ -1054,7 +1067,8 @@
10541067
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
10551068
"faucet": "https://faucet.v4testnet.dydx.exchange",
10561069
"affiliates": "https://dydx.stg.fuul.xyz",
1057-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
1070+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
1071+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
10581072
},
10591073
"stakingValidators": [],
10601074
"featureFlags": {
@@ -1099,7 +1113,8 @@
10991113
"stakingAPR": "https://apybara-proxy-web-testnet.infrastructure-34d.workers.dev/v0/protocols/dydx",
11001114
"faucet": "https://faucet.v4testnet.dydx.exchange",
11011115
"affiliates": "https://dydx.stg.fuul.xyz",
1102-
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com"
1116+
"spotApi": "https://dydx-solana-api-prod-89bf4c933ba0.herokuapp.com",
1117+
"pnlImageApi": "https://image-generator.dydx.trade/generate-trade-card-web"
11031118
},
11041119
"stakingValidators": [],
11051120
"featureFlags": {
@@ -1144,7 +1159,8 @@
11441159
"geoV2": "[geo v2 endpoint for mainnet]",
11451160
"stakingAPR": "[staking APR endpoint for mainnet]",
11461161
"affiliates": "[affiliates endpoint for mainnet]",
1147-
"spotApi": "[spot api endpoint for mainnet]"
1162+
"spotApi": "[spot api endpoint for mainnet]",
1163+
"pnlImageApi": "[pnl image api endpoint for mainnet]"
11481164
},
11491165
"stakingValidators": [],
11501166
"featureFlags": {

src/hooks/useEndpointsConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface EndpointsConfig {
1919
affiliates?: string;
2020
spotApi: string;
2121
geoV2: string;
22+
pnlImageApi: string;
2223
}
2324

2425
export const useEndpointsConfig = () => {
@@ -38,5 +39,6 @@ export const useEndpointsConfig = () => {
3839
affiliatesBaseUrl: endpointsConfig.affiliates,
3940
spotApi: endpointsConfig.spotApi,
4041
geoV2: endpointsConfig.geoV2,
42+
pnlImageApi: endpointsConfig.pnlImageApi,
4143
};
4244
};

src/hooks/useSharePnlImage.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { logBonsaiError } from '@/bonsai/logs';
2+
import { useQuery } from '@tanstack/react-query';
3+
4+
import { timeUnits } from '@/constants/time';
5+
import { IndexerPositionSide } from '@/types/indexer/indexerApiGen';
6+
7+
import { useAccounts } from '@/hooks/useAccounts';
8+
9+
import { getOpenPositions } from '@/state/accountSelectors';
10+
import { useAppSelector } from '@/state/appTypes';
11+
12+
import { Nullable } from '@/lib/typeUtils';
13+
import { truncateAddress } from '@/lib/wallet';
14+
15+
import { useEndpointsConfig } from './useEndpointsConfig';
16+
17+
export type SharePnlImageParams = {
18+
marketId: string;
19+
side: Nullable<IndexerPositionSide>;
20+
leverage: Nullable<number>;
21+
oraclePrice: Nullable<number>;
22+
entryPrice: Nullable<number>;
23+
unrealizedPnl: Nullable<number>;
24+
type?: 'open' | 'closed' | 'liquidated' | undefined;
25+
};
26+
27+
export const useSharePnlImage = ({
28+
marketId,
29+
side,
30+
leverage,
31+
oraclePrice,
32+
entryPrice,
33+
unrealizedPnl,
34+
type = 'open',
35+
}: SharePnlImageParams) => {
36+
const { pnlImageApi } = useEndpointsConfig();
37+
38+
// Get user wallet address for username
39+
const { dydxAddress } = useAccounts();
40+
41+
// Get full position data from state
42+
const openPositions = useAppSelector(getOpenPositions);
43+
const position = openPositions?.find((p) => p.market === marketId);
44+
45+
const queryFn = async (): Promise<Blob | undefined> => {
46+
if (!dydxAddress) {
47+
return undefined;
48+
}
49+
50+
// Build the request body matching the API's zod schema
51+
const requestBody = {
52+
brand: 'dydx',
53+
ticker: marketId,
54+
type,
55+
leverage: leverage ?? 0,
56+
username: truncateAddress(dydxAddress),
57+
isLong: side === IndexerPositionSide.LONG,
58+
isCross: position?.marginMode === 'CROSS',
59+
// Optional fields - include if available
60+
size: position?.unsignedSize.toNumber(),
61+
userImage: 'https://dydx.trade/hedgie-profile.png',
62+
pnl: position?.realizedPnl.toNumber(),
63+
uPnl: unrealizedPnl ?? undefined,
64+
pnlPercentage: position?.updatedUnrealizedPnlPercent?.toNumber(),
65+
entryPx: entryPrice ?? undefined,
66+
liquidationPx: position?.liquidationPrice?.toNumber(),
67+
markPx: oraclePrice ?? undefined,
68+
};
69+
70+
const response = await fetch(pnlImageApi, {
71+
method: 'POST',
72+
headers: {
73+
'Content-Type': 'application/json',
74+
},
75+
body: JSON.stringify(requestBody),
76+
});
77+
78+
if (!response.ok) {
79+
logBonsaiError('useSharePnlImage', 'Failed to fetch share image', { response });
80+
throw new Error(`Failed to fetch share image: ${response.status}`);
81+
}
82+
83+
return response.blob();
84+
};
85+
86+
return useQuery({
87+
queryKey: [
88+
'sharePnlImage',
89+
marketId,
90+
dydxAddress,
91+
side,
92+
leverage,
93+
oraclePrice,
94+
entryPrice,
95+
unrealizedPnl,
96+
type,
97+
position?.marginMode,
98+
position?.unsignedSize.toString(),
99+
position?.liquidationPrice?.toString(),
100+
],
101+
queryFn,
102+
enabled: Boolean(dydxAddress),
103+
refetchOnWindowFocus: false,
104+
refetchOnReconnect: false,
105+
staleTime: 2 * timeUnits.minute, // 2 minutes
106+
retry: 2,
107+
retryDelay: 1 * timeUnits.second,
108+
retryOnMount: true,
109+
});
110+
};

0 commit comments

Comments
 (0)