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.
Files changed (62) hide show
  1. package/README.md +405 -0
  2. package/dist/api/index.d.mts +321 -0
  3. package/dist/api/index.d.ts +321 -0
  4. package/dist/api/index.js +312 -0
  5. package/dist/api/index.js.map +1 -0
  6. package/dist/api/index.mjs +306 -0
  7. package/dist/api/index.mjs.map +1 -0
  8. package/dist/automation/index.d.mts +140 -0
  9. package/dist/automation/index.d.ts +140 -0
  10. package/dist/automation/index.js +331 -0
  11. package/dist/automation/index.js.map +1 -0
  12. package/dist/automation/index.mjs +326 -0
  13. package/dist/automation/index.mjs.map +1 -0
  14. package/dist/campaigns/index.d.mts +286 -0
  15. package/dist/campaigns/index.d.ts +286 -0
  16. package/dist/campaigns/index.js +652 -0
  17. package/dist/campaigns/index.js.map +1 -0
  18. package/dist/campaigns/index.mjs +645 -0
  19. package/dist/campaigns/index.mjs.map +1 -0
  20. package/dist/claims/index.d.mts +190 -0
  21. package/dist/claims/index.d.ts +190 -0
  22. package/dist/claims/index.js +414 -0
  23. package/dist/claims/index.js.map +1 -0
  24. package/dist/claims/index.mjs +409 -0
  25. package/dist/claims/index.mjs.map +1 -0
  26. package/dist/index-BTG0TRJt.d.mts +555 -0
  27. package/dist/index-BTG0TRJt.d.ts +555 -0
  28. package/dist/index.d.mts +170 -0
  29. package/dist/index.d.ts +170 -0
  30. package/dist/index.js +2926 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/index.mjs +2888 -0
  33. package/dist/index.mjs.map +1 -0
  34. package/dist/marketplace/index.d.mts +225 -0
  35. package/dist/marketplace/index.d.ts +225 -0
  36. package/dist/marketplace/index.js +529 -0
  37. package/dist/marketplace/index.js.map +1 -0
  38. package/dist/marketplace/index.mjs +524 -0
  39. package/dist/marketplace/index.mjs.map +1 -0
  40. package/dist/react/index.d.mts +185 -0
  41. package/dist/react/index.d.ts +185 -0
  42. package/dist/react/index.js +340 -0
  43. package/dist/react/index.js.map +1 -0
  44. package/dist/react/index.mjs +333 -0
  45. package/dist/react/index.mjs.map +1 -0
  46. package/dist/staking/index.d.mts +158 -0
  47. package/dist/staking/index.d.ts +158 -0
  48. package/dist/staking/index.js +359 -0
  49. package/dist/staking/index.js.map +1 -0
  50. package/dist/staking/index.mjs +354 -0
  51. package/dist/staking/index.mjs.map +1 -0
  52. package/package.json +106 -0
  53. package/src/api/index.ts +577 -0
  54. package/src/automation/index.ts +436 -0
  55. package/src/campaigns/index.ts +835 -0
  56. package/src/claims/index.ts +530 -0
  57. package/src/client.ts +518 -0
  58. package/src/index.ts +101 -0
  59. package/src/marketplace/index.ts +730 -0
  60. package/src/react/index.ts +498 -0
  61. package/src/staking/index.ts +449 -0
  62. 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