@strobelabs/perpcity-sdk 0.2.0 → 0.3.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 CHANGED
@@ -13,47 +13,105 @@ pnpm add @strobelabs/perpcity-sdk
13
13
  The SDK requires configuration via environment variables. Create a `.env` file:
14
14
 
15
15
  ```bash
16
- # Blockchain RPC endpoint
16
+ # Blockchain RPC endpoint (full URL)
17
+ # For production, use a private RPC provider like Alchemy or Infura
17
18
  RPC_URL=https://sepolia.base.org
18
19
 
19
20
  # Your private key (only for write operations)
20
21
  PRIVATE_KEY=0x...
21
22
 
22
- # Goldsky GraphQL API endpoint
23
- GOLDSKY_ENDPOINT=https://api.goldsky.com/api/private/project_xxx/subgraphs/perp-city/xxx/gn
24
-
25
- # Goldsky bearer token
26
- GOLDSKY_BEARER_TOKEN=your_token_here
27
-
28
23
  # Contract addresses
29
24
  PERP_MANAGER_ADDRESS=0x59F1766b77fd67af6c80217C2025A0D536998000
30
25
  USDC_ADDRESS=0xC1a5D4E99BB224713dd179eA9CA2Fa6600706210
26
+
27
+ # Optional: Module addresses (used as defaults when creating new perps)
28
+ # These can also be fetched dynamically from existing perp configs
29
+ FEES_MODULE_ADDRESS=0x...
30
+ MARGIN_RATIOS_MODULE_ADDRESS=0x...
31
+ LOCKUP_PERIOD_MODULE_ADDRESS=0x...
32
+ SQRT_PRICE_IMPACT_LIMIT_MODULE_ADDRESS=0x...
33
+ ```
34
+
35
+ ### RPC Configuration
36
+
37
+ #### Private RPC Providers (Recommended for Production)
38
+
39
+ For production applications, we recommend using a private RPC provider like [Alchemy](https://www.alchemy.com/) or [Infura](https://infura.io/) for better reliability, performance, and rate limits.
40
+
41
+ Simply set `RPC_URL` to your provider's full endpoint URL:
42
+
43
+ ```bash
44
+ # Alchemy (Recommended)
45
+ RPC_URL=https://base-sepolia.g.alchemy.com/v2/YOUR_API_KEY
46
+
47
+ # Or for Base Mainnet
48
+ RPC_URL=https://base-mainnet.g.alchemy.com/v2/YOUR_API_KEY
49
+
50
+ # Infura
51
+ RPC_URL=https://base-sepolia.infura.io/v3/YOUR_API_KEY
52
+
53
+ # Or for Base Mainnet
54
+ RPC_URL=https://base-mainnet.infura.io/v3/YOUR_API_KEY
55
+ ```
56
+
57
+ #### Public RPC (Development Only)
58
+
59
+ For development and testing, you can use public RPC endpoints:
60
+
61
+ ```bash
62
+ # Base Sepolia (testnet)
63
+ RPC_URL=https://sepolia.base.org
64
+
65
+ # Base Mainnet
66
+ RPC_URL=https://mainnet.base.org
67
+ ```
68
+
69
+ #### Using getRpcUrl Helper
70
+
71
+ The SDK provides a helper function for retrieving the RPC URL:
72
+
73
+ ```typescript
74
+ import { getRpcUrl } from '@strobelabs/perpcity-sdk';
75
+
76
+ // Get RPC URL from RPC_URL environment variable
77
+ const rpcUrl = getRpcUrl();
78
+
79
+ // Or override with a custom URL
80
+ const rpcUrl = getRpcUrl({
81
+ url: 'https://base-mainnet.g.alchemy.com/v2/YOUR_KEY'
82
+ });
31
83
  ```
32
84
 
33
85
  ## Quick Start
34
86
 
35
87
  ```typescript
36
- import { PerpCityContext } from '@strobelabs/perpcity-sdk';
88
+ import { PerpCityContext, getRpcUrl } from '@strobelabs/perpcity-sdk';
37
89
  import { createWalletClient, http } from 'viem';
38
90
  import { privateKeyToAccount } from 'viem/accounts';
39
91
  import { baseSepolia } from 'viem/chains';
40
92
 
93
+ // Get RPC URL from environment
94
+ const rpcUrl = getRpcUrl();
95
+
41
96
  // Create wallet client
42
97
  const account = privateKeyToAccount(process.env.PRIVATE_KEY);
43
98
  const walletClient = createWalletClient({
44
99
  account,
45
100
  chain: baseSepolia,
46
- transport: http(process.env.RPC_URL)
101
+ transport: http(rpcUrl)
47
102
  });
48
103
 
49
104
  // Initialize context with configuration
50
105
  const context = new PerpCityContext({
51
106
  walletClient,
52
- goldskyBearerToken: process.env.GOLDSKY_BEARER_TOKEN,
53
- goldskyEndpoint: process.env.GOLDSKY_ENDPOINT,
54
107
  deployments: {
55
108
  perpManager: process.env.PERP_MANAGER_ADDRESS as `0x${string}`,
56
109
  usdc: process.env.USDC_ADDRESS as `0x${string}`,
110
+ // Optional: Module addresses for creating new perps
111
+ feesModule: process.env.FEES_MODULE_ADDRESS as `0x${string}`,
112
+ marginRatiosModule: process.env.MARGIN_RATIOS_MODULE_ADDRESS as `0x${string}`,
113
+ lockupPeriodModule: process.env.LOCKUP_PERIOD_MODULE_ADDRESS as `0x${string}`,
114
+ sqrtPriceImpactLimitModule: process.env.SQRT_PRICE_IMPACT_LIMIT_MODULE_ADDRESS as `0x${string}`,
57
115
  },
58
116
  });
59
117
  ```
@@ -91,7 +149,6 @@ const wagmiConfig = createConfig({
91
149
  import { useWalletClient, useChainId } from 'wagmi';
92
150
  import { useMemo } from 'react';
93
151
  import { PerpCityContext, openTakerPosition } from '@strobelabs/perpcity-sdk';
94
- import { GraphQLClient } from 'graphql-request';
95
152
 
96
153
  function usePerpCity() {
97
154
  const { data: walletClient } = useWalletClient();
@@ -103,11 +160,14 @@ function usePerpCity() {
103
160
  // Wagmi's WalletClient is viem-compatible - works directly with SDK!
104
161
  return new PerpCityContext({
105
162
  walletClient,
106
- goldskyBearerToken: process.env.GOLDSKY_BEARER_TOKEN,
107
- goldskyEndpoint: process.env.GOLDSKY_ENDPOINT,
108
163
  deployments: {
109
164
  perpManager: process.env.PERP_MANAGER_ADDRESS as `0x${string}`,
110
165
  usdc: process.env.USDC_ADDRESS as `0x${string}`,
166
+ // Optional: Module addresses for creating new perps
167
+ feesModule: process.env.FEES_MODULE_ADDRESS as `0x${string}`,
168
+ marginRatiosModule: process.env.MARGIN_RATIOS_MODULE_ADDRESS as `0x${string}`,
169
+ lockupPeriodModule: process.env.LOCKUP_PERIOD_MODULE_ADDRESS as `0x${string}`,
170
+ sqrtPriceImpactLimitModule: process.env.SQRT_PRICE_IMPACT_LIMIT_MODULE_ADDRESS as `0x${string}`,
111
171
  },
112
172
  });
113
173
  }, [walletClient, chainId]);
@@ -138,84 +198,114 @@ See `examples/wagmi-integration.ts` for complete example with React components.
138
198
 
139
199
  ## Features
140
200
 
141
- ### Optimized Data Fetching
142
- The SDK batches multiple GraphQL queries into single requests, dramatically improving performance:
201
+ ### Config Caching
202
+ The SDK automatically caches perp configurations (including module addresses) to minimize redundant contract calls:
203
+
204
+ ```typescript
205
+ // First call fetches from contract
206
+ const config1 = await context.getPerpConfig(perpId);
207
+
208
+ // Subsequent calls use cache
209
+ const config2 = await context.getPerpConfig(perpId); // Instant!
210
+ ```
211
+
212
+ ### Contract-Only Data Fetching
213
+ The SDK fetches all data directly from blockchain contracts, providing real-time, trustless data:
143
214
 
144
215
  ```typescript
145
- // Fetch perp data with all related information in one call
216
+ // Fetch perp data with live information from contracts
146
217
  const perpData = await context.getPerpData(perpId);
147
218
 
148
- console.log(perpData.mark); // Current mark price
149
- console.log(perpData.index); // Current index price
150
- console.log(perpData.fundingRate); // Current funding rate
151
- console.log(perpData.openInterest); // Open interest
152
- console.log(perpData.markTimeSeries); // Historical mark prices
153
- console.log(perpData.indexTimeSeries); // Historical index prices
219
+ console.log(perpData.mark); // Current mark price from contract
220
+ console.log(perpData.beacon); // Oracle beacon address
221
+ console.log(perpData.tickSpacing);// Tick spacing
222
+ console.log(perpData.bounds); // Margin and leverage bounds
223
+ console.log(perpData.fees); // Fee structure
154
224
  ```
155
225
 
156
226
  ### Functional API
157
227
  Pure functions for data extraction:
158
228
 
159
229
  ```typescript
160
- import {
161
- getPerpMark,
162
- getPerpIndex,
163
- getPerpFundingRate,
230
+ import {
231
+ getPerpMark,
232
+ getPerpBeacon,
164
233
  getPerpBounds,
165
- getPerpFees
234
+ getPerpFees,
235
+ getPerpTickSpacing
166
236
  } from '@strobelabs/perpcity-sdk';
167
237
 
168
238
  // Use functional API for clean, composable code
169
239
  const mark = getPerpMark(perpData);
170
- const index = getPerpIndex(perpData);
171
- const fundingRate = getPerpFundingRate(perpData);
240
+ const beacon = getPerpBeacon(perpData);
241
+ const bounds = getPerpBounds(perpData);
242
+ const fees = getPerpFees(perpData);
243
+ const tickSpacing = getPerpTickSpacing(perpData);
172
244
  ```
173
245
 
174
246
  ### Create and Manage Perps
175
247
 
176
248
  ```typescript
177
- import { createPerp, getPerps } from '@strobelabs/perpcity-sdk';
249
+ import { createPerp } from '@strobelabs/perpcity-sdk';
178
250
 
179
251
  // Create a new perpetual market
252
+ // Module addresses will use deployment config defaults if not specified
180
253
  const perpId = await createPerp(context, {
181
254
  startingPrice: 3000,
182
- beacon: '0x...' // Beacon address for price oracle
255
+ beacon: '0x...', // Beacon address for price oracle
256
+ // Optional: Override module addresses for this perp
257
+ // fees: '0x...',
258
+ // marginRatios: '0x...',
259
+ // lockupPeriod: '0x...',
260
+ // sqrtPriceImpactLimit: '0x...',
183
261
  });
184
262
 
185
- // Get all perps
186
- const allPerps = await getPerps(context);
263
+ // Get cached config for a perp (includes module addresses)
264
+ const config = await context.getPerpConfig(perpId);
265
+ console.log(config.fees); // Fees module address
266
+ console.log(config.marginRatios); // Margin ratios module address
267
+
268
+ // Note: Perp discovery must be done externally (e.g., from events, databases, etc.)
269
+ // The SDK focuses on interacting with known perp IDs
187
270
  ```
188
271
 
189
272
  ### Manage Positions
190
273
 
191
274
  ```typescript
192
- import { OpenPosition, getAllTakerPositions, getAllMakerPositions } from '@strobelabs/perpcity-sdk';
193
-
194
- // Get all taker positions for a perp
195
- const takerPositions = await getAllTakerPositions(context, perpId);
196
- for (const position of takerPositions) {
197
- const liveDetails = await position.liveDetails();
198
- console.log('Position PnL:', liveDetails.pnl);
199
- console.log('Funding Payment:', liveDetails.fundingPayment);
200
- console.log('Is Liquidatable:', liveDetails.isLiquidatable);
201
- }
202
-
203
- // Get all maker positions for a perp
204
- const makerPositions = await getAllMakerPositions(context, perpId);
275
+ import { openTakerPosition, openMakerPosition } from '@strobelabs/perpcity-sdk';
276
+
277
+ // Open a taker (long/short) position
278
+ const takerPosition = await openTakerPosition(context, perpId, {
279
+ isLong: true,
280
+ margin: 100, // $100 USDC
281
+ leverage: 2, // 2x leverage
282
+ unspecifiedAmountLimit: 0,
283
+ });
205
284
 
206
- // Close a position
207
- const closedPosition = await takerPositions[0].closePosition({
208
- margin: 0, // Full close
209
- unspecifiedAmountLimit: 1000000
285
+ // Open a maker (LP) position
286
+ const makerPosition = await openMakerPosition(context, perpId, {
287
+ margin: 1000,
288
+ priceLower: 2900,
289
+ priceUpper: 3100,
290
+ liquidity: 1000000n,
291
+ maxAmt0In: 1000000,
292
+ maxAmt1In: 1000000,
210
293
  });
211
294
 
212
- // Note: To open new positions, call the PerpManager contract directly using viem:
213
- // const txHash = await context.walletClient.writeContract({
214
- // address: context.deployments().perpManager,
215
- // abi: PERP_MANAGER_ABI,
216
- // functionName: 'openTakerPosition',
217
- // args: [perpId, isLong, margin, leverage, unspecifiedAmountLimit]
218
- // });
295
+ // Get live details for a position (requires position ID from transaction receipt)
296
+ const positionData = await context.getOpenPositionData(
297
+ perpId,
298
+ positionId, // bigint from PositionOpened event
299
+ isLong, // boolean tracked from when position was opened
300
+ isMaker // boolean tracked from when position was opened
301
+ );
302
+
303
+ console.log('Position PnL:', positionData.liveDetails.pnl);
304
+ console.log('Funding Payment:', positionData.liveDetails.fundingPayment);
305
+ console.log('Is Liquidatable:', positionData.liveDetails.isLiquidatable);
306
+
307
+ // Note: Position tracking must be done externally (e.g., tracking PositionOpened events)
308
+ // The SDK requires you to provide position IDs and metadata
219
309
  ```
220
310
 
221
311
  ### Close Positions
@@ -232,14 +322,26 @@ const closedPosition = await position.closePosition({
232
322
  ### User Data
233
323
 
234
324
  ```typescript
235
- // Fetch comprehensive user data
236
- const userData = await context.getUserData(userAddress);
325
+ // Fetch user data with live details for tracked positions
326
+ // You must provide position metadata from your own tracking system
327
+ const positions = [
328
+ { perpId: '0x...', positionId: 1n, isLong: true, isMaker: false },
329
+ { perpId: '0x...', positionId: 2n, isLong: false, isMaker: false },
330
+ ];
331
+
332
+ const userData = await context.getUserData(userAddress, positions);
237
333
 
238
334
  console.log(userData.usdcBalance);
239
- console.log(userData.openPositions);
240
- console.log(userData.closedPositions);
241
- console.log(userData.realizedPnl);
242
- console.log(userData.unrealizedPnl);
335
+ console.log(userData.openPositions); // Array with live details for each position
336
+
337
+ // Access individual position live details
338
+ for (const position of userData.openPositions) {
339
+ console.log('Position:', position.positionId);
340
+ console.log('PnL:', position.liveDetails.pnl);
341
+ console.log('Funding:', position.liveDetails.fundingPayment);
342
+ console.log('Margin:', position.liveDetails.effectiveMargin);
343
+ console.log('Liquidatable:', position.liveDetails.isLiquidatable);
344
+ }
243
345
  ```
244
346
 
245
347
  ## API Reference
@@ -247,26 +349,26 @@ console.log(userData.unrealizedPnl);
247
349
  ### Core Classes
248
350
 
249
351
  #### `PerpCityContext`
250
- Base context for all SDK operations.
251
-
252
- #### `GlobalPerpCityContext`
253
- Optimized context that batches GraphQL queries for better performance.
352
+ Base context for all SDK operations. Includes:
353
+ - `getPerpConfig(perpId)` - Fetch and cache perp configuration (module addresses, pool settings)
354
+ - `getPerpData(perpId)` - Fetch perp data from contracts
355
+ - `getUserData(userAddress, positions)` - Fetch user data with live position details
356
+ - `getOpenPositionData(perpId, positionId, isLong, isMaker)` - Fetch live details for a single position
357
+ - `deployments()` - Get deployment addresses
254
358
 
255
359
  ### Main Functions
256
360
 
257
361
  #### Perp Manager
258
- - `getPerps(context)` - Get all perp IDs
259
362
  - `createPerp(context, params)` - Create a new perp market
363
+ - `openTakerPosition(context, perpId, params)` - Open a taker (long/short) position
364
+ - `openMakerPosition(context, perpId, params)` - Open a maker (LP) position
260
365
 
261
366
  #### Perp Data (Pure Functions)
262
367
  - `getPerpMark(perpData)` - Get mark price
263
- - `getPerpIndex(perpData)` - Get index price
264
- - `getPerpFundingRate(perpData)` - Get funding rate
368
+ - `getPerpBeacon(perpData)` - Get oracle beacon address
369
+ - `getPerpTickSpacing(perpData)` - Get tick spacing
265
370
  - `getPerpBounds(perpData)` - Get margin and leverage bounds
266
371
  - `getPerpFees(perpData)` - Get fee structure
267
- - `getPerpOpenInterest(perpData)` - Get open interest
268
- - `getPerpMarkTimeSeries(perpData)` - Get historical mark prices
269
- - `getPerpIndexTimeSeries(perpData)` - Get historical index prices
270
372
 
271
373
  #### Position Functions
272
374
  - `getPositionPnl(positionData)` - Get position PnL
@@ -277,10 +379,8 @@ Optimized context that batches GraphQL queries for better performance.
277
379
 
278
380
  #### User Functions
279
381
  - `getUserUsdcBalance(userData)` - Get USDC balance
280
- - `getUserOpenPositions(userData)` - Get open positions
281
- - `getUserClosedPositions(userData)` - Get closed positions
282
- - `getUserRealizedPnl(userData)` - Get realized PnL
283
- - `getUserUnrealizedPnl(userData)` - Get unrealized PnL
382
+ - `getUserOpenPositions(userData)` - Get open positions with live details
383
+ - `getUserWalletAddress(userData)` - Get user's wallet address
284
384
 
285
385
  ## Examples
286
386
 
@@ -332,8 +432,17 @@ pnpm ci
332
432
  Create a `.env.local` file:
333
433
 
334
434
  ```env
435
+ # Required
335
436
  PRIVATE_KEY=your_private_key_here
336
- GOLDSKY_BEARER_TOKEN=your_goldsky_api_key_here
437
+ PERP_MANAGER_ADDRESS=0x59F1766b77fd67af6c80217C2025A0D536998000
438
+ USDC_ADDRESS=0xC1a5D4E99BB224713dd179eA9CA2Fa6600706210
439
+
440
+ # RPC Configuration
441
+ # For production, use a private RPC provider URL
442
+ RPC_URL=https://base-sepolia.g.alchemy.com/v2/YOUR_API_KEY
443
+
444
+ # Or for development/testing with public RPC
445
+ # RPC_URL=https://sepolia.base.org
337
446
  ```
338
447
 
339
448
  ## License