pepay-streams-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +405 -0
- package/dist/api/index.d.mts +321 -0
- package/dist/api/index.d.ts +321 -0
- package/dist/api/index.js +312 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/index.mjs +306 -0
- package/dist/api/index.mjs.map +1 -0
- package/dist/automation/index.d.mts +140 -0
- package/dist/automation/index.d.ts +140 -0
- package/dist/automation/index.js +331 -0
- package/dist/automation/index.js.map +1 -0
- package/dist/automation/index.mjs +326 -0
- package/dist/automation/index.mjs.map +1 -0
- package/dist/campaigns/index.d.mts +286 -0
- package/dist/campaigns/index.d.ts +286 -0
- package/dist/campaigns/index.js +652 -0
- package/dist/campaigns/index.js.map +1 -0
- package/dist/campaigns/index.mjs +645 -0
- package/dist/campaigns/index.mjs.map +1 -0
- package/dist/claims/index.d.mts +190 -0
- package/dist/claims/index.d.ts +190 -0
- package/dist/claims/index.js +414 -0
- package/dist/claims/index.js.map +1 -0
- package/dist/claims/index.mjs +409 -0
- package/dist/claims/index.mjs.map +1 -0
- package/dist/index-BTG0TRJt.d.mts +555 -0
- package/dist/index-BTG0TRJt.d.ts +555 -0
- package/dist/index.d.mts +170 -0
- package/dist/index.d.ts +170 -0
- package/dist/index.js +2926 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2888 -0
- package/dist/index.mjs.map +1 -0
- package/dist/marketplace/index.d.mts +225 -0
- package/dist/marketplace/index.d.ts +225 -0
- package/dist/marketplace/index.js +529 -0
- package/dist/marketplace/index.js.map +1 -0
- package/dist/marketplace/index.mjs +524 -0
- package/dist/marketplace/index.mjs.map +1 -0
- package/dist/react/index.d.mts +185 -0
- package/dist/react/index.d.ts +185 -0
- package/dist/react/index.js +340 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +333 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/staking/index.d.mts +158 -0
- package/dist/staking/index.d.ts +158 -0
- package/dist/staking/index.js +359 -0
- package/dist/staking/index.js.map +1 -0
- package/dist/staking/index.mjs +354 -0
- package/dist/staking/index.mjs.map +1 -0
- package/package.json +106 -0
- package/src/api/index.ts +577 -0
- package/src/automation/index.ts +436 -0
- package/src/campaigns/index.ts +835 -0
- package/src/claims/index.ts +530 -0
- package/src/client.ts +518 -0
- package/src/index.ts +101 -0
- package/src/marketplace/index.ts +730 -0
- package/src/react/index.ts +498 -0
- package/src/staking/index.ts +449 -0
- package/src/types/index.ts +631 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Integration Helpers
|
|
3
|
+
*
|
|
4
|
+
* Provides React-friendly patterns for using the SDK:
|
|
5
|
+
* - Context provider
|
|
6
|
+
* - Hooks for common operations
|
|
7
|
+
* - Type-safe query helpers
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Note: This module provides patterns and helpers, not actual React hooks,
|
|
11
|
+
// since we want to avoid React as a hard dependency.
|
|
12
|
+
|
|
13
|
+
import type { Address } from 'viem';
|
|
14
|
+
import type { PepayStreamsConfig } from '../types';
|
|
15
|
+
import { PepayStreamsClient, PepayStreamsClientWithSigner } from '../client';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* SDK context value type for React context
|
|
19
|
+
*/
|
|
20
|
+
export interface PepayStreamsContextValue {
|
|
21
|
+
/** SDK client instance */
|
|
22
|
+
client: PepayStreamsClient | PepayStreamsClientWithSigner | null;
|
|
23
|
+
/** Whether the client has a signer */
|
|
24
|
+
isConnected: boolean;
|
|
25
|
+
/** Connected wallet address */
|
|
26
|
+
address: Address | null;
|
|
27
|
+
/** Chain ID */
|
|
28
|
+
chainId: number;
|
|
29
|
+
/** Diamond contract address */
|
|
30
|
+
diamondAddress: Address;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration for creating SDK context
|
|
35
|
+
*/
|
|
36
|
+
export interface CreateContextConfig extends PepayStreamsConfig {
|
|
37
|
+
/** Auto-connect on mount */
|
|
38
|
+
autoConnect?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Helper to create SDK client configuration from environment
|
|
43
|
+
*/
|
|
44
|
+
export function getConfigFromEnv(): Partial<PepayStreamsConfig> {
|
|
45
|
+
if (typeof process === 'undefined') {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const config: Partial<PepayStreamsConfig> = {
|
|
50
|
+
chainId: Number(
|
|
51
|
+
process.env.NEXT_PUBLIC_CHAIN_ID ?? process.env.CHAIN_ID ?? '1'
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL ?? process.env.RPC_URL;
|
|
56
|
+
if (rpcUrl) {
|
|
57
|
+
config.rpcUrl = rpcUrl;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const diamondAddress = process.env.NEXT_PUBLIC_DIAMOND_ADDRESS ??
|
|
61
|
+
process.env.DIAMOND_ADDRESS;
|
|
62
|
+
if (diamondAddress) {
|
|
63
|
+
config.diamondAddress = diamondAddress as Address;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const apiBaseUrl = process.env.NEXT_PUBLIC_API_URL ?? process.env.API_URL;
|
|
67
|
+
if (apiBaseUrl) {
|
|
68
|
+
config.apiBaseUrl = apiBaseUrl;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Example React context creation pattern
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```tsx
|
|
79
|
+
* // contexts/PepayStreams.tsx
|
|
80
|
+
* import { createContext, useContext, useState, useEffect } from 'react';
|
|
81
|
+
* import { PepayStreamsClient, PepayStreamsClientWithSigner } from '@pepay-streams/sdk';
|
|
82
|
+
* import { useWalletClient, usePublicClient, useAccount } from 'wagmi';
|
|
83
|
+
*
|
|
84
|
+
* const PepayStreamsContext = createContext<PepayStreamsContextValue | null>(null);
|
|
85
|
+
*
|
|
86
|
+
* export function PepayStreamsProvider({ children, config }) {
|
|
87
|
+
* const { address, isConnected } = useAccount();
|
|
88
|
+
* const publicClient = usePublicClient();
|
|
89
|
+
* const { data: walletClient } = useWalletClient();
|
|
90
|
+
* const [client, setClient] = useState<PepayStreamsClient | null>(null);
|
|
91
|
+
*
|
|
92
|
+
* useEffect(() => {
|
|
93
|
+
* if (!publicClient) return;
|
|
94
|
+
*
|
|
95
|
+
* if (walletClient && isConnected) {
|
|
96
|
+
* setClient(new PepayStreamsClient(config).withSigner(walletClient));
|
|
97
|
+
* } else {
|
|
98
|
+
* setClient(new PepayStreamsClient(config));
|
|
99
|
+
* }
|
|
100
|
+
* }, [publicClient, walletClient, isConnected]);
|
|
101
|
+
*
|
|
102
|
+
* return (
|
|
103
|
+
* <PepayStreamsContext.Provider value={{ client, isConnected, address, ... }}>
|
|
104
|
+
* {children}
|
|
105
|
+
* </PepayStreamsContext.Provider>
|
|
106
|
+
* );
|
|
107
|
+
* }
|
|
108
|
+
*
|
|
109
|
+
* export function usePepayStreams() {
|
|
110
|
+
* const context = useContext(PepayStreamsContext);
|
|
111
|
+
* if (!context) throw new Error('Must be used within PepayStreamsProvider');
|
|
112
|
+
* return context;
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export const CONTEXT_EXAMPLE = `
|
|
117
|
+
// Example usage with wagmi and React
|
|
118
|
+
|
|
119
|
+
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
|
120
|
+
import { PepayStreamsClient, PepayStreamsClientWithSigner, PepayStreamsConfig } from '@pepay-streams/sdk';
|
|
121
|
+
import { useWalletClient, usePublicClient, useAccount, useChainId } from 'wagmi';
|
|
122
|
+
|
|
123
|
+
interface PepayStreamsContextValue {
|
|
124
|
+
client: PepayStreamsClient | PepayStreamsClientWithSigner | null;
|
|
125
|
+
isConnected: boolean;
|
|
126
|
+
isLoading: boolean;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const PepayStreamsContext = createContext<PepayStreamsContextValue>({
|
|
130
|
+
client: null,
|
|
131
|
+
isConnected: false,
|
|
132
|
+
isLoading: true,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export function PepayStreamsProvider({
|
|
136
|
+
children,
|
|
137
|
+
config,
|
|
138
|
+
}: {
|
|
139
|
+
children: ReactNode;
|
|
140
|
+
config: PepayStreamsConfig;
|
|
141
|
+
}) {
|
|
142
|
+
const { isConnected } = useAccount();
|
|
143
|
+
const chainId = useChainId();
|
|
144
|
+
const { data: walletClient, isLoading: walletLoading } = useWalletClient();
|
|
145
|
+
const [client, setClient] = useState<PepayStreamsClient | null>(null);
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const newClient = new PepayStreamsClient({
|
|
149
|
+
...config,
|
|
150
|
+
chainId,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (walletClient && isConnected) {
|
|
154
|
+
setClient(newClient.withSigner(walletClient));
|
|
155
|
+
} else {
|
|
156
|
+
setClient(newClient);
|
|
157
|
+
}
|
|
158
|
+
}, [walletClient, isConnected, chainId, config]);
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<PepayStreamsContext.Provider
|
|
162
|
+
value={{
|
|
163
|
+
client,
|
|
164
|
+
isConnected,
|
|
165
|
+
isLoading: walletLoading,
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</PepayStreamsContext.Provider>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function usePepayStreams() {
|
|
174
|
+
return useContext(PepayStreamsContext);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Usage in components:
|
|
178
|
+
function ClaimButton({ campaignId }: { campaignId: bigint }) {
|
|
179
|
+
const { client, isConnected } = usePepayStreams();
|
|
180
|
+
const [loading, setLoading] = useState(false);
|
|
181
|
+
|
|
182
|
+
const handleClaim = async () => {
|
|
183
|
+
if (!client || !isConnected) return;
|
|
184
|
+
|
|
185
|
+
setLoading(true);
|
|
186
|
+
try {
|
|
187
|
+
const result = await client.claims.claim({ campaignId });
|
|
188
|
+
await result.wait();
|
|
189
|
+
console.log('Claimed!');
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('Claim failed:', error);
|
|
192
|
+
} finally {
|
|
193
|
+
setLoading(false);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<button onClick={handleClaim} disabled={!isConnected || loading}>
|
|
199
|
+
{loading ? 'Claiming...' : 'Claim'}
|
|
200
|
+
</button>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Query key factories for react-query integration
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```tsx
|
|
210
|
+
* import { useQuery } from '@tanstack/react-query';
|
|
211
|
+
* import { queryKeys, usePepayStreams } from '@pepay-streams/sdk/react';
|
|
212
|
+
*
|
|
213
|
+
* function CampaignDetails({ id }) {
|
|
214
|
+
* const { client } = usePepayStreams();
|
|
215
|
+
*
|
|
216
|
+
* const { data: campaign, isLoading } = useQuery({
|
|
217
|
+
* queryKey: queryKeys.campaign(id),
|
|
218
|
+
* queryFn: () => client.campaigns.getCampaign(BigInt(id)),
|
|
219
|
+
* enabled: !!client,
|
|
220
|
+
* });
|
|
221
|
+
*
|
|
222
|
+
* if (isLoading) return <div>Loading...</div>;
|
|
223
|
+
* return <div>{campaign?.name}</div>;
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export const queryKeys = {
|
|
228
|
+
// Campaigns
|
|
229
|
+
campaigns: (params?: Record<string, unknown>) =>
|
|
230
|
+
['pepay', 'campaigns', params] as const,
|
|
231
|
+
campaign: (id: string | bigint) => ['pepay', 'campaign', String(id)] as const,
|
|
232
|
+
campaignRecipients: (id: string | bigint, page?: number) =>
|
|
233
|
+
['pepay', 'campaign', String(id), 'recipients', page] as const,
|
|
234
|
+
campaignActivity: (id: string | bigint, page?: number) =>
|
|
235
|
+
['pepay', 'campaign', String(id), 'activity', page] as const,
|
|
236
|
+
|
|
237
|
+
// Claims
|
|
238
|
+
dueAmount: (campaignId: bigint, address: Address) =>
|
|
239
|
+
['pepay', 'dueAmount', String(campaignId), address] as const,
|
|
240
|
+
recipientStatus: (campaignId: bigint, address: Address) =>
|
|
241
|
+
['pepay', 'recipientStatus', String(campaignId), address] as const,
|
|
242
|
+
|
|
243
|
+
// Staking
|
|
244
|
+
stakingPools: (params?: Record<string, unknown>) =>
|
|
245
|
+
['pepay', 'staking', 'pools', params] as const,
|
|
246
|
+
stakingPool: (id: string | bigint) =>
|
|
247
|
+
['pepay', 'staking', 'pool', String(id)] as const,
|
|
248
|
+
userStake: (poolId: bigint, address: Address) =>
|
|
249
|
+
['pepay', 'staking', 'userStake', String(poolId), address] as const,
|
|
250
|
+
pendingRewards: (poolId: bigint, address: Address) =>
|
|
251
|
+
['pepay', 'staking', 'pendingRewards', String(poolId), address] as const,
|
|
252
|
+
|
|
253
|
+
// Marketplace
|
|
254
|
+
orders: (params?: Record<string, unknown>) =>
|
|
255
|
+
['pepay', 'marketplace', 'orders', params] as const,
|
|
256
|
+
order: (id: string | bigint) =>
|
|
257
|
+
['pepay', 'marketplace', 'order', String(id)] as const,
|
|
258
|
+
orderQuote: (id: string | bigint) =>
|
|
259
|
+
['pepay', 'marketplace', 'order', String(id), 'quote'] as const,
|
|
260
|
+
|
|
261
|
+
// Wallet
|
|
262
|
+
walletActivity: (address: Address) =>
|
|
263
|
+
['pepay', 'wallet', address, 'activity'] as const,
|
|
264
|
+
walletPositions: (address: Address) =>
|
|
265
|
+
['pepay', 'wallet', address, 'positions'] as const,
|
|
266
|
+
walletClaims: (address: Address, page?: number) =>
|
|
267
|
+
['pepay', 'wallet', address, 'claims', page] as const,
|
|
268
|
+
|
|
269
|
+
// Tokens
|
|
270
|
+
tokenBalance: (token: Address, address: Address) =>
|
|
271
|
+
['pepay', 'token', token, 'balance', address] as const,
|
|
272
|
+
tokenAllowance: (token: Address, owner: Address, spender: Address) =>
|
|
273
|
+
['pepay', 'token', token, 'allowance', owner, spender] as const,
|
|
274
|
+
tokenInfo: (token: Address) => ['pepay', 'token', token, 'info'] as const,
|
|
275
|
+
|
|
276
|
+
// Automation
|
|
277
|
+
autoWithdrawStatus: (campaignId: bigint) =>
|
|
278
|
+
['pepay', 'automation', 'autoWithdraw', String(campaignId)] as const,
|
|
279
|
+
autoReleaseStatus: (campaignId: bigint) =>
|
|
280
|
+
['pepay', 'automation', 'autoRelease', String(campaignId)] as const,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Mutation key factories for react-query
|
|
285
|
+
*/
|
|
286
|
+
export const mutationKeys = {
|
|
287
|
+
claim: (campaignId: bigint) =>
|
|
288
|
+
['pepay', 'mutation', 'claim', String(campaignId)] as const,
|
|
289
|
+
claimBatch: () => ['pepay', 'mutation', 'claimBatch'] as const,
|
|
290
|
+
stake: (poolId: bigint) =>
|
|
291
|
+
['pepay', 'mutation', 'stake', String(poolId)] as const,
|
|
292
|
+
unstake: (poolId: bigint) =>
|
|
293
|
+
['pepay', 'mutation', 'unstake', String(poolId)] as const,
|
|
294
|
+
fillOrder: (orderId: bigint) =>
|
|
295
|
+
['pepay', 'mutation', 'fillOrder', String(orderId)] as const,
|
|
296
|
+
cancelOrder: (orderId: bigint) =>
|
|
297
|
+
['pepay', 'mutation', 'cancelOrder', String(orderId)] as const,
|
|
298
|
+
approve: (token: Address) =>
|
|
299
|
+
['pepay', 'mutation', 'approve', token] as const,
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Example hook patterns for common operations
|
|
304
|
+
*/
|
|
305
|
+
export const HOOK_EXAMPLES = `
|
|
306
|
+
// Custom hooks for common operations
|
|
307
|
+
|
|
308
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
309
|
+
import { usePepayStreams, queryKeys, mutationKeys } from '@pepay-streams/sdk/react';
|
|
310
|
+
import { parseEther } from 'viem';
|
|
311
|
+
|
|
312
|
+
// Hook: Get campaign with due amount
|
|
313
|
+
export function useCampaignWithDue(campaignId: bigint, recipientAddress: Address) {
|
|
314
|
+
const { client } = usePepayStreams();
|
|
315
|
+
|
|
316
|
+
const campaign = useQuery({
|
|
317
|
+
queryKey: queryKeys.campaign(campaignId),
|
|
318
|
+
queryFn: () => client!.campaigns.getCampaign(campaignId),
|
|
319
|
+
enabled: !!client,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const dueAmount = useQuery({
|
|
323
|
+
queryKey: queryKeys.dueAmount(campaignId, recipientAddress),
|
|
324
|
+
queryFn: () => client!.claims.getDueAmount(campaignId, recipientAddress),
|
|
325
|
+
enabled: !!client && !!recipientAddress,
|
|
326
|
+
refetchInterval: 30000, // Refresh every 30s
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
campaign: campaign.data,
|
|
331
|
+
dueAmount: dueAmount.data,
|
|
332
|
+
isLoading: campaign.isLoading || dueAmount.isLoading,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Hook: Claim mutation with optimistic updates
|
|
337
|
+
export function useClaim() {
|
|
338
|
+
const { client } = usePepayStreams();
|
|
339
|
+
const queryClient = useQueryClient();
|
|
340
|
+
|
|
341
|
+
return useMutation({
|
|
342
|
+
mutationFn: async ({ campaignId }: { campaignId: bigint }) => {
|
|
343
|
+
const result = await client!.claims.claim({ campaignId });
|
|
344
|
+
return result.wait();
|
|
345
|
+
},
|
|
346
|
+
onSuccess: (_, { campaignId }) => {
|
|
347
|
+
// Invalidate related queries
|
|
348
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.campaign(campaignId) });
|
|
349
|
+
queryClient.invalidateQueries({ queryKey: ['pepay', 'dueAmount', String(campaignId)] });
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Hook: Stake with approval check
|
|
355
|
+
export function useStake() {
|
|
356
|
+
const { client } = usePepayStreams();
|
|
357
|
+
const queryClient = useQueryClient();
|
|
358
|
+
|
|
359
|
+
return useMutation({
|
|
360
|
+
mutationFn: async ({
|
|
361
|
+
poolId,
|
|
362
|
+
amount,
|
|
363
|
+
stakeToken,
|
|
364
|
+
}: {
|
|
365
|
+
poolId: bigint;
|
|
366
|
+
amount: bigint;
|
|
367
|
+
stakeToken: Address;
|
|
368
|
+
}) => {
|
|
369
|
+
// Check and approve if needed
|
|
370
|
+
const approval = await (client as PepayStreamsClientWithSigner).ensureAllowance(stakeToken, amount);
|
|
371
|
+
if (approval) {
|
|
372
|
+
await approval.wait();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Stake
|
|
376
|
+
const result = await client!.staking.stake(poolId, amount);
|
|
377
|
+
return result.wait();
|
|
378
|
+
},
|
|
379
|
+
onSuccess: (_, { poolId }) => {
|
|
380
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.stakingPool(poolId) });
|
|
381
|
+
queryClient.invalidateQueries({ queryKey: ['pepay', 'staking', 'userStake', String(poolId)] });
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Hook: Fill order with price display
|
|
387
|
+
export function useOrderWithQuote(orderId: bigint) {
|
|
388
|
+
const { client } = usePepayStreams();
|
|
389
|
+
|
|
390
|
+
const order = useQuery({
|
|
391
|
+
queryKey: queryKeys.order(orderId),
|
|
392
|
+
queryFn: () => client!.marketplace.getOrder(orderId),
|
|
393
|
+
enabled: !!client,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const quote = useQuery({
|
|
397
|
+
queryKey: queryKeys.orderQuote(orderId),
|
|
398
|
+
queryFn: () => client!.marketplace.getQuote(orderId),
|
|
399
|
+
enabled: !!client && order.data?.status === 'open',
|
|
400
|
+
refetchInterval: 10000, // Refresh quote every 10s
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
order: order.data,
|
|
405
|
+
quote: quote.data,
|
|
406
|
+
isLoading: order.isLoading || quote.isLoading,
|
|
407
|
+
canFill: quote.data?.isValid ?? false,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
`;
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Format helpers for display
|
|
414
|
+
*/
|
|
415
|
+
export const formatters = {
|
|
416
|
+
/**
|
|
417
|
+
* Format token amount with decimals
|
|
418
|
+
*/
|
|
419
|
+
formatTokenAmount(amount: bigint, decimals = 18, maxDecimals = 4): string {
|
|
420
|
+
const divisor = BigInt(10 ** decimals);
|
|
421
|
+
const integerPart = amount / divisor;
|
|
422
|
+
const fractionalPart = amount % divisor;
|
|
423
|
+
|
|
424
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, '0');
|
|
425
|
+
const trimmedFractional = fractionalStr.slice(0, maxDecimals).replace(/0+$/, '');
|
|
426
|
+
|
|
427
|
+
if (trimmedFractional) {
|
|
428
|
+
return `${integerPart}.${trimmedFractional}`;
|
|
429
|
+
}
|
|
430
|
+
return integerPart.toString();
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Format USD amount
|
|
435
|
+
*/
|
|
436
|
+
formatUsd(amount: number | string): string {
|
|
437
|
+
const num = typeof amount === 'string' ? parseFloat(amount) : amount;
|
|
438
|
+
return new Intl.NumberFormat('en-US', {
|
|
439
|
+
style: 'currency',
|
|
440
|
+
currency: 'USD',
|
|
441
|
+
minimumFractionDigits: 2,
|
|
442
|
+
maximumFractionDigits: 2,
|
|
443
|
+
}).format(num);
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Format percentage
|
|
448
|
+
*/
|
|
449
|
+
formatPercent(value: number, decimals = 2): string {
|
|
450
|
+
return `${value.toFixed(decimals)}%`;
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Format timestamp to date string
|
|
455
|
+
*/
|
|
456
|
+
formatDate(timestamp: number): string {
|
|
457
|
+
return new Date(timestamp * 1000).toLocaleDateString();
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Format timestamp to relative time
|
|
462
|
+
*/
|
|
463
|
+
formatRelativeTime(timestamp: number): string {
|
|
464
|
+
const now = Math.floor(Date.now() / 1000);
|
|
465
|
+
const diff = timestamp - now;
|
|
466
|
+
|
|
467
|
+
if (diff < 0) {
|
|
468
|
+
const absDiff = Math.abs(diff);
|
|
469
|
+
if (absDiff < 60) return 'just now';
|
|
470
|
+
if (absDiff < 3600) return `${Math.floor(absDiff / 60)}m ago`;
|
|
471
|
+
if (absDiff < 86400) return `${Math.floor(absDiff / 3600)}h ago`;
|
|
472
|
+
return `${Math.floor(absDiff / 86400)}d ago`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (diff < 60) return 'in < 1m';
|
|
476
|
+
if (diff < 3600) return `in ${Math.floor(diff / 60)}m`;
|
|
477
|
+
if (diff < 86400) return `in ${Math.floor(diff / 3600)}h`;
|
|
478
|
+
return `in ${Math.floor(diff / 86400)}d`;
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Format vesting progress
|
|
483
|
+
*/
|
|
484
|
+
formatVestingProgress(claimed: bigint, allocated: bigint): string {
|
|
485
|
+
if (allocated === 0n) return '0%';
|
|
486
|
+
const progress = Number((claimed * 100n) / allocated);
|
|
487
|
+
return `${progress.toFixed(1)}%`;
|
|
488
|
+
},
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Shorten address for display
|
|
492
|
+
*/
|
|
493
|
+
shortenAddress(address: Address, chars = 4): string {
|
|
494
|
+
return `${address.slice(0, chars + 2)}...${address.slice(-chars)}`;
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Types are already exported as interfaces above
|