@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/LICENSE.md +21 -674
- package/README.md +188 -79
- package/dist/index.d.mts +964 -541
- package/dist/index.d.ts +964 -541
- package/dist/index.js +1454 -1308
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1453 -1291
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -5
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(
|
|
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
|
-
###
|
|
142
|
-
The SDK
|
|
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
|
|
216
|
+
// Fetch perp data with live information from contracts
|
|
146
217
|
const perpData = await context.getPerpData(perpId);
|
|
147
218
|
|
|
148
|
-
console.log(perpData.mark);
|
|
149
|
-
console.log(perpData.
|
|
150
|
-
console.log(perpData.
|
|
151
|
-
console.log(perpData.
|
|
152
|
-
console.log(perpData.
|
|
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
|
-
|
|
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
|
|
171
|
-
const
|
|
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
|
|
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
|
|
186
|
-
const
|
|
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 {
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
//
|
|
207
|
-
const
|
|
208
|
-
margin:
|
|
209
|
-
|
|
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
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
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
|
|
236
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
253
|
-
|
|
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
|
-
- `
|
|
264
|
-
- `
|
|
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
|
-
- `
|
|
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
|
-
|
|
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
|