@stocklayer/sdk 0.0.0-stage → 0.1.1

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/src/types.ts ADDED
@@ -0,0 +1,445 @@
1
+ /**
2
+ * Public request and response models for the Stocklayer V0 API.
3
+ *
4
+ * This file is the single source of truth for the wire contract. The
5
+ * application re-exports it from `src/core/types.ts`, so services, route
6
+ * handlers, the OpenAPI document, and the SDK all describe the same shapes.
7
+ * It has no runtime imports so it can ship inside the SDK unchanged.
8
+ */
9
+
10
+ export type Address = `0x${string}`;
11
+ export type Hex = `0x${string}`;
12
+
13
+ export const ROBINHOOD_CHAIN_ID = 4663;
14
+ export type RobinhoodChainId = typeof ROBINHOOD_CHAIN_ID;
15
+
16
+ export type ApiVersion = 'v0';
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Envelopes
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export interface ApiMeta {
23
+ requestId: string;
24
+ version: ApiVersion;
25
+ generatedAt: string;
26
+ }
27
+
28
+ export interface ApiSuccess<T> {
29
+ data: T;
30
+ meta: ApiMeta;
31
+ }
32
+
33
+ export interface ApiErrorBody {
34
+ code: string;
35
+ message: string;
36
+ retryable: boolean;
37
+ details: unknown;
38
+ }
39
+
40
+ export interface ApiFailure {
41
+ error: ApiErrorBody;
42
+ meta: ApiMeta;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Assets and prices
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export type AssetStatus = 'active' | 'inactive' | 'unspecified';
50
+
51
+ export interface AssetDeployment {
52
+ chainId: number;
53
+ contractAddress: Address;
54
+ }
55
+
56
+ export interface CanonicalAsset {
57
+ id: string;
58
+ symbol: string;
59
+ name: string;
60
+ status: AssetStatus;
61
+ currentMultiplier: string;
62
+ pendingMultiplier: string | null;
63
+ pendingMultiplierEffectiveTime: string | null;
64
+ deployments: AssetDeployment[];
65
+ logoUrl: string | null;
66
+ tradingCapabilities: Record<string, unknown> | null;
67
+ provenance: {
68
+ source: 'robinhood';
69
+ sourceUrl: string;
70
+ retrievedAt: string;
71
+ };
72
+ }
73
+
74
+ export interface AssetList {
75
+ assets: CanonicalAsset[];
76
+ next: string | null;
77
+ total: number;
78
+ }
79
+
80
+ export type CorporateActionType =
81
+ | 'forward_split'
82
+ | 'reverse_split'
83
+ | 'cash_dividend'
84
+ | 'stock_dividend'
85
+ | 'spin_off'
86
+ | 'cash_merger'
87
+ | 'stock_merger'
88
+ | 'stock_and_cash_merger'
89
+ | 'redemption'
90
+ | 'name_change'
91
+ | 'worthless_removal'
92
+ | 'rights_distribution'
93
+ | 'unit_split'
94
+ | 'unspecified';
95
+
96
+ export type CorporateActionStatus = 'in_progress' | 'completed' | 'unspecified';
97
+
98
+ export interface CorporateAction {
99
+ id: string;
100
+ type: CorporateActionType;
101
+ /** Original Robinhood enum, retained for forward compatibility. */
102
+ sourceType: string;
103
+ status: CorporateActionStatus;
104
+ /** ISO calendar date (YYYY-MM-DD), or null when Robinhood has not scheduled it. */
105
+ processDate: string | null;
106
+ asset: {
107
+ symbol: string;
108
+ deployments: AssetDeployment[];
109
+ };
110
+ /** Exactly one Robinhood-defined variant is present for known action types. */
111
+ details: Record<string, unknown>;
112
+ provenance: {
113
+ source: 'robinhood';
114
+ sourceUrl: string;
115
+ retrievedAt: string;
116
+ };
117
+ }
118
+
119
+ export interface CorporateActionList {
120
+ actions: CorporateAction[];
121
+ total: number;
122
+ }
123
+
124
+ export interface SystemToken {
125
+ kind: 'system';
126
+ symbol: string;
127
+ name: string;
128
+ address: Address;
129
+ decimals: number;
130
+ chainId: number;
131
+ }
132
+
133
+ export interface MarketToken {
134
+ kind: 'market';
135
+ symbol: string;
136
+ name: string;
137
+ address: Address;
138
+ decimals: number;
139
+ chainId: number;
140
+ asset: CanonicalAsset;
141
+ }
142
+
143
+ export type ResolvedToken = SystemToken | MarketToken;
144
+
145
+ export interface PriceObservation {
146
+ asset: Pick<CanonicalAsset, 'id' | 'symbol' | 'name' | 'status'>;
147
+ currency: string;
148
+ rawUnderlying: {
149
+ bid: string;
150
+ ask: string;
151
+ mid: string;
152
+ source: 'robinhood-rest';
153
+ };
154
+ tokenEquivalent: {
155
+ bid: string;
156
+ ask: string;
157
+ mid: string;
158
+ multiplier: string;
159
+ calculation: 'underlying_price_x_current_multiplier';
160
+ };
161
+ isTradingHalt: boolean;
162
+ dailyTradingVolume: string;
163
+ generatedAt: string;
164
+ retrievedAt: string;
165
+ }
166
+
167
+ // ---------------------------------------------------------------------------
168
+ // Portfolios
169
+ // ---------------------------------------------------------------------------
170
+
171
+ export interface PortfolioPosition {
172
+ asset: {
173
+ kind: 'system' | 'market';
174
+ symbol: string;
175
+ name: string;
176
+ contractAddress: Address;
177
+ decimals: number;
178
+ };
179
+ balance: { raw: string; formatted: string };
180
+ /** Underlying quantity for market tokens (`balance × currentMultiplier`); null for system tokens. */
181
+ underlyingQuantity: string | null;
182
+ }
183
+
184
+ export interface Portfolio {
185
+ wallet: Address;
186
+ chainId: RobinhoodChainId;
187
+ blockNumber: string;
188
+ nativeBalance: { symbol: 'ETH'; raw: string; formatted: string };
189
+ positions: PortfolioPosition[];
190
+ scannedAssets: number;
191
+ partial: boolean;
192
+ warning: string | null;
193
+ }
194
+
195
+ // ---------------------------------------------------------------------------
196
+ // Transactions and simulation
197
+ // ---------------------------------------------------------------------------
198
+
199
+ export interface UnsignedTransaction {
200
+ chainId: number;
201
+ from: Address;
202
+ to: Address;
203
+ data: Hex;
204
+ value: string;
205
+ gasLimit?: string;
206
+ maxFeePerGas?: string;
207
+ maxPriorityFeePerGas?: string;
208
+ gasPrice?: string;
209
+ }
210
+
211
+ /** Client-supplied transaction for simulation; `from` may come from `wallet`. */
212
+ export interface SimulateTransactionInput {
213
+ chainId?: number;
214
+ from?: string;
215
+ to: string;
216
+ data?: string;
217
+ value?: string;
218
+ gasLimit?: string;
219
+ gasPrice?: string;
220
+ maxFeePerGas?: string;
221
+ maxPriorityFeePerGas?: string;
222
+ }
223
+
224
+ export interface SimulateRequest {
225
+ wallet?: string;
226
+ transactions: SimulateTransactionInput[];
227
+ }
228
+
229
+ export interface SimulationCallResult {
230
+ index: number;
231
+ status: 'passed' | 'failed' | 'not_run';
232
+ gasUsed: string | null;
233
+ returnData: string | null;
234
+ error: string | null;
235
+ }
236
+
237
+ export interface SimulationResult {
238
+ status: 'passed' | 'failed' | 'partial';
239
+ mode: 'sequential' | 'independent';
240
+ blockNumber: string;
241
+ calls: SimulationCallResult[];
242
+ warning: string | null;
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Quotes and trade plans
247
+ // ---------------------------------------------------------------------------
248
+
249
+ export interface ExactInputTradeIntent {
250
+ type: 'exact_input';
251
+ tokenIn?: string;
252
+ tokenOut: string;
253
+ amountIn: string;
254
+ }
255
+
256
+ export interface ExactOutputTradeIntent {
257
+ type: 'exact_output';
258
+ tokenIn?: string;
259
+ tokenOut: string;
260
+ amountOut: string;
261
+ }
262
+
263
+ /** Preferred public request body for `POST /quotes` and `POST /trades/plan`. */
264
+ export interface IntentTradeRequest {
265
+ wallet: string;
266
+ intent: ExactInputTradeIntent | ExactOutputTradeIntent;
267
+ /** Percent, 0.01–5. Defaults to 0.5. */
268
+ slippageTolerance?: number;
269
+ }
270
+
271
+ /** V0 compatibility shape. New integrations should use `IntentTradeRequest`. */
272
+ export interface LegacyTradeRequest {
273
+ wallet: string;
274
+ tokenOut: string;
275
+ input: {
276
+ /** Defaults to USDG. */
277
+ tokenIn?: string;
278
+ /** Positive decimal string with at most 18 decimal places. */
279
+ amount: string;
280
+ };
281
+ /** Percent, 0.01–5. Defaults to 0.5. */
282
+ slippageTolerance?: number;
283
+ }
284
+
285
+ export type TradeRequest = IntentTradeRequest | LegacyTradeRequest;
286
+
287
+ export interface Quote {
288
+ id: string;
289
+ requestType: 'exact_input' | 'exact_output';
290
+ routing: string;
291
+ tokenIn: ResolvedToken;
292
+ tokenOut: ResolvedToken;
293
+ amountIn: string;
294
+ amountInBaseUnits: string;
295
+ /** Maximum input the wallet can spend after applying the requested slippage tolerance. */
296
+ maximumAmountIn: string;
297
+ maximumAmountInBaseUnits: string;
298
+ amountOut: string | null;
299
+ amountOutBaseUnits: string | null;
300
+ /** Minimum output the transaction is required to deliver. */
301
+ minimumAmountOut: string;
302
+ minimumAmountOutBaseUnits: string;
303
+ slippageTolerance: number;
304
+ receivedAt: string;
305
+ expiresAt: string;
306
+ provider: '0x-swap-api' | 'uniswap-trading-api';
307
+ providerRequestId: string | null;
308
+ rawQuote: Record<string, unknown>;
309
+ permitData: Record<string, unknown> | null;
310
+ }
311
+
312
+ export type TradePlanStepKind = 'cancel_approval' | 'approve' | 'swap';
313
+
314
+ export interface TradePlanStep {
315
+ index: number;
316
+ kind: TradePlanStepKind;
317
+ transaction: UnsignedTransaction;
318
+ }
319
+
320
+ export type PolicyVerdict = 'allow' | 'warn' | 'block';
321
+
322
+ export interface PolicyCheck {
323
+ source: 'rail_safety' | 'project_policy' | 'project_configuration';
324
+ verdict: PolicyVerdict;
325
+ code: string;
326
+ message: string;
327
+ evidence: Record<string, string | number | boolean | null>;
328
+ }
329
+
330
+ export interface TradePlanVerdict {
331
+ outcome: PolicyVerdict;
332
+ policyVersion: {
333
+ id: string;
334
+ version: number;
335
+ schemaVersion: 1;
336
+ } | null;
337
+ checks: PolicyCheck[];
338
+ }
339
+
340
+ export interface TokenTradeSizeLimit {
341
+ token: string;
342
+ warnAbove?: string;
343
+ blockAbove?: string;
344
+ }
345
+
346
+ export interface PolicyThresholds {
347
+ warnAbove?: number;
348
+ blockAbove?: number;
349
+ }
350
+
351
+ export interface ProjectPolicyRulesV1 {
352
+ schemaVersion: 1;
353
+ allowedOutputAssets?: string[];
354
+ blockedOutputAssets?: string[];
355
+ allowedInputTokens?: string[];
356
+ tradeSizeLimits?: TokenTradeSizeLimit[];
357
+ slippageTolerance?: PolicyThresholds;
358
+ }
359
+
360
+ export interface ProjectPolicyVersion {
361
+ id: string;
362
+ projectId: string;
363
+ version: number;
364
+ schemaVersion: 1;
365
+ rules: ProjectPolicyRulesV1;
366
+ createdAt: number;
367
+ active: boolean;
368
+ }
369
+
370
+ export interface TradePlan {
371
+ id: string;
372
+ status: 'ready' | 'review_required' | 'blocked';
373
+ chainId: RobinhoodChainId;
374
+ wallet: Address;
375
+ createdAt: string;
376
+ expiresAt: string;
377
+ intent:
378
+ | {
379
+ type: 'swap_exact_input';
380
+ tokenIn: string;
381
+ tokenOut: string;
382
+ amountIn: string;
383
+ }
384
+ | {
385
+ type: 'swap_exact_output';
386
+ tokenIn: string;
387
+ tokenOut: string;
388
+ amountOut: string;
389
+ };
390
+ quote: Quote;
391
+ walletContext: {
392
+ inputBalance: { raw: string; formatted: string; decimals: number };
393
+ sufficientBalance: true;
394
+ };
395
+ steps: TradePlanStep[];
396
+ simulation: SimulationResult;
397
+ verdict: TradePlanVerdict;
398
+ signing: {
399
+ mode: 'wallet_controlled';
400
+ submittedByRail: false;
401
+ unsignedTransactions: UnsignedTransaction[];
402
+ };
403
+ }
404
+
405
+ // ---------------------------------------------------------------------------
406
+ // System
407
+ // ---------------------------------------------------------------------------
408
+
409
+ export interface ApiIndex {
410
+ name: string;
411
+ version: ApiVersion;
412
+ chainId: RobinhoodChainId;
413
+ documentation: string;
414
+ endpoints: Record<string, string>;
415
+ }
416
+
417
+ export interface HealthReport {
418
+ status: 'ok' | 'degraded';
419
+ chainId: RobinhoodChainId;
420
+ services: {
421
+ robinhoodAssets: { configured: boolean };
422
+ robinhoodPrices: { configured: boolean };
423
+ robinhoodRpc: { configured: boolean; provider: string };
424
+ uniswapTradingApi: { configured: boolean };
425
+ zeroExSwapApi: { configured: boolean };
426
+ platformDatabase: {
427
+ configured: boolean;
428
+ schema: 'current' | 'behind' | 'not_configured';
429
+ };
430
+ stocklayerApiAuthentication: {
431
+ enabled: boolean;
432
+ mode: 'project_keys' | 'disabled';
433
+ keyVerifierConfigured: boolean;
434
+ };
435
+ };
436
+ }
437
+
438
+ /** Scenario names accepted by `x-stocklayer-scenario` on test-environment keys. */
439
+ export type SandboxScenario =
440
+ | 'funded'
441
+ | 'approved'
442
+ | 'insufficient_balance'
443
+ | 'zero_balance'
444
+ | 'simulation_failed'
445
+ | 'provider_degraded';